propagate from branch 'ca.inverse.sogo.1_3_1' (head 996598fce70f35fe733debbc06cb1ae8c677afe1)

to branch 'ca.inverse.sogo' (head e16807404ac6ea20632ee7e0e21ae2db34830540)

Monotone-Parent: 996598fce70f35fe733debbc06cb1ae8c677afe1
Monotone-Parent: e16807404ac6ea20632ee7e0e21ae2db34830540
Monotone-Revision: 38aeede6f54d0b954356cd2d385ba01bbdd8aecd

Monotone-Author: ludovic@Sophos.ca
Monotone-Date: 2010-08-17T14:10:30
Monotone-Branch: ca.inverse.sogo
This commit is contained in:
Ludovic Marcotte
2010-08-17 14:10:30 +00:00
97 changed files with 3642 additions and 1968 deletions

378
ChangeLog
View File

@@ -1,26 +1,78 @@
2010-08-12 Wolfgang Sourdeau <wsourdeau@inverse.ca>
* UI/WebServerResources/generic.js: (onAjaxRequestStateChange):
when using CAS authentication and when the return code is 0 and
when the request status is 4, chances are that the initial ajax
request failed due to the expiration of the CAS ticket entry in
memcached. In FF 3.5 and above, a bug prevents the initial cookie
from being given back to the reauthentication redirect from an
AJAX request. Therefore we open a window that will do this for us
and will close one the authentication has succeeded again.
* UI/WebServerResources/MailerUIdTree.js: (getMailboxNode): fixed
method to return unfolded nodes too.
* UI/MainUI/SOGoRootPage.m (-revoverAction): new fake action that
perform the CAS redirection and then trigger the respawn of the
initial request.
* UI/WebServerResources/MailerUI.js: (updateUnseenCount): renamed
from "updateStatusFolders" and improved to use DOM methods.
* UI/Common/UIxPageFrame.m (-usesCASAuthentication): new accessor
used for recovery of redirected requests.
* UI/WebServerResources/dtree.js: (Node): removed the "hasUnseen"
parameter.
2010-08-11 Francis Lachapelle <flachapelle@inverse.ca>
* UI/MailerUI/UIxMailAccountActions.m (-listMailboxes): make use
of the "application/json" content type in the response.
* UI/WebServerResources/MailerUI.js (refreshMessage): if the
current mailbox is the sent folder, refresh the mailbox instead of
only the message.
* UI/MailerUI/UIxMailFolderActions.m: (-unseenCount): renamed and
moved implementation of the -[UIxMailAccountActions statusFolders]
method, splitted thereof since we now execute the method on all
mail folders.
* UI/MailerUI/UIxMailMainFrame.m (-defaultColumnsOrder): removed
accessor, made obsolete by the new propagation mechanism of user
defaults.
(-getUnseenCountForAllFolders): new accessor that returns the
value below as a string value.
* SoObjects/SOGo/SOGoDomainDefaults.m (-mailCheckAllUnseenCounts):
new method returning whether the unseen count of all mailboxes
should be checked.
* SoObjects/Mailer/SOGoMailAccount.m (-updateFilters): when the
first login attempt fails, we must request a new password to the
authenticator (as intended originally).
* UI/WebServerResources/generic.js: (onAjaxRequestStateChange):
when using CAS authentication and when the return code is 0 and
when the request status is 4, chances are that the initial ajax
request failed due to the expiration of the CAS ticket entry in
memcached. In FF 3.5 and above, a bug prevents the initial cookie
from being given back to the reauthentication redirect from an
AJAX request. Therefore we open a window that will do this for us
and will close one the authentication has succeeded again.
* UI/MainUI/SOGoRootPage.m (-revoverAction): new fake action that
perform the CAS redirection and then trigger the respawn of the
initial request.
* UI/Common/UIxPageFrame.m (-usesCASAuthentication): new accessor
used for recovery of redirected requests.
* SoObjects/Contacts/SOGoFolder+CardDAV.m:
(_appendObject:withBaseURL:toREPORTResponse:): moved method here
from SOGOContact{GCS,Source}Folder since its implementation was
mostly the same in the two classes and never invoked anywhere
else.
Modified to return both "address-data" and "addressbook-data"
(draft rev < 4) unless we parse the requested props appropriately.
* SoObjects/Contacts/SOGoContactGCSFolder.m
(-davAddressbookMultiget): new method that responds the
CardDAV addressbook-multiget report.
(-davSQLFieldsTable): new overriden method that adds support for
the CardDAV address-data property.
* SoObjects/Appointments/SOGoAppointmentFolder.m
(-davCalendarMultiget:): make use of the new method below.
* SoObjects/SOGo/SOGoGCSFolder.m
(-performMultigetInContext:inNamespace:): collection-type
independent form of -[SOGoAppointmentFolder davCalendarMultiget:],
moved along with all its private methods.
* SoObjects/Contacts/SOGoContactFolders.m
(-toManyRelationshipKeys): new overriden method to return only the
personal addressbook when the request is performed by
AddressBook.app.
2010-08-11 Ludovic Marcotte <lmarcotte@inverse.ca>
@@ -41,6 +93,274 @@
messages and also when opening mailboxes. This
fixes #716.
2010-08-11 Francis Lachapelle <flachapelle@inverse.ca>
* UI/WebServerResources/MailerUI.js (refreshMessage): if the
current mailbox is the sent folder, refresh the mailbox instead of
only the message.
2010-08-11 Wolfgang Sourdeau <wsourdeau@inverse.ca>
* UI/WebServerResources/SchedulerUI.js: (initCalendars): avoid
setting "showCompletedTasks" when in a popup, since this needs
access to a UserDefaults dictionary which is not present nor event
required.
* Main/SOGo+DAV.m (-davCurrentUserPrincipal): new method
implementing the "{DAV:}current-user-principal" property.
* SoObjects/Contacts/SOGoContactGCSEntry.m (-davAddressData): new
DAV property accessor returning the card content.
* SoObjects/SOGo/WORequest+SOGo.m (-isAddressBookApp): new
(hackish) test method to determine whether the client is Apple's
AddressBook.app.
* UI/Common/UIxPageFrame.m (_dictionaryWithKeys:fromSource:):
set an NSNull as value for keys which return no results, in order
to avoid an NSInvalidArgumentException.
2010-08-10 Francis Lachapelle <flachapelle@inverse.ca>
* UI/WebServerResources/generic.js (showAlertDialog): new function
to replace window.alert().
2010-08-09 Wolfgang Sourdeau <wsourdeau@inverse.ca>
* UI/WebServerResources/MailerUI.js (openMailbox): removed the
"updateStatus" parameter, which is never used.
* UI/WebServerResources/generic.js (log): fixed handling of
messages ending with "\n".
* UI/Common/UIxPageFrame.m (-setUserDefaultsKeys:)
(-hasUserDefaultsKeys, -setUserSettingsKeys:)
(-hasUserSettingsKeys): new accessor for determining explicitly
which user defaults/settings keys are returned. The new
corresponding ivars are "udKeys" and "usKeys" respectively.
(-userDefaults, -userSettings): rewrote methods to create a
dictionary containing only the requested keys (see above). Since
the source are now explicitly queried, the values can now be
inherited from the domain and system defaults.
* UI/WebServerResources/UIxFilterEditor.js (loadMailboxes): the
first account is now always identitied with "0", therefore we no
longer need the "firstMailAccount" variable.
* UI/PreferencesUI/UIxFilterEditor.m (-firstMailAccount): removed
osbolete accessor.
* UI/WebServerResources/UIxPreferences.js: (onMailAccountAdd)
(onMailAccountDelete): set "hasChanged" to 1 when triggered.
(onColorPickerChoice): don't invoke "onChoiceChanged" as it is an
event callback.
2010-08-06 Wolfgang Sourdeau <wsourdeau@inverse.ca>
* UI/WebServerResources/generic.js (reloadPreferences): dropped
useless method.
* UI/PreferencesUI/UIxPreferences.m (_extractAuxiliaryAccounts:):
since we no longer transfer the user password to the client, the
old password has to be fetched from the old account unless a new
password has been set.
(-mailAccounts): we now strip the passwords from the returned mail
accounts, to avoid transferring them uselessly with a risk of
keeping them in cache.
* UI/WebServerResources/MailerUI.js: (initMailboxTree): make use
of the new class below and drop all code related to keeping track
of active requests.
* UI/WebServerResources/generic.js: (AjaxRequestsChain): new class
that implements the chaining of ajax requests.
* UI/WebServerResources/MailerUI.js: (dropAction): ensure that the
destination mailbox is in the same mail account as the message
being dragged.
(getMenus): arranged menu descriptions as as single statement.
Added "prepareVisibility" callbacks for the "messageListMenu",
"messagesListMenu" and "messageContentMenu" (move/copy ops) and
"inboxIconMenu", "trashIconMenu", "mailboxIconMenu" menus (folder
sharing).
(onMessageListMenuPrepareVisibility): new vis. callback designed
to attach the right folder submenu to the copy and move ops.
(onFolderMenuPrepareVisibility): new vis. cb for disabling the
share of folders for auxiliary accounts.
(updateMailboxMenus): simplified method by removing the first
level of submenus, where the account names used to be.
(openInbox): "openMailbox" only takes 3 parameters. Using "null"
instead of "false" was also pretty lame.
(updateStatusFolders): take the mailbox name as parameter and
arranged for multiple mail accounts. In particular, "unseenCount"
is now a class identifier rather than an element id.
(markMailInWindow): make use of
MailerUIdTreeExtension.getMailboxNode.
* UI/WebServerResources/ContactsUI.js
(onToolbarDeleteSelectedContacts): use translated labels.
* UI/WebServerResources/MailerUIdTree.js:
(MailerUIdTreeExtension._addFolderNode): no longer set a SPAN
arroung the unseen count.
(MailerUIdTreeExtension.getMailboxNode): new method returning the
DIV.dTreeNode corresponding to the mailbox name passed as
parameter.
* UI/WebServerResources/UIxPreferences.js: (_setupEvents): no
longer take any parameter, which simplifies the method, as
removing the "change" observers on input field is not useful.
(initMailAccounts): new method that parses the mail accounts JSON
dictionary, populates the mail account UL and setup related
events.
(onMailAccountInfoChange, onMailIdentityInfoChange)
(onMailIdentitySignatureClick, onMailIdentitySignatureOK)
(createMailAccountLI, onMailAccountEntryClick, displayMailAccount)
(displayAccountSignature, createMailAccount)
(onMailAccountAdd, onMailAccountDelete, saveMailAccounts)
(compactMailAccounts): new methods completing the above.
* UI/PreferencesUI/UIxPreferences.m (-identitiesList)
(-itemIdentityText, -signature, -setSignature): removed obsolete
methods.
(-defaultAction): when updating filters, we now only need to query
the "0" account since accounts are no longer identified by "name".
(-mailAuxiliaryUserAccountsEnabled): new bool method.
(-setMailAccounts): new accessor that decodes the new mail
accounts dictionary in JSON format and validates it prior to save
it in the user defaults.
(-mailAccounts): new accessor.
* UI/MailerUI/UIxMailFolderActions.m (_setFolderPurpose:): we now
use the corresponding methods on the SOGoUserDefaults instance
rather than in the user settings (old bug) when setting folders
for the main account. Additionally, we now handle the entries from
auxiliary accounts.
* UI/MailerUI/UIxMailEditor.m (-fromEmails): the list of
identities is now taken from the corresponding mail account dict.
* SoObjects/Mailer/SOGoMailForward.m (-signature): same as below.
* UI/MailerUI/UIxMailAccountActions.m (-composeAction): same as below.
* SoObjects/Mailer/SOGoDraftObject.m (-fetchMailForForwarding):
the signature is now taken from the original mail's corresponding
account.
* SoObjects/Mailer/SOGoMailFolder.m
(-copyUIDs:toFolder:inContext:): we now ensure that the operation
is not attempted on two separate accounts.
(-httpURLForAdvisoryToUser): the returned mail account is now
always "0".
* SoObjects/Mailer/SOGoMailBaseObject.m (-imap4URL): we now add
the "tls=YES" url parameter if the corresponding account
encryption is set to "tls".
(-imap4Login): removed useless method.
(-imap4PasswordRenewed:): now a proxy to the same method on the
instance account folder.
* SoObjects/Mailer/SOGoMailAccount.m (-identities, -signature)
(encryption): new helper methods that return the corresponding key
in the appropriate mail account dictionary.
(-_migrateFolderWithPurpose:): moved method in SOGoMailAccount.m.
(-_userFolderNameWithPurpose): rewrote method to make use of the
mail account dictionaries. Fallback on the main account of the
auxiliary ones do not have the required entry.
(-imap4PasswordRenewed:): moved method from SOGoMailBaseObject.m
and use the "password" entry in auxiliary account dicts depending
on "nameInContainer".
(-imap4LoginFromHTTP, -imap4Login): removed useless methods.
* SoObjects/SOGo/SOGoUser.m (-mailAccounts): we now conditionnally
append the array of auxiliary user accounts, if enabled in the
domain defaults.
(-_appendSystemMailAccount): the user main signature and the
sent, drafts, trash folder location are now stored in the account
dictionaries. Added migration code from us to ud from
SOGoMailAccount.m.
* SoObjects/SOGo/SOGoDomainDefaults.m
(-mailAuxiliaryUserAccountsEnabled): new method returning a BOOL
describing whether the domain users can access auxiliary mail
accounts.
* SoObjects/SOGo/SOGoUserDefaults.m (-setAuxiliaryMailAccounts:)
(auxiliaryMailAccounts): new methods that receives and returns an
NSArray of NSDictionary describing the user's auxiliary mail
accounts.
* UI/WebServerResources/HTMLElement.js: (deselectAll): if the
container has a "selectedElements" attribute, then it's no longer
required to wander through the "selectedIds" attribute. This fixes
a strange bug on IE when modifying the selection on LI lists.
2010-08-05 Francis Lachapelle <flachapelle@inverse.ca>
* UI/WebServerResources/SOGoResizableTable.js
(computeColumnsWidths): was previous part of saveColumnsState.
Used when the user hasn't modify the columns widths but the local
"ratios" hash still need to be built.
2010-08-04 Francis Lachapelle <flachapelle@inverse.ca>
* UI/WebServerResources/generic.js (createDialog): when the
position class is set to "none", the dialog will be modal (with a
dimmed background).
* UI/WebServerResources/ContactsUI.js: replaced window.confirm by
a CSS modal dialog.
2010-08-04 Wolfgang Sourdeau <wsourdeau@inverse.ca>
* UI/WebServerResources/RowEditionController.js:
(RowEditionController.startEditing): avoid setting the whole
element class. Make use of addClassName/removeClassName instead,
so that classes added/removed via other evt handlers are not
ignored.
2010-08-03 Wolfgang Sourdeau <wsourdeau@inverse.ca>
* SoObjects/SOGo/SOGoUser.m (-_appendSystemMailAccount): new
private method with the mailbox code split from -[mailAccounts].
* UI/WebServerResources/MailerUI.js (composeNewMessage): the first
account is always identified as "0".
* UI/MailerUI/UIxMailMainFrame.m (-mailAccounts): we now return
tje JSON rep. of a simple and flat array containing the account
display names, since the account are represented with an integer
index.
(-inboxData): we directly fetch the first mail account by using
the "0" key.
(-composeAction): same as above.
* SoObjects/Mailer/SOGoMailAccount.m (-imap4URLString): the
"nameInContainer" now represents the index of the current account
in the array of user mail accounts, which simplifies this method.
We also handle the "encryption" key, albeit currently unused, and
we now add the port to the url as needed.
(-shortTitle): removed method.
(-davDisplayName): we only need to return the "name" key of the
current account.
* SoObjects/Mailer/SOGoMailAccounts.m (-mailAccounts): new utility
method that returns the mail account dictionaries corresponding to
the owner.
(-toManyRelationshipKeys): we now identify mail accounts via their
index in the array of accounts, since the order will never vary,
this simplifies the code.
(-lookupName:inContext:acquire:): same as the above.
* UI/WebServerResources/RowEditionController.js:
RowEditionController.startEditing: keep the "_selected" class
when starting edition, if exists.
RowEditionController.acceptEdition: revert the changes if the new
value is an empty string (might be wrong, though).
RowEditionController.onInputKeyDown: we now handle the tab key
too.
2010-08-03 Ludovic Marcotte <lmarcotte@inverse.ca>
* Applied patch for bug #690
@@ -49,6 +369,30 @@
* Updated Ukrainian translation - patch from
Oleksa Stasevych <oleksiy.stasevych@gmail.com>
2010-08-02 Wolfgang Sourdeau <wsourdeau@inverse.ca>
* UI/WebServerResources/RowEditionController.js: new class module
that implements the handling of editable list items.
* SoObjects/SOGo/NSNumber+Utilities.m (-jsonRepresentation):
returns "true" or "false" when the initial return value is "YES"
or "NO".
2010-07-30 Francis Lachapelle <flachapelle@inverse.ca>
* UI/WebServerResources/SchedulerUI.js (deleteEvent): improved
the function to delete all selected events cells.
(onSelectAll): improved the function to select all events cells.
(onDocumentKeydown): select all elements upon ctrl-A.
(onCalendarSelectEvent): allow multiple selections.
* UI/WebServerResources/HTMLElement.js (selectAll): new method to
select all entries of a list or a table.
* UI/WebServerResources/scriptaculous/dragdrop.js (initDrag):
avoid stopping the event propagation so the blur event is properly
fired on the search input field when it isfocused.
2010-07-28 Francis Lachapelle <flachapelle@inverse.ca>
* UI/WebServerResources/MailerUI.js (deleteCachedMailboxByType,

View File

@@ -504,4 +504,30 @@
return r;
}
- (SOGoWebDAVValue *) davCurrentUserPrincipal
{
NSDictionary *userHREF;
NSString *usersUrl, *login;
SOGoUser *activeUser;
SOGoWebDAVValue *davCurrentUserPrincipal;
activeUser = [[self context] activeUser];
login = [activeUser login];
if ([login isEqualToString: @"anonymous"])
davCurrentUserPrincipal = nil;
else
{
usersUrl = [NSString stringWithFormat: @"%@%@/",
[self davURLAsString], login];
userHREF = davElementWithContent (@"href", XMLNS_WEBDAV, usersUrl);
davCurrentUserPrincipal
= [davElementWithContent (@"current-user-principal",
XMLNS_WEBDAV,
userHREF)
asWebDAVValue];
}
return davCurrentUserPrincipal;
}
@end

View File

@@ -1,3 +1,8 @@
2010-08-11 Wolfgang Sourdeau <wsourdeau@inverse.ca>
* NGVCardPhoto.m (-type): returns @"JPEG" if the type is
unspecified.
2010-07-21 Wolfgang Sourdeau <wsourdeau@inverse.ca>
* iCalXMLRenderer.m (_appendPaddingValues:withTag:intoString:):

View File

@@ -37,7 +37,13 @@
- (NSString *) type
{
return [[self value: 0 ofAttribute: @"type"] uppercaseString];
NSString *type;
type = [[self value: 0 ofAttribute: @"type"] uppercaseString];
if (!type)
type = @"JPEG";
return type;
}
- (NSData *) decodedContent

View File

@@ -1,3 +1,9 @@
2010-08-11 Wolfgang Sourdeau <wsourdeau@inverse.ca>
* VSSaxDriver.m (_parseAttr:forTag:intoAttr:intoValue:): when no
attribute tag is not specified and the value is "BASE64" or "B", the
attribute tag is set to "ENCODING".
2010-05-05 Wolfgang Sourdeau <wsourdeau@inverse.ca>
* VSSaxDriver.m (_endComponent:value:): avoid a crash occurring
@@ -38,7 +44,7 @@
* VSSaxDriver.m: improved error reporting in case no data could be
retrieved from a URL (v4.5.22)
2005-12-05 Helge Hess <helge.hess@skyrix.com>
* v4.5.21

View File

@@ -325,7 +325,7 @@ static NSCharacterSet *whitespaceCharSet = nil;
intoValue: (NSString **) value_
{
NSRange r;
NSString *attrName, *attrValue;
NSString *attrName, *attrValue, *upperAttr;
r = [_attr rangeOfCharacterFromSet: equalSignCharSet];
if (r.length > 0)
@@ -360,13 +360,16 @@ static NSCharacterSet *whitespaceCharSet = nil;
}
else
{
if ([[_attr uppercaseString] isEqualToString: @"QUOTED-PRINTABLE"])
upperAttr = [_attr uppercaseString];
if ([upperAttr isEqualToString: @"QUOTED-PRINTABLE"]
|| [upperAttr isEqualToString: @"BASE64"]
|| [upperAttr isEqualToString: @"B"])
attrName = @"ENCODING";
else
attrName = @"TYPE";
attrValue = _attr;
}
#if 0
// ZNeK: what's this for?
r = [attrValue rangeOfCharacterFromSet: commaCharSet];

View File

@@ -81,6 +81,16 @@
#define defaultColor @"#AAAAAA"
@interface SOGoGCSFolder (SOGoPrivate)
- (void) appendObject: (NSDictionary *) object
properties: (NSString **) properties
count: (unsigned int) propertiesCount
withBaseURL: (NSString *) baseURL
toBuffer: (NSMutableString *) r;
@end
@implementation SOGoAppointmentFolder
static NSNumber *sharedYes = nil;
@@ -1065,27 +1075,10 @@ firstInstanceCalendarDateRange: (NGCalendarDateRange *) fir
return ma;
}
- (void) _appendPropstat: (NSDictionary *) propstat
toBuffer: (NSMutableString *) r
{
NSArray *properties;
unsigned int count, max;
[r appendString: @"<D:propstat><D:prop>"];
properties = [propstat objectForKey: @"properties"];
max = [properties count];
for (count = 0; count < max; count++)
[r appendString: [properties objectAtIndex: count]];
[r appendString: @"</D:prop><D:status>"];
[r appendString: [propstat objectForKey: @"status"]];
[r appendString: @"</D:status></D:propstat>"];
}
#warning we should use the EOFetchSpecification for that!!! (see doPROPFIND:)
#warning components in calendar-data query are ignored
#warning the two following methods should be replaced with the new dav rendering mechanism
- (NSString *) _nodeTagForProperty: (NSString *) property
{
NSString *namespace, *nodeName, *nsRep;
@@ -1103,158 +1096,6 @@ firstInstanceCalendarDateRange: (NGCalendarDateRange *) fir
return [NSString stringWithFormat: @"%@:%@", nsRep, nodeName];
}
- (NSString *) _nodeTag: (NSString *) property
{
static NSMutableDictionary *tags = nil;
NSString *nodeTag;
if (!tags)
tags = [NSMutableDictionary new];
nodeTag = [tags objectForKey: property];
if (!nodeTag)
{
nodeTag = [self _nodeTagForProperty: property];
[tags setObject: nodeTag forKey: property];
}
return nodeTag;
}
- (NSString **) _properties: (NSString **) properties
count: (unsigned int) propertiesCount
ofObject: (NSDictionary *) object
{
SOGoCalendarComponent *sogoObject;
NSString **currentProperty;
NSString **values, **currentValue;
SEL methodSel;
// NSLog (@"_properties:ofObject:: %@", [NSDate date]);
values = NSZoneMalloc (NULL,
(propertiesCount + 1) * sizeof (NSString *));
*(values + propertiesCount) = nil;
//c = [self objectClassForComponentName: [object objectForKey: @"c_component"]];
#warning TODO: determine why this commented invocation takes so long...
// sogoObject = [self createChildComponentWithRecord: object];
sogoObject = [SOGoCalendarComponent objectWithRecord: object
inContainer: self];
[sogoObject setComponentTag: [object objectForKey: @"c_component"]];
currentProperty = properties;
currentValue = values;
while (*currentProperty)
{
methodSel = SOGoSelectorForPropertyGetter (*currentProperty);
if (methodSel && [sogoObject respondsToSelector: methodSel])
*currentValue = [[sogoObject performSelector: methodSel]
stringByEscapingXMLString];
currentProperty++;
currentValue++;
}
// NSLog (@"/_properties:ofObject:: %@", [NSDate date]);
return values;
}
- (NSArray *) _propstats: (NSString **) properties
count: (unsigned int) propertiesCount
ofObject: (NSDictionary *) object
{
NSMutableArray *propstats, *properties200, *properties404, *propDict;
NSString **property, **values, **currentValue;
NSString *propertyValue, *nodeTag;
// NSLog (@"_propstats:ofObject:: %@", [NSDate date]);
propstats = [NSMutableArray array];
properties200 = [NSMutableArray array];
properties404 = [NSMutableArray array];
values = [self _properties: properties count: propertiesCount
ofObject: object];
currentValue = values;
property = properties;
while (*property)
{
nodeTag = [self _nodeTag: *property];
if (*currentValue)
{
propertyValue = [NSString stringWithFormat: @"<%@>%@</%@>",
nodeTag, *currentValue, nodeTag];
propDict = properties200;
}
else
{
propertyValue = [NSString stringWithFormat: @"<%@/>", nodeTag];
propDict = properties404;
}
[propDict addObject: propertyValue];
property++;
currentValue++;
}
free (values);
if ([properties200 count])
[propstats addObject: [NSDictionary dictionaryWithObjectsAndKeys:
properties200, @"properties",
@"HTTP/1.1 200 OK", @"status",
nil]];
if ([properties404 count])
[propstats addObject: [NSDictionary dictionaryWithObjectsAndKeys:
properties404, @"properties",
@"HTTP/1.1 404 Not Found", @"status",
nil]];
// NSLog (@"/_propstats:ofObject:: %@", [NSDate date]);
return propstats;
}
#warning We need to use the new DAV utilities here...
#warning this is baddddd because we return a single-valued dictionary containing \
a cname which may not event exist... the logic behind appendObject:... should be \
rethought, especially since we may start using SQL views
- (void) appendObject: (NSDictionary *) object
properties: (NSString **) properties
count: (unsigned int) propertiesCount
withBaseURL: (NSString *) baseURL
toBuffer: (NSMutableString *) r
{
NSArray *propstats;
unsigned int count, max;
[r appendFormat: @"<D:response><D:href>"];
[r appendString: baseURL];
[r appendString: [object objectForKey: @"c_name"]];
[r appendString: @"</D:href>"];
// NSLog (@"(appendPropstats...): %@", [NSDate date]);
propstats = [self _propstats: properties count: propertiesCount
ofObject: object];
max = [propstats count];
for (count = 0; count < max; count++)
[self _appendPropstat: [propstats objectAtIndex: count]
toBuffer: r];
// NSLog (@"/(appendPropstats...): %@", [NSDate date]);
[r appendString: @"</D:response>"];
}
- (void) appendMissingObjectRef: (NSString *) href
toBuffer: (NSMutableString *) r
{
[r appendString: @"<D:response><D:href>"];
[r appendString: href];
[r appendString: @"</D:href><D:status>HTTP/1.1 404 Not Found</D:status></D:response>"];
}
- (NSCalendarDate *) _getMaxStartDate
{
NSCalendarDate *now, *rc;
@@ -1688,211 +1529,10 @@ firstInstanceCalendarDateRange: (NGCalendarDateRange *) fir
return r;
}
- (NSDictionary *) _deduceObjectNamesFromURLs: (NSArray *) urls
- (WOResponse *) davCalendarMultiget: (WOContext *) queryContext
{
unsigned int count, max;
NSString *url, *componentURLPath, *cName, *baseURLString;
NSMutableDictionary *cNames;
NSURL *componentURL, *baseURL;
NSArray *urlComponents;
max = [urls count];
cNames = [NSMutableDictionary dictionaryWithCapacity: max];
baseURL = [self davURL];
baseURLString = [self davURLAsString];
for (count = 0; count < max; count++)
{
url = [NSString stringWithFormat: @"%@/%@",
[[urls objectAtIndex: count] stringByDeletingLastPathComponent],
[[[urls objectAtIndex: count] lastPathComponent] stringByEscapingURL]];
componentURL = [[NSURL URLWithString: url relativeToURL: baseURL]
standardizedURL];
componentURLPath = [componentURL absoluteString];
if ([componentURLPath rangeOfString: baseURLString].location
!= NSNotFound)
{
urlComponents = [componentURLPath componentsSeparatedByString: @"/"];
cName = [[urls objectAtIndex: count] lastPathComponent];
[cNames setObject: [urls objectAtIndex: count] forKey: cName];
}
}
return cNames;
}
- (NSArray *) _fetchComponentsWithNames: (NSArray *) cNames
fields: (NSArray *) fields
{
NSMutableString *filterString;
NSArray *records;
// NSLog (@"fetchComponentsWithNames");
filterString = [NSMutableString string];
[filterString appendFormat: @"c_name='%@'",
[cNames componentsJoinedByString: @"' OR c_name='"]];
// NSLog (@"fetchComponentsWithNames: query");
records = [self bareFetchFields: fields
from: nil to: nil
title: nil
component: nil
additionalFilters: filterString];
// NSLog (@"/fetchComponentsWithNames");
return records;
}
#define maxQuerySize 2500
#define baseQuerySize 160
#define idQueryOverhead 13
- (NSArray *) _fetchComponentsMatchingObjectNames: (NSArray *) cNames
fields: (NSArray *) fields
{
NSMutableArray *components;
NSArray *records;
NSMutableArray *currentNames;
unsigned int count, max, currentSize, queryNameLength;
NSString *currentName;
// NSLog (@"fetching components matching names");
currentNames = [NSMutableArray array];
currentSize = baseQuerySize;
max = [cNames count];
components = [NSMutableArray arrayWithCapacity: max];
for (count = 0; count < max; count++)
{
currentName = [cNames objectAtIndex: count];
queryNameLength = idQueryOverhead + [currentName length];
if ((currentSize + queryNameLength)
> maxQuerySize)
{
records = [self _fetchComponentsWithNames: currentNames fields: fields];
[components addObjectsFromArray: records];
[currentNames removeAllObjects];
currentSize = baseQuerySize;
}
[currentNames addObject: currentName];
currentSize += queryNameLength;
}
records = [self _fetchComponentsWithNames: currentNames fields: fields];
[components addObjectsFromArray: records];
// NSLog (@"/fetching components matching names");
return components;
}
- (NSDictionary *) _fetchComponentsMatchingURLs: (NSArray *) urls
fields: (NSArray *) fields
{
NSMutableDictionary *components;
NSDictionary *cnames, *record;
NSString *recordURL;
NSArray *records;
unsigned int count, max;
components = [NSMutableDictionary dictionary];
cnames = [self _deduceObjectNamesFromURLs: urls];
records = [self _fetchComponentsMatchingObjectNames: [cnames allKeys]
fields: fields];
max = [records count];
for (count = 0; count < max; count++)
{
record = [records objectAtIndex: count];
recordURL = [cnames objectForKey: [record objectForKey: @"c_name"]];
if (recordURL)
[components setObject: record forKey: recordURL];
}
return components;
}
- (void) _appendComponentProperties: (NSDictionary *) properties
matchingURLs: (id <DOMNodeList>) refs
toResponse: (WOResponse *) response
{
NSObject <DOMElement> *element;
NSDictionary *currentComponent, *components;
NSString *currentURL, *baseURL, *currentField;
NSString **propertiesArray;
NSMutableArray *urls, *fields;
NSMutableString *buffer;
unsigned int count, max, propertiesCount;
NSEnumerator *addFields;
baseURL = [self davURLAsString];
#warning review this when fixing http://www.scalableogo.org/bugs/view.php?id=276
if (![baseURL hasSuffix: @"/"])
baseURL = [NSString stringWithFormat: @"%@/", baseURL];
urls = [NSMutableArray array];
max = [refs length];
for (count = 0; count < max; count++)
{
element = [refs objectAtIndex: count];
currentURL = [[element firstChild] nodeValue];
[urls addObject: currentURL];
}
propertiesArray = [[properties allKeys] asPointersOfObjects];
propertiesCount = [properties count];
fields = [NSMutableArray arrayWithObjects: @"c_name", @"c_component", nil];
addFields = [[properties allValues] objectEnumerator];
while ((currentField = [addFields nextObject]))
if ([currentField length])
[fields addObjectUniquely: currentField];
components = [self _fetchComponentsMatchingURLs: urls fields: fields];
max = [urls count];
// NSLog (@"adding properties with url");
buffer = [NSMutableString stringWithCapacity: max*512];
for (count = 0; count < max; count++)
{
currentComponent = [components objectForKey: [urls objectAtIndex: count]];
if (currentComponent)
[self appendObject: currentComponent
properties: propertiesArray
count: propertiesCount
withBaseURL: baseURL
toBuffer: buffer];
else
[self appendMissingObjectRef: currentURL
toBuffer: buffer];
}
[response appendContentString: buffer];
// NSLog (@"/adding properties with url");
NSZoneFree (NULL, propertiesArray);
}
- (id) davCalendarMultiget: (id) queryContext
{
WOResponse *r;
id <DOMDocument> document;
DOMElement *documentElement, *propElement;
r = [context response];
[r prepareDAVResponse];
[r appendContentString: @"<D:multistatus xmlns:D=\"DAV:\""
@" xmlns:C=\"urn:ietf:params:xml:ns:caldav\">"];
document = [[context request] contentAsDOMDocument];
documentElement = (DOMElement *) [document documentElement];
propElement = [documentElement firstElementWithTag: @"prop"
inNamespace: @"DAV:"];
[self _appendComponentProperties: [self parseDAVRequestedProperties: propElement]
matchingURLs: [documentElement getElementsByTagName: @"href"]
toResponse: r];
[r appendContentString:@"</D:multistatus>"];
return r;
return [self performMultigetInContext: queryContext
inNamespace: @"urn:ietf:params:xml:ns:caldav"];
}
- (NSString *) additionalWebdavSyncFilters

View File

@@ -42,10 +42,6 @@
@protocol SOGoContactFolder <NSObject>
- (void) appendObject: (NSDictionary *) object
withBaseURL: (NSString *) baseURL
toREPORTResponse: (WOResponse *) r;
- (NSArray *) lookupContactsWithFilter: (NSString *) filter
sortBy: (NSString *) sortKey
ordering: (NSComparisonResult) sortOrdering;

View File

@@ -35,6 +35,7 @@
#import <NGObjWeb/WOContext+SoObjects.h>
#import <SOGo/SOGoUser.h>
#import <SOGo/SOGoUserManager.h>
#import <SOGo/WORequest+SOGo.h>
#import "SOGoContactGCSFolder.h"
#import "SOGoContactSourceFolder.h"
@@ -87,4 +88,16 @@
return [self labelForKey: @"Personal Address Book"];
}
- (NSArray *) toManyRelationshipKeys
{
NSMutableArray *keys;
if ([[context request] isAddressBookApp])
keys = [NSMutableArray arrayWithObject: @"personal"];
else
keys = (NSMutableArray *) [super toManyRelationshipKeys];
return keys;
}
@end

View File

@@ -115,6 +115,11 @@
return @"text/x-vcard";
}
- (NSString *) davAddressData
{
return [self contentAsString];
}
/* specialized actions */
- (void) save

View File

@@ -23,6 +23,7 @@
#import <Foundation/NSArray.h>
#import <Foundation/NSEnumerator.h>
#import <Foundation/NSString.h>
#import <Foundation/NSURL.h>
#import <NGObjWeb/NSException+HTTP.h>
#import <NGObjWeb/SoObject+SoDAV.h>
@@ -31,15 +32,19 @@
#import <NGObjWeb/WOResponse.h>
#import <NGExtensions/NSObject+Logs.h>
#import <NGExtensions/NSString+misc.h>
#import <NGCards/CardGroup.h>
#import <SaxObjC/XMLNamespaces.h>
#import <EOControl/EOQualifier.h>
#import <EOControl/EOSortOrdering.h>
#import <NGCards/CardGroup.h>
#import <GDLContentStore/GCSFolder.h>
#import <SOGo/DOMNode+SOGo.h>
#import <SOGo/SOGoCache.h>
#import <SOGo/NSArray+Utilities.h>
#import <SOGo/NSDictionary+Utilities.h>
#import <SOGo/NSString+Utilities.h>
#import <SOGo/NSObject+DAV.h>
#import "SOGoContactGCSEntry.h"
#import "SOGoContactGCSList.h"
@@ -84,7 +89,7 @@ static NSArray *folderListingFields = nil;
- (Class) objectClassForComponentName: (NSString *) componentName
{
Class objectClass;
if ([componentName isEqualToString: @"vcard"])
objectClass = [SOGoContactGCSEntry class];
else if ([componentName isEqualToString: @"vlist"])
@@ -289,38 +294,17 @@ static NSArray *folderListingFields = nil;
return records;
}
#warning this should be unified within SOGoFolder
- (void) appendObject: (NSDictionary *) object
withBaseURL: (NSString *) baseURL
toREPORTResponse: (WOResponse *) r
- (NSDictionary *) davSQLFieldsTable
{
SOGoContactGCSEntry *component;
NSString *name, *etagLine, *contactString;
static NSMutableDictionary *davSQLFieldsTable = nil;
name = [object objectForKey: @"c_name"];
component = [self lookupName: name inContext: context acquire: NO];
if (!davSQLFieldsTable)
{
davSQLFieldsTable = [[super davSQLFieldsTable] mutableCopy];
[davSQLFieldsTable setObject: @"c_content" forKey: @"{urn:ietf:params:xml:ns:carddav}address-data"];
}
[r appendContentString: @" <D:response>\r\n"];
[r appendContentString: @" <D:href>"];
[r appendContentString: baseURL];
if (![baseURL hasSuffix: @"/"])
[r appendContentString: @"/"];
[r appendContentString: name];
[r appendContentString: @"</D:href>\r\n"];
[r appendContentString: @" <D:propstat>\r\n"];
[r appendContentString: @" <D:prop>\r\n"];
etagLine = [NSString stringWithFormat: @" <D:getetag>%@</D:getetag>\r\n",
[component davEntityTag]];
[r appendContentString: etagLine];
[r appendContentString: @" </D:prop>\r\n"];
[r appendContentString: @" <D:status>HTTP/1.1 200 OK</D:status>\r\n"];
[r appendContentString: @" </D:propstat>\r\n"];
[r appendContentString: @" <C:addressbook-data>"];
contactString = [[component contentAsString] stringByEscapingXMLString];
[r appendContentString: contactString];
[r appendContentString: @"</C:addressbook-data>\r\n"];
[r appendContentString: @" </D:response>\r\n"];
return davSQLFieldsTable;
}
- (NSArray *) davComplianceClassesInContext: (id)_ctx
@@ -359,6 +343,12 @@ static NSArray *folderListingFields = nil;
return resourceType;
}
- (id) davAddressbookMultiget: (id) queryContext
{
return [self performMultigetInContext: queryContext
inNamespace: @"urn:ietf:params:xml:ns:carddav"];
}
/* sorting */
- (NSComparisonResult) compare: (id) otherFolder
{
@@ -385,4 +375,22 @@ static NSArray *folderListingFields = nil;
return @"IPF.Contact";
}
/* TODO: multiget reorg */
- (NSString *) _nodeTagForProperty: (NSString *) property
{
NSString *namespace, *nodeName, *nsRep;
NSRange nsEnd;
nsEnd = [property rangeOfString: @"}"];
namespace
= [property substringFromRange: NSMakeRange (1, nsEnd.location - 1)];
nodeName = [property substringFromIndex: nsEnd.location + 1];
if ([namespace isEqualToString: XMLNS_CARDDAV])
nsRep = @"C";
else
nsRep = @"D";
return [NSString stringWithFormat: @"%@:%@", nsRep, nodeName];
}
@end /* SOGoContactGCSFolder */

View File

@@ -34,7 +34,6 @@
#import <NGObjWeb/SoSelectorInvocation.h>
#import <NGObjWeb/SoUser.h>
#import <NGExtensions/NSString+misc.h>
#import <NGExtensions/NSObject+Logs.h>
#import <EOControl/EOSortOrdering.h>
#import <SaxObjC/XMLNamespaces.h>
@@ -50,49 +49,6 @@
@implementation SOGoContactSourceFolder
#warning this should be unified within SOGoFolder
- (void) appendObject: (NSDictionary *) object
withBaseURL: (NSString *) baseURL
toREPORTResponse: (WOResponse *) r
{
SOGoContactLDIFEntry *component;
NSString *name, *etagLine, *contactString;
name = [object objectForKey: @"c_name"];
if ([name length])
{
component = [self lookupName: name inContext: context acquire: NO];
if ([component isKindOfClass: [NSException class]])
{
[self logWithFormat: @"Object with name '%@' not found. You likely have a LDAP configuration issue.", name];
return;
}
[r appendContentString: @" <D:response>\r\n"];
[r appendContentString: @" <D:href>"];
[r appendContentString: baseURL];
if (![baseURL hasSuffix: @"/"])
[r appendContentString: @"/"];
[r appendContentString: name];
[r appendContentString: @"</D:href>\r\n"];
[r appendContentString: @" <D:propstat>\r\n"];
[r appendContentString: @" <D:prop>\r\n"];
etagLine = [NSString stringWithFormat: @" <D:getetag>%@</D:getetag>\r\n",
[component davEntityTag]];
[r appendContentString: etagLine];
[r appendContentString: @" </D:prop>\r\n"];
[r appendContentString: @" <D:status>HTTP/1.1 200 OK</D:status>\r\n"];
[r appendContentString: @" </D:propstat>\r\n"];
[r appendContentString: @" <C:addressbook-data>"];
contactString = [[component contentAsString] stringByEscapingXMLString];
[r appendContentString: contactString];
[r appendContentString: @"</C:addressbook-data>\r\n"];
[r appendContentString: @" </D:response>\r\n"];
}
}
+ (id) folderWithName: (NSString *) aName
andDisplayName: (NSString *) aDisplayName
inContainer: (id) aContainer

View File

@@ -27,6 +27,7 @@
#import <NGObjWeb/WOContext.h>
#import <NGObjWeb/WORequest.h>
#import <NGObjWeb/WOResponse.h>
#import <NGExtensions/NSObject+Logs.h>
#import <NGExtensions/NSString+misc.h>
#import <DOM/DOMProtocols.h>
#import <SaxObjC/SaxObjC.h>
@@ -39,6 +40,50 @@
@implementation SOGoFolder (CardDAV)
- (void) _appendObject: (NSDictionary *) object
withBaseURL: (NSString *) baseURL
toREPORTResponse: (WOResponse *) r
{
id component;
NSString *name, *etagLine, *contactString;
name = [object objectForKey: @"c_name"];
if ([name length])
{
component = [self lookupName: name inContext: context acquire: NO];
if ([component isKindOfClass: [NSException class]])
{
[self logWithFormat: @"Object with name '%@' not found. You likely have a LDAP configuration issue.", name];
return;
}
#warning we provide both "address-data" and "addressbook-data" for compatibility reasons, we should actually check which one has been queried
[r appendContentString: @"<D:response>"
@"<D:href>"];
[r appendContentString: baseURL];
if (![baseURL hasSuffix: @"/"])
[r appendContentString: @"/"];
[r appendContentString: name];
[r appendContentString: @"</D:href>"
@"<D:propstat>"
@"<D:prop>"];
etagLine = [NSString stringWithFormat: @"<D:getetag>%@</D:getetag>",
[component davEntityTag]];
[r appendContentString: etagLine];
[r appendContentString: @"</D:prop>"
@"<D:status>HTTP/1.1 200 OK</D:status>"
@"</D:propstat>"
@"<C:addressbook-data>"];
contactString = [[component contentAsString] stringByEscapingXMLString];
[r appendContentString: contactString];
[r appendContentString: @"</C:addressbook-data>"
@"<C:address-data>"];
[r appendContentString: contactString];
[r appendContentString: @"</C:address-data>"
@"</D:response>"];
}
}
- (void) _appendComponentsMatchingFilters: (NSArray *) filters
toResponse: (WOResponse *) response
context: (id) localContext
@@ -60,9 +105,8 @@
objectEnumerator];
while ((contact = [contacts nextObject]))
[(id<SOGoContactFolder>)self appendObject: contact
withBaseURL: baseURL
toREPORTResponse: response];
[self _appendObject: contact withBaseURL: baseURL
toREPORTResponse: response];
}
}
@@ -143,7 +187,7 @@
[self _appendComponentsMatchingFilters: filters
toResponse: r
context: queryContext];
[r appendContentString:@"</D:multistatus>"];
[r appendContentString: @"</D:multistatus>"];
return r;
}

View File

@@ -722,7 +722,7 @@ static NSString *userAgent = nil;
{
// TODO: use subject for filename?
// error = [newDraft saveAttachment:content withName:@"forward.eml"];
signature = [ud mailSignature];
signature = [[self mailAccountFolder] signature];
if ([signature length])
[self setText: [NSString stringWithFormat: @"\n-- \n%@", signature]];
attachment = [NSDictionary dictionaryWithObjectsAndKeys:

View File

@@ -50,7 +50,6 @@ typedef enum {
@interface SOGoMailAccount : SOGoMailBaseObject
{
NSString *accountName;
SOGoMailFolder *inboxFolder;
SOGoDraftsFolder *draftsFolder;
SOGoSentFolder *sentFolder;
@@ -58,14 +57,16 @@ typedef enum {
SOGoIMAPAclStyle imapAclStyle;
}
- (void) setAccountName: (NSString *) newAccountName;
- (SOGoIMAPAclStyle) imapAclStyle;
- (BOOL) imapAclConformsToIMAPExt;
- (BOOL) supportsQuotas;
- (BOOL) updateFilters;
- (NSArray *) identities;
- (NSString *) signature;
- (NSString *) encryption;
/* folder pathes */
- (NSArray *) allFolderPaths;

View File

@@ -40,6 +40,7 @@
#import <SOGo/NSArray+Utilities.h>
#import <SOGo/NSString+Utilities.h>
#import <SOGo/SOGoAuthenticator.h>
#import <SOGo/SOGoDomainDefaults.h>
#import <SOGo/SOGoUser.h>
#import <SOGo/SOGoUserDefaults.h>
@@ -69,7 +70,6 @@ static NSString *sieveScriptName = @"sogo";
draftsFolder = nil;
sentFolder = nil;
trashFolder = nil;
accountName = nil;
imapAclStyle = undefined;
}
@@ -82,15 +82,9 @@ static NSString *sieveScriptName = @"sogo";
[draftsFolder release];
[sentFolder release];
[trashFolder release];
[accountName release];
[super dealloc];
}
- (void) setAccountName: (NSString *) newAccountName
{
ASSIGN (accountName, newAccountName);
}
/* listing the available folders */
- (BOOL) isInDraftsFolder
@@ -362,7 +356,7 @@ static NSString *sieveScriptName = @"sogo";
result = [client login: [[self imap4URL] user] password: password];
if (![[result valueForKey:@"result"] boolValue]) {
[self errorWithFormat: @"failure. Attempting with a renewed password."];
password = [self imap4PasswordRenewed: NO];
password = [self imap4PasswordRenewed: YES];
result = [client login: [[self imap4URL] user] password: password];
}
@@ -475,76 +469,82 @@ static NSString *sieveScriptName = @"sogo";
/* IMAP4 */
- (BOOL) useSSL
{
return NO;
}
- (NSString *) imap4LoginFromHTTP
{
WORequest *rq;
NSString *s;
NSArray *creds;
rq = [context request];
s = [rq headerForKey:@"x-webobjects-remote-user"];
if ([s length] > 0)
return s;
if ((s = [rq headerForKey:@"authorization"]) == nil) {
/* no basic auth */
return nil;
}
creds = [SoHTTPAuthenticator parseCredentials:s];
if ([creds count] < 2)
/* somehow invalid */
return nil;
return [creds objectAtIndex:0]; /* the user */
}
- (NSString *) _urlHostString
- (NSDictionary *) _mailAccount
{
NSDictionary *mailAccount;
NSString *username, *escUsername, *hostString;
NSArray *accounts;
SOGoUser *user;
mailAccount = [[context activeUser] accountWithName: accountName];
if (mailAccount)
{
username = [mailAccount objectForKey: @"userName"];
escUsername
= [[username stringByEscapingURL] stringByReplacingString: @"@"
withString: @"%40"];
hostString = [NSString stringWithFormat: @"%@@%@", escUsername,
[mailAccount objectForKey: @"serverName"]];
}
user = [SOGoUser userWithLogin: [self ownerInContext: nil]];
accounts = [user mailAccounts];
mailAccount = [accounts objectAtIndex: [nameInContainer intValue]];
return mailAccount;
}
- (NSArray *) identities
{
return [[self _mailAccount] objectForKey: @"identities"];
}
- (NSString *) signature
{
NSArray *identities;
NSString *signature;
identities = [self identities];
if ([identities count] > 0)
signature = [[identities objectAtIndex: 0] objectForKey: @"signature"];
else
hostString = @"localhost";
signature = nil;
return hostString;
return signature;
}
- (NSString *) encryption
{
NSString *encryption;
encryption = [[self _mailAccount] objectForKey: @"encryption"];
if (![encryption length])
encryption = @"none";
return encryption;
}
- (NSMutableString *) imap4URLString
{
/* private, overridden by SOGoSharedMailAccount */
NSMutableString *urlString;
NSString *host;
NSMutableString *imap4URLString;
NSDictionary *mailAccount;
NSString *encryption, *protocol, *username, *escUsername;
int defaultPort, port;
urlString = [NSMutableString string];
if ([self useSSL])
[urlString appendString: @"imaps://"];
mailAccount = [self _mailAccount];
encryption = [mailAccount objectForKey: @"encryption"];
if ([encryption isEqualToString: @"ssl"])
{
protocol = @"imaps";
defaultPort = 993;
}
else
[urlString appendString: @"imap://"];
{
protocol = @"imap";
defaultPort = 143;
}
username = [mailAccount objectForKey: @"userName"];
escUsername
= [[username stringByEscapingURL] stringByReplacingString: @"@"
withString: @"%40"];
imap4URLString = [NSMutableString stringWithFormat: @"%@://%@@%@",
protocol, escUsername,
[mailAccount objectForKey: @"serverName"]];
port = [[mailAccount objectForKey: @"port"] intValue];
if (port && port != defaultPort)
[imap4URLString appendFormat: @":%d", port];
[imap4URLString appendString: @"/"];
host = [self _urlHostString];
if (![host rangeOfString: @"@"].length)
[urlString appendFormat: @"%@@", [self imap4LoginFromHTTP]];
[urlString appendFormat: @"%@/", host];
return urlString;
return imap4URLString;
}
- (NSMutableString *) traversalFromMailAccount
@@ -552,9 +552,33 @@ static NSString *sieveScriptName = @"sogo";
return [NSMutableString string];
}
- (NSString *) imap4Login
- (NSString *) imap4PasswordRenewed: (BOOL) renewed
{
return [[self imap4URL] user];
/*
Extract password from basic authentication.
*/
NSURL *imapURL;
NSString *password;
if ([nameInContainer isEqualToString: @"0"])
{
imapURL = [self imap4URL];
password = [[self authenticatorInContext: context]
imapPasswordInContext: context
forServer: [imapURL host]
forceRenew: renewed];
if (!password)
[self errorWithFormat: @"no IMAP4 password available"];
}
else
{
password = [[self _mailAccount] objectForKey: @"password"];
if (!password)
password = @"";
}
return password;
}
/* name lookup */
@@ -610,70 +634,27 @@ static NSString *sieveScriptName = @"sogo";
return inboxFolderName;
}
- (BOOL) _migrateFolderWithPurpose: (NSString *) purpose
withName: (NSString *) folderName
{
SOGoUserDefaults *ud;
NSString *methodName;
SEL methodSel;
BOOL rc;
ud = [[context activeUser] userDefaults];
methodName = [NSString stringWithFormat: @"set%@FolderName:", purpose];
methodSel = NSSelectorFromString (methodName);
if ([ud respondsToSelector: methodSel])
{
[ud performSelector: methodSel withObject: folderName];
[ud synchronize];
rc = YES;
}
else
{
[self errorWithFormat: @"method '%@' not available with user defaults"
@" object, folder migration fails", methodName];
rc = NO;
}
return rc;
}
- (NSString *) _userFolderNameWithPurpose: (NSString *) purpose
{
SOGoUser *user;
SOGoUserSettings *us;
SOGoUserDefaults *ud;
NSMutableDictionary *mailSettings;
NSString *folderName, *key, *methodName;
SEL methodSel;
NSArray *accounts;
int accountIdx;
NSDictionary *account;
NSString *folderName;
folderName = nil;
user = [context activeUser];
/* migration part: */
us = [user userSettings];
mailSettings = [us objectForKey: @"Mail"];
if (mailSettings)
user = [SOGoUser userWithLogin: [self ownerInContext: nil]];
accounts = [user mailAccounts];
accountIdx = [nameInContainer intValue];
account = [accounts objectAtIndex: accountIdx];
folderName = [[account objectForKey: @"mailboxes"]
objectForKey: purpose];
if (!folderName && accountIdx > 0)
{
key = [NSString stringWithFormat: @"%@Folder", purpose];
folderName = [mailSettings objectForKey: key];
if ([folderName length]
&& [self _migrateFolderWithPurpose: purpose withName: folderName])
{
[mailSettings removeObjectForKey: key];
[us synchronize];
folderName = nil;
}
}
else
folderName = nil;
if (!folderName)
{
ud = [[context activeUser] userDefaults];
methodName = [NSString stringWithFormat: @"%@FolderName",
[purpose lowercaseString]];
methodSel = NSSelectorFromString (methodName);
folderName = [ud performSelector: methodSel];
account = [accounts objectAtIndex: 0];
folderName = [[account objectForKey: @"mailboxes"]
objectForKey: purpose];
}
return folderName;
@@ -800,40 +781,9 @@ static NSString *sieveScriptName = @"sogo";
return [[self imap4Connection] createMailbox:_name atURL:[self imap4URL]];
}
- (NSString *) shortTitle
{
NSString *login, *host;
NSRange r;
r = [accountName rangeOfString:@"@"];
if (r.length > 0)
{
login = [accountName substringToIndex:r.location];
host = [accountName substringFromIndex:(r.location + r.length)];
}
else
{
login = nil;
host = accountName;
}
r = [host rangeOfString:@"."];
if (r.length > 0)
host = [host substringToIndex:r.location];
if ([login length] == 0)
return host;
r = [login rangeOfString:@"."];
if (r.length > 0)
login = [login substringToIndex:r.location];
return [NSString stringWithFormat:@"%@@%@", login, host];
}
- (NSString *) davDisplayName
{
return [self shortTitle];
return [[self _mailAccount] objectForKey: @"name"];
}
@end /* SOGoMailAccount */

View File

@@ -41,12 +41,10 @@
@class NSMutableDictionary;
@interface SOGoMailAccounts : SOGoFolder
{
NSMutableDictionary *accountKeys;
}
- (NSArray *) mailAccounts;
- (NSArray *) toManyRelationshipKeys;
- (NSDictionary *) accountKeys;
@end

View File

@@ -36,113 +36,55 @@
@implementation SOGoMailAccounts
/* listing the available mailboxes */
// - (BOOL) isInHomeFolderBranchOfLoggedInAccount: (NSString *) userLogin
// {
// return [[container nameInContainer] isEqualToString: userLogin];
// }
- (id) init
- (NSArray *) mailAccounts
{
if ((self = [super init]))
{
accountKeys = nil;
}
SOGoUser *user;
user = [SOGoUser userWithLogin: [self ownerInContext: nil]];
return self;
}
- (void) dealloc
{
[accountKeys release];
[super dealloc];
}
- (void) _initAccountKeys
{
NSArray *accounts;
NSString *currentName;
int count, max;
if (!accountKeys)
{
accountKeys = [NSMutableDictionary new];
accounts = [[context activeUser] mailAccounts];
max = [accounts count];
for (count = 0; count < max; count++)
{
currentName = [[accounts objectAtIndex: count] objectForKey: @"name"];
[accountKeys setObject: currentName
forKey: [currentName asCSSIdentifier]];
}
}
}
- (NSDictionary *) accountKeys
{
[self _initAccountKeys];
return accountKeys;
return [user mailAccounts];
}
- (NSArray *) toManyRelationshipKeys
{
[self _initAccountKeys];
NSMutableArray *keys;
NSArray *accounts;
int count, max;
SOGoUser *user;
user = [SOGoUser userWithLogin: [self ownerInContext: nil]];
accounts = [user mailAccounts];
max = [accounts count];
return [accountKeys allKeys];
keys = [NSMutableArray arrayWithCapacity: max];
for (count = 0; count < max; count++)
[keys addObject: [NSString stringWithFormat: @"%d", count]];
return keys;
}
/* name lookup */
// - (id) mailAccountWithName: (NSString *) _key
// inContext: (id) _ctx
// {
// static Class ctClass = Nil;
// id ct;
// if (ctClass == Nil)
// ctClass = NSClassFromString(@"SOGoMailAccount");
// if (ctClass == Nil) {
// [self errorWithFormat:@"missing SOGoMailAccount class!"];
// return nil;
// }
// ct = [[ctClass alloc] initWithName:_key inContainer:self];
// return [ct autorelease];
// }
- (id) lookupName: (NSString *) _key
inContext: (id) _ctx
acquire: (BOOL) _flag
{
id obj;
NSString *accountName;
// NSString *userLogin;
// userLogin = [[context activeUser] login];
NSArray *accounts;
SOGoUser *user;
int keyCount;
// if (![self isInHomeFolderBranchOfLoggedInAccount: userLogin]) {
// [self warnWithFormat:@ "User %@ tried to access mail hierarchy of %@",
// userLogin, [container nameInContainer]];
// return [NSException exceptionWithHTTPStatus:403 /* Forbidden */
// reason:@"Tried to access the mail of another user"];
// }
/* first check attributes directly bound to the application */
obj = [super lookupName:_key inContext:_ctx acquire:NO];
if (!obj)
{
[self _initAccountKeys];
accountName = [accountKeys objectForKey: _key];
if ([accountName length])
{
obj = [SOGoMailAccount objectWithName: _key inContainer: self];
[obj setAccountName: accountName];
}
user = [SOGoUser userWithLogin: [self ownerInContext: nil]];
accounts = [user mailAccounts];
keyCount = [_key intValue];
if ([_key isEqualToString: [NSString stringWithFormat: @"%d", keyCount]]
&& keyCount > -1 && keyCount < [accounts count])
obj = [SOGoMailAccount objectWithName: _key inContainer: self];
else
obj = [NSException exceptionWithHTTPStatus: 404 /* Not Found */];
}

View File

@@ -69,7 +69,6 @@
- (NSMutableString *) traversalFromMailAccount;
- (NSURL *) imap4URL;
- (NSString *) imap4Login;
- (NSString *) imap4PasswordRenewed: (BOOL) renew;
- (void) flushMailCaches;

View File

@@ -30,8 +30,7 @@
#import <NGExtensions/NSString+misc.h>
#import <NGExtensions/NSURL+misc.h>
#import <SOGo/SOGoAuthenticator.h>
#import "SOGoMailAccount.h"
#import "SOGoMailManager.h"
#import "SOGoMailBaseObject.h"
@@ -187,44 +186,28 @@ static BOOL debugOn = YES;
- (NSURL *) imap4URL
{
SOGoMailAccount *account;
NSString *urlString;
/* this could probably be handled better from NSURL but it's buggy in
GNUstep */
if (!imap4URL)
imap4URL = [[NSURL alloc] initWithString: [self imap4URLString]];
{
account = [self mailAccountFolder];
if ([[account encryption] isEqualToString: @"tls"])
urlString = [NSString stringWithFormat: @"%@?tls=YES",
[self imap4URLString]];
else
urlString = [self imap4URLString];
imap4URL = [[NSURL alloc] initWithString: urlString];
}
return imap4URL;
}
- (NSString *) imap4Login
{
if (![container respondsToSelector:_cmd])
return nil;
return [container imap4Login];
}
- (NSString *) imap4PasswordRenewed: (BOOL) renewed
{
/*
Extract password from basic authentication.
TODO: we might want to
a) move the primary code to SOGoMailAccount
b) cache the password
*/
NSURL *imapURL;
NSString *password;
imapURL = [[self mailAccountFolder] imap4URL];
password = [[self authenticatorInContext: context]
imapPasswordInContext: context
forServer: [imapURL host]
forceRenew: renewed];
if (!password)
[self errorWithFormat: @"no IMAP4 password available"];
return password;
return [[self mailAccountFolder] imap4PasswordRenewed: renewed];
}
- (NSMutableString *) traversalFromMailAccount

View File

@@ -447,7 +447,7 @@ static NSString *defaultUserID = @"anyone";
inContext: (id) localContext
{
NSArray *folders;
NSString *currentFolderName;
NSString *currentFolderName, *currentAccountName;
NSMutableString *imapDestinationFolder;
NGImap4Client *client;
id result;
@@ -458,31 +458,45 @@ static NSString *defaultUserID = @"anyone";
folders = [[destinationFolder componentsSeparatedByString: @"/"]
resultsOfSelector: @selector (fromCSSIdentifier)];
max = [folders count];
for (count = 2; count < max; count++)
if (max > 1)
{
currentFolderName
= [[folders objectAtIndex: count] substringFromIndex: 6];
[imapDestinationFolder appendFormat: @"/%@", currentFolderName];
currentAccountName = [[self mailAccountFolder] nameInContainer];
if ([[folders objectAtIndex: 1] isEqualToString: currentAccountName])
{
for (count = 2; count < max; count++)
{
currentFolderName
= [[folders objectAtIndex: count] substringFromIndex: 6];
[imapDestinationFolder appendFormat: @"/%@", currentFolderName];
}
client = [[self imap4Connection] client];
[imap4 selectFolder: [self imap4URL]];
// We make sure the destination IMAP folder exist, if not, we create it.
result = [[client status: imapDestinationFolder
flags: [NSArray arrayWithObject: @"UIDVALIDITY"]]
objectForKey: @"result"];
if (![result boolValue])
result = [[self imap4Connection] createMailbox: imapDestinationFolder
atURL: [[self mailAccountFolder] imap4URL]];
if (!result || [result boolValue])
result = [client copyUids: uids toFolder: imapDestinationFolder];
if ([[result valueForKey: @"result"] boolValue])
result = nil;
else
result = [NSException exceptionWithHTTPStatus: 500
reason: @"Couldn't copy UIDs."];
}
else
result = [NSException exceptionWithHTTPStatus: 500
reason: @"Cannot copy messages across different accounts."];
}
client = [[self imap4Connection] client];
[imap4 selectFolder: [self imap4URL]];
// We make sure the destination IMAP folder exist, if not, we create it.
result = [[client status: imapDestinationFolder
flags: [NSArray arrayWithObject: @"UIDVALIDITY"]]
objectForKey: @"result"];
if (![result boolValue])
result = [[self imap4Connection] createMailbox: imapDestinationFolder
atURL: [[self mailAccountFolder] imap4URL]];
if (!result || [result boolValue])
result = [client copyUids: uids toFolder: imapDestinationFolder];
if ([[result valueForKey: @"result"] boolValue])
result = nil;
else
result = [NSException exceptionWithHTTPStatus: 500 reason: @"Couldn't copy UIDs."];
result = [NSException exceptionWithHTTPStatus: 500
reason: @"Invalid destination."];
return result;
}
@@ -1061,9 +1075,8 @@ static NSString *defaultUserID = @"anyone";
{
thisAccount = [self mailAccountFolder];
mailAccount = [[user mailAccounts] objectAtIndex: 0];
url = [NSString stringWithFormat: @"%@/%@%@",
url = [NSString stringWithFormat: @"%@/0%@",
[self soURLToBaseContainerForUser: uid],
[mailAccount objectForKey: @"name"],
otherUsersPath];
}
else

View File

@@ -29,6 +29,7 @@
#import <SOGo/SOGoUser.h>
#import <SOGo/SOGoUserDefaults.h>
#import "SOGoMailAccount.h"
#import "SOGoMailObject+Draft.h"
#import "SOGoMailForward.h"
@@ -221,7 +222,7 @@
SOGoUserDefaults *ud;
ud = [[context activeUser] userDefaults];
signature = [ud mailSignature];
signature = [[sourceMail mailAccountFolder] signature];
if ([signature length])
mailSignature = [NSString stringWithFormat: @"-- \n%@", signature];
else

View File

@@ -23,6 +23,7 @@
#ifndef S_GO_SOOBJECTS_SOGO_DOMNODE_SOGO_H
#define S_GO_SOOBJECTS_SOGO_DOMNODE_SOGO_H
#import <DOM/DOMElement.h>
#import <DOM/DOMNode.h>
@class DOMElement;

View File

@@ -28,8 +28,15 @@
- (NSString *) jsonRepresentation
{
NSString *jsonRepresentation;
return [NSString stringWithFormat: @"%@", self];
jsonRepresentation = [NSString stringWithFormat: @"%@", self];
if ([jsonRepresentation isEqualToString: @"YES"])
jsonRepresentation = @"true";
else if ([jsonRepresentation isEqualToString: @"NO"])
jsonRepresentation = @"false";
return jsonRepresentation;
}
@end

View File

@@ -37,6 +37,8 @@
- (NSArray *) userSources;
- (BOOL) mailAuxiliaryUserAccountsEnabled;
- (NSString *) mailDomain;
- (NSString *) imapServer;
- (NSString *) imapAclStyle;
@@ -60,6 +62,7 @@
- (NSArray *) calendarDefaultRoles;
- (NSArray *) contactsDefaultRoles;
- (NSArray *) mailPollingIntervals;
- (BOOL) mailCheckAllUnseenCounts;
- (NSString *) calendarDefaultCategoryColor;

View File

@@ -98,6 +98,11 @@
return [self stringForKey: @"OCSFolderInfoURL"];
}
- (BOOL) mailAuxiliaryUserAccountsEnabled
{
return [self boolForKey: @"SOGoMailAuxiliaryUserAccountsEnabled"];
}
- (NSString *) mailDomain
{
return [self stringForKey: @"SOGoMailDomain"];
@@ -193,6 +198,11 @@
return [self arrayForKey: @"SOGoMailPollingIntervals"];
}
- (BOOL) mailCheckAllUnseenCounts
{
return [self boolForKey: @"SOGoMailCheckAllUnseenCounts"];
}
- (NSString *) smtpServer
{
return [self stringForKey: @"SOGoSMTPServer"];

View File

@@ -121,6 +121,10 @@
- (NSString *) davCollectionTag;
/* multiget helper */
- (WOResponse *) performMultigetInContext: (WOContext *) queryContext
inNamespace: (NSString *) namespace;
@end
#endif /* __SOGo_SOGoGCSFolder_H__ */

View File

@@ -1685,4 +1685,392 @@ static NSArray *childRecordFields = nil;
[_ms appendFormat:@" ocs=%@", [self ocsPath]];
}
/* tmp: multiget */
- (NSArray *) _fetchComponentsWithNames: (NSArray *) cNames
fields: (NSArray *) fields
{
NSArray *records;
NSString *sqlFilter;
NSMutableString *filterString;
EOQualifier *qualifier;
sqlFilter = [self aclSQLListingFilter];
if (sqlFilter)
{
filterString = [NSMutableString stringWithCapacity: 8192];
[filterString appendFormat: @"(c_name='%@')",
[cNames componentsJoinedByString: @"' OR c_name='"]];
if ([sqlFilter length] > 0)
[filterString appendFormat: @" AND (%@)", sqlFilter];
qualifier = [EOQualifier qualifierWithQualifierFormat: filterString];
records = [[self ocsFolder] fetchFields: fields
matchingQualifier: qualifier];
if (![records isNotNull])
{
[self errorWithFormat: @"(%s): fetch failed!", __PRETTY_FUNCTION__];
return nil;
}
}
else
records = [NSArray array];
return records;
}
#define maxQuerySize 2500
#define baseQuerySize 160
#define idQueryOverhead 13
- (NSArray *) _fetchComponentsMatchingObjectNames: (NSArray *) cNames
fields: (NSArray *) fields
{
NSMutableArray *components;
NSArray *records;
NSMutableArray *currentNames;
unsigned int count, max, currentSize, queryNameLength;
NSString *currentName;
// NSLog (@"fetching components matching names");
currentNames = [NSMutableArray array];
currentSize = baseQuerySize;
max = [cNames count];
components = [NSMutableArray arrayWithCapacity: max];
for (count = 0; count < max; count++)
{
currentName = [cNames objectAtIndex: count];
queryNameLength = idQueryOverhead + [currentName length];
if ((currentSize + queryNameLength)
> maxQuerySize)
{
records = [self _fetchComponentsWithNames: currentNames fields: fields];
[components addObjectsFromArray: records];
[currentNames removeAllObjects];
currentSize = baseQuerySize;
}
[currentNames addObject: currentName];
currentSize += queryNameLength;
}
records = [self _fetchComponentsWithNames: currentNames fields: fields];
[components addObjectsFromArray: records];
// NSLog (@"/fetching components matching names");
return components;
}
- (NSDictionary *) _deduceObjectNamesFromURLs: (NSArray *) urls
{
unsigned int count, max;
NSString *url, *componentURLPath, *cName, *baseURLString;
NSMutableDictionary *cNames;
NSURL *componentURL, *baseURL;
NSArray *urlComponents;
max = [urls count];
cNames = [NSMutableDictionary dictionaryWithCapacity: max];
baseURL = [self davURL];
baseURLString = [self davURLAsString];
for (count = 0; count < max; count++)
{
url = [NSString stringWithFormat: @"%@/%@",
[[urls objectAtIndex: count] stringByDeletingLastPathComponent],
[[[urls objectAtIndex: count] lastPathComponent] stringByEscapingURL]];
componentURL = [[NSURL URLWithString: url relativeToURL: baseURL]
standardizedURL];
componentURLPath = [componentURL absoluteString];
if ([componentURLPath rangeOfString: baseURLString].location
!= NSNotFound)
{
urlComponents = [componentURLPath componentsSeparatedByString: @"/"];
cName = [[urls objectAtIndex: count] lastPathComponent];
[cNames setObject: [urls objectAtIndex: count] forKey: cName];
}
}
return cNames;
}
- (NSDictionary *) _fetchComponentsMatchingURLs: (NSArray *) urls
fields: (NSArray *) fields
{
NSMutableDictionary *components;
NSDictionary *cnames, *record;
NSString *recordURL;
NSArray *records;
unsigned int count, max;
components = [NSMutableDictionary dictionary];
cnames = [self _deduceObjectNamesFromURLs: urls];
records = [self _fetchComponentsMatchingObjectNames: [cnames allKeys]
fields: fields];
max = [records count];
for (count = 0; count < max; count++)
{
record = [records objectAtIndex: count];
recordURL = [cnames objectForKey: [record objectForKey: @"c_name"]];
if (recordURL)
[components setObject: record forKey: recordURL];
}
return components;
}
#warning the two following methods should be replaced with the new dav rendering mechanism
- (NSString *) _nodeTagForProperty: (NSString *) property
{
[self subclassResponsibility: _cmd];
return nil;
}
- (NSString *) _nodeTag: (NSString *) property
{
static NSMutableDictionary *tags = nil;
NSString *nodeTag;
if (!tags)
tags = [NSMutableDictionary new];
nodeTag = [tags objectForKey: property];
if (!nodeTag)
{
nodeTag = [self _nodeTagForProperty: property];
[tags setObject: nodeTag forKey: property];
}
return nodeTag;
}
- (NSString **) _properties: (NSString **) properties
count: (unsigned int) propertiesCount
ofObject: (NSDictionary *) object
{
SOGoContentObject *sogoObject;
NSString **currentProperty;
NSString **values, **currentValue;
SEL methodSel;
// NSLog (@"_properties:ofObject:: %@", [NSDate date]);
values = NSZoneMalloc (NULL,
(propertiesCount + 1) * sizeof (NSString *));
*(values + propertiesCount) = nil;
//c = [self objectClassForComponentName: [object objectForKey: @"c_component"]];
sogoObject = [self createChildComponentWithRecord: object];
currentProperty = properties;
currentValue = values;
while (*currentProperty)
{
methodSel = SOGoSelectorForPropertyGetter (*currentProperty);
if (methodSel && [sogoObject respondsToSelector: methodSel])
*currentValue = [[sogoObject performSelector: methodSel]
stringByEscapingXMLString];
currentProperty++;
currentValue++;
}
// NSLog (@"/_properties:ofObject:: %@", [NSDate date]);
return values;
}
- (NSArray *) _propstats: (NSString **) properties
count: (unsigned int) propertiesCount
ofObject: (NSDictionary *) object
{
NSMutableArray *propstats, *properties200, *properties404, *propDict;
NSString **property, **values, **currentValue;
NSString *propertyValue, *nodeTag;
// NSLog (@"_propstats:ofObject:: %@", [NSDate date]);
propstats = [NSMutableArray array];
properties200 = [NSMutableArray array];
properties404 = [NSMutableArray array];
values = [self _properties: properties count: propertiesCount
ofObject: object];
currentValue = values;
property = properties;
while (*property)
{
nodeTag = [self _nodeTag: *property];
if (*currentValue)
{
propertyValue = [NSString stringWithFormat: @"<%@>%@</%@>",
nodeTag, *currentValue, nodeTag];
propDict = properties200;
}
else
{
propertyValue = [NSString stringWithFormat: @"<%@/>", nodeTag];
propDict = properties404;
}
[propDict addObject: propertyValue];
property++;
currentValue++;
}
free (values);
if ([properties200 count])
[propstats addObject: [NSDictionary dictionaryWithObjectsAndKeys:
properties200, @"properties",
@"HTTP/1.1 200 OK", @"status",
nil]];
if ([properties404 count])
[propstats addObject: [NSDictionary dictionaryWithObjectsAndKeys:
properties404, @"properties",
@"HTTP/1.1 404 Not Found", @"status",
nil]];
// NSLog (@"/_propstats:ofObject:: %@", [NSDate date]);
return propstats;
}
- (void) _appendPropstat: (NSDictionary *) propstat
toBuffer: (NSMutableString *) r
{
NSArray *properties;
unsigned int count, max;
[r appendString: @"<D:propstat><D:prop>"];
properties = [propstat objectForKey: @"properties"];
max = [properties count];
for (count = 0; count < max; count++)
[r appendString: [properties objectAtIndex: count]];
[r appendString: @"</D:prop><D:status>"];
[r appendString: [propstat objectForKey: @"status"]];
[r appendString: @"</D:status></D:propstat>"];
}
#warning We need to use the new DAV utilities here...
#warning this is baddddd because we return a single-valued dictionary containing \
a cname which may not event exist... the logic behind appendObject:... should be \
rethought, especially since we may start using SQL views
- (void) appendObject: (NSDictionary *) object
properties: (NSString **) properties
count: (unsigned int) propertiesCount
withBaseURL: (NSString *) baseURL
toBuffer: (NSMutableString *) r
{
NSArray *propstats;
unsigned int count, max;
[r appendFormat: @"<D:response><D:href>"];
[r appendString: baseURL];
[r appendString: [object objectForKey: @"c_name"]];
[r appendString: @"</D:href>"];
// NSLog (@"(appendPropstats...): %@", [NSDate date]);
propstats = [self _propstats: properties count: propertiesCount
ofObject: object];
max = [propstats count];
for (count = 0; count < max; count++)
[self _appendPropstat: [propstats objectAtIndex: count]
toBuffer: r];
// NSLog (@"/(appendPropstats...): %@", [NSDate date]);
[r appendString: @"</D:response>"];
}
- (void) appendMissingObjectRef: (NSString *) href
toBuffer: (NSMutableString *) r
{
[r appendString: @"<D:response><D:href>"];
[r appendString: href];
[r appendString: @"</D:href><D:status>HTTP/1.1 404 Not Found</D:status></D:response>"];
}
- (void) _appendComponentProperties: (NSDictionary *) properties
matchingURLs: (id <DOMNodeList>) refs
toResponse: (WOResponse *) response
{
NSObject <DOMElement> *element;
NSDictionary *currentComponent, *components;
NSString *currentURL, *baseURL, *currentField;
NSString **propertiesArray;
NSMutableArray *urls, *fields;
NSMutableString *buffer;
unsigned int count, max, propertiesCount;
NSEnumerator *addFields;
baseURL = [self davURLAsString];
#warning review this when fixing http://www.scalableogo.org/bugs/view.php?id=276
if (![baseURL hasSuffix: @"/"])
baseURL = [NSString stringWithFormat: @"%@/", baseURL];
urls = [NSMutableArray array];
max = [refs length];
for (count = 0; count < max; count++)
{
element = [refs objectAtIndex: count];
currentURL = [[element firstChild] nodeValue];
[urls addObject: currentURL];
}
propertiesArray = [[properties allKeys] asPointersOfObjects];
propertiesCount = [properties count];
fields = [NSMutableArray arrayWithObjects: @"c_name", @"c_component", nil];
addFields = [[properties allValues] objectEnumerator];
while ((currentField = [addFields nextObject]))
if ([currentField length])
[fields addObjectUniquely: currentField];
components = [self _fetchComponentsMatchingURLs: urls fields: fields];
max = [urls count];
// NSLog (@"adding properties with url");
buffer = [NSMutableString stringWithCapacity: max*512];
for (count = 0; count < max; count++)
{
currentComponent = [components objectForKey: [urls objectAtIndex: count]];
if (currentComponent)
[self appendObject: currentComponent
properties: propertiesArray
count: propertiesCount
withBaseURL: baseURL
toBuffer: buffer];
else
[self appendMissingObjectRef: currentURL
toBuffer: buffer];
}
[response appendContentString: buffer];
// NSLog (@"/adding properties with url");
NSZoneFree (NULL, propertiesArray);
}
- (WOResponse *) performMultigetInContext: (WOContext *) queryContext
inNamespace: (NSString *) namespace
{
WOResponse *r;
id <DOMDocument> document;
DOMElement *documentElement, *propElement;
r = [context response];
[r prepareDAVResponse];
[r appendContentString:
[NSString stringWithFormat: @"<D:multistatus xmlns:D=\"DAV:\""
@" xmlns:C=\"%@\">", namespace]];
document = [[queryContext request] contentAsDOMDocument];
documentElement = (DOMElement *) [document documentElement];
propElement = [documentElement firstElementWithTag: @"prop"
inNamespace: @"DAV:"];
[self _appendComponentProperties: [self parseDAVRequestedProperties: propElement]
matchingURLs: [documentElement getElementsByTagName: @"href"]
toResponse: r];
[r appendContentString:@"</D:multistatus>"];
return r;
}
@end /* SOGoFolder */

View File

@@ -40,7 +40,6 @@
@class NSMutableArray;
@class NSMutableDictionary;
@class NSString;
@class NSURL;
@class WOContext;
@@ -69,7 +68,7 @@
NSString *domainId;
NSString *language;
NSArray *allEmails;
NSArray *mailAccounts;
NSMutableArray *mailAccounts;
NSString *cn;
BOOL propagateCache;
}

View File

@@ -436,53 +436,145 @@
}
/* mail */
- (NSArray *) mailAccounts
- (BOOL) _migrateFolderWithPurpose: (NSString *) purpose
withName: (NSString *) folderName
{
NSMutableDictionary *mailAccount, *identity;
NSString *methodName;
SEL methodSel;
BOOL rc;
[self userDefaults];
methodName = [NSString stringWithFormat: @"set%@FolderName:", purpose];
methodSel = NSSelectorFromString (methodName);
if ([_defaults respondsToSelector: methodSel])
{
[_defaults performSelector: methodSel withObject: folderName];
rc = YES;
}
else
{
[self errorWithFormat: @"method '%@' not available with user defaults"
@" object, folder migration fails", methodName];
rc = NO;
}
return rc;
}
- (void) _migrateFolderSettings
{
NSMutableDictionary *mailSettings;
NSString *folderName, *key;
BOOL migrated;
NSString **purpose;
NSString *purposes[] = { @"Drafts", @"Sent", @"Trash", nil };
[self userSettings];
mailSettings = [_settings objectForKey: @"Mail"];
if (mailSettings)
{
migrated = NO;
purpose = purposes;
while (*purpose)
{
key = [NSString stringWithFormat: @"%@Folder", *purpose];
folderName = [mailSettings objectForKey: key];
if ([folderName length]
&& [self _migrateFolderWithPurpose: *purpose
withName: folderName])
{
migrated = YES;
[mailSettings removeObjectForKey: key];
folderName = nil;
}
purpose++;
}
if (migrated)
{
[_settings synchronize];
[self userDefaults];
[_defaults synchronize];
}
}
}
- (void) _appendSystemMailAccount
{
NSMutableDictionary *mailAccount, *identity, *mailboxes;
NSMutableArray *identities;
NSString *fullName, *imapLogin, *imapServer;
NSString *fullName, *imapLogin, *imapServer, *signature;
NSArray *mails;
unsigned int count, max;
mailAccount = [NSMutableDictionary new];
imapLogin = [[SOGoUserManager sharedUserManager]
getImapLoginForUID: login];
imapServer = [self _fetchFieldForUser: @"c_imaphostname"];
if (!imapServer)
imapServer = [[self domainDefaults] imapServer];
[mailAccount setObject: imapLogin forKey: @"userName"];
[mailAccount setObject: imapServer forKey: @"serverName"];
identities = [NSMutableArray new];
mails = [self allEmails];
[mailAccount setObject: [mails objectAtIndex: 0] forKey: @"name"];
max = [mails count];
if (max > 1)
max--;
for (count = 0; count < max; count++)
{
identity = [NSMutableDictionary new];
fullName = [self cn];
if (![fullName length])
fullName = login;
[identity setObject: fullName forKey: @"fullName"];
[identity setObject: [mails objectAtIndex: count] forKey: @"email"];
signature = [[self userDefaults] mailSignature];
if (signature)
[identity setObject: signature forKey: @"signature"];
[identities addObject: identity];
[identity release];
}
[[identities objectAtIndex: 0] setObject: [NSNumber numberWithBool: YES]
forKey: @"isDefault"];
[mailAccount setObject: identities forKey: @"identities"];
[identities release];
mailboxes = [NSMutableDictionary new];
[self userDefaults];
[self _migrateFolderSettings];
[mailboxes setObject: [_defaults draftsFolderName]
forKey: @"Drafts"];
[mailboxes setObject: [_defaults sentFolderName]
forKey: @"Sent"];
[mailboxes setObject: [_defaults trashFolderName]
forKey: @"Trash"];
[mailAccount setObject: mailboxes forKey: @"mailboxes"];
[mailboxes release];
[mailAccounts addObject: mailAccount];
[mailAccount release];
}
- (NSArray *) mailAccounts
{
NSArray *auxAccounts;
if (!mailAccounts)
{
imapLogin = [[SOGoUserManager sharedUserManager]
getImapLoginForUID: login];
imapServer = [self _fetchFieldForUser: @"c_imaphostname"];
if (!imapServer)
imapServer = [[self domainDefaults] imapServer];
mailAccount = [NSMutableDictionary new];
[mailAccount setObject: imapLogin forKey: @"userName"];
[mailAccount setObject: imapServer forKey: @"serverName"];
identities = [NSMutableArray new];
mails = [self allEmails];
[mailAccount setObject: [mails objectAtIndex: 0]
forKey: @"name"];
max = [mails count];
if (max > 1)
max--;
for (count = 0; count < max; count++)
mailAccounts = [NSMutableArray new];
[self _appendSystemMailAccount];
if ([[self domainDefaults] mailAuxiliaryUserAccountsEnabled])
{
identity = [NSMutableDictionary new];
fullName = [self cn];
if (![fullName length])
fullName = login;
[identity setObject: fullName forKey: @"fullName"];
[identity setObject: [mails objectAtIndex: count] forKey: @"email"];
[identities addObject: identity];
[identity release];
auxAccounts = [[self userDefaults] auxiliaryMailAccounts];
if (auxAccounts)
[mailAccounts addObjectsFromArray: auxAccounts];
}
[[identities objectAtIndex: 0] setObject: [NSNumber numberWithBool: YES]
forKey: @"isDefault"];
[mailAccount setObject: identities forKey: @"identities"];
[identities release];
mailAccounts = [NSArray arrayWithObject: mailAccount];
[mailAccounts retain];
[mailAccount release];
}
return mailAccounts;
@@ -505,60 +597,6 @@
return mailAccount;
}
/*
@interface SOGoMailIdentity : NSObject
{
NSString *name;
NSString *email;
NSString *replyTo;
NSString *organization;
NSString *signature;
NSString *vCard;
NSString *sentFolderName;
NSString *sentBCC;
NSString *draftsFolderName;
NSString *templatesFolderName;
struct
{
int composeHTML:1;
int reserved:31;
} idFlags;
}
- (void) setName: (NSString *) _value;
- (NSString *) name;
- (void) setEmail: (NSString *) _value;
- (NSString *) email;
- (void) setReplyTo: (NSString *) _value;
- (NSString *) replyTo;
- (void) setOrganization: (NSString *) _value;
- (NSString *) organization;
- (void) setSignature: (NSString *) _value;
- (NSString *) signature;
- (BOOL) hasSignature;
- (void) setVCard: (NSString *) _value;
- (NSString *) vCard;
- (BOOL) hasVCard;
- (void) setSentFolderName: (NSString *) _value;
- (NSString *) sentFolderName;
- (void) setSentBCC: (NSString *) _value;
- (NSString *) sentBCC;
- (void) setDraftsFolderName: (NSString *) _value;
- (NSString *) draftsFolderName;
- (void) setTemplatesFolderName: (NSString *) _value;
- (NSString *) templatesFolderName;
@end */
- (NSArray *) allIdentities
{
NSArray *identities;
@@ -603,28 +641,6 @@
ignoringRights: YES];
}
// - (id) schedulingCalendarInContext: (id) _ctx
// {
// /* Note: watch out for cyclic references */
// id folder;
// folder = [(WOContext *)_ctx objectForKey:@"ActiveUserCalendar"];
// if (folder != nil)
// return [folder isNotNull] ? folder : nil;
// folder = [self homeFolderInContext:_ctx];
// if ([folder isKindOfClass:[NSException class]])
// return folder;
// folder = [folder lookupName:@"Calendar" inContext:_ctx acquire:NO];
// if ([folder isKindOfClass:[NSException class]])
// return folder;
// [(WOContext *)_ctx setObject:folder ? folder : [NSNull null]
// forKey:@"ActiveUserCalendar"];
// return folder;
// }
- (NSArray *) rolesForObject: (NSObject *) object
inContext: (WOContext *) context
{

View File

@@ -122,6 +122,9 @@ extern NSString *SOGoWeekStartFirstFullWeek;
- (void) setMailUseOutlookStyleReplies: (BOOL) newValue;
- (BOOL) mailUseOutlookStyleReplies;
- (void) setAuxiliaryMailAccounts: (NSArray *) newAccounts;
- (NSArray *) auxiliaryMailAccounts;
- (void) setCalendarCategories: (NSArray *) newValues;
- (NSArray *) calendarCategories;

View File

@@ -511,6 +511,16 @@ NSString *SOGoWeekStartFirstFullWeek = @"FirstFullWeek";
return [self boolForKey: @"SOGoMailUseOutlookStyleReplies"];
}
- (void) setAuxiliaryMailAccounts: (NSArray *) newAccounts
{
[self setObject: newAccounts forKey: @"AuxiliaryMailAccounts"];
}
- (NSArray *) auxiliaryMailAccounts
{
return [self arrayForKey: @"AuxiliaryMailAccounts"];
}
- (void) setCalendarCategories: (NSArray *) newValues
{
[self setObject: newValues forKey: @"SOGoCalendarCategories"];

View File

@@ -25,7 +25,7 @@
#import <Foundation/NSObject.h>
#include "SOGoSource.h"
#import "SOGoSource.h"
@class NSArray;
@class NSDictionary;

View File

@@ -20,6 +20,12 @@
* Boston, MA 02111-1307, USA.
*/
#define _XOPEN_SOURCE 1
#include <unistd.h>
#include <openssl/evp.h>
#include <openssl/md5.h>
#import <Foundation/NSArray.h>
#import <Foundation/NSObject.h>
#import <Foundation/NSDictionary.h>
@@ -35,14 +41,9 @@
#import <GDLAccess/EOAdaptorContext.h>
#import <GDLAccess/EOAttribute.h>
#include <unistd.h>
#import "SOGoConstants.h"
#include <openssl/evp.h>
#include <openssl/md5.h>
#include "SQLSource.h"
#include "SOGoConstants.h"
#import "SQLSource.h"
/**
* The view MUST contain the following columns:

View File

@@ -34,6 +34,7 @@
- (BOOL) isIPhone;
- (BOOL) isICal;
- (BOOL) isICal4;
- (BOOL) isAddressBookApp;
@end

View File

@@ -137,4 +137,14 @@
return [self isAppleDAVWithSubstring: @"iCal/4."];
}
- (BOOL) isAddressBookApp
{
WEClientCapabilities *cc;
cc = [self clientCapabilities];
return ([[cc userAgent] rangeOfString: @"CFNetwork"].location != NSNotFound
&& [[cc userAgent] rangeOfString: @"Darwin"].location != NSNotFound);
}
@end

View File

@@ -473,7 +473,7 @@ class CalDAVITIPDelegationTest(unittest.TestCase):
att_inv = self._getEvent(self.client,
"%stest-delegation.ics" % self.attendee1_calendar, 404)
del_inv = self._getEvent(self.client,
"%stest-delegation.ics" % self.attendee1_calendar, 404)
"%stest-delegation.ics" % self.attendee1_delegate_calendar, 404)
if __name__ == "__main__":
sogotests.runTests()

View File

@@ -65,6 +65,12 @@
"delegate is a participant" = "The delegate is already a participant.";
"delegate is a group" = "The specified address corresponds to a group. You can only delegate to a unique person.";
/* common buttons */
"OK" = "OK";
"Cancel" = "Cancel";
"Yes" = "Yes";
"No" = "No";
/* alarms */
"Reminder:" = "Lembrete:";
"Start:" = "Inicio:";

View File

@@ -65,6 +65,12 @@
"delegate is a participant" = "Delegovaný je již účastníkem.";
"delegate is a group" = "Určená adresa odpovídá skupině. Delegovat můžete pouze na osobu.";
/* common buttons */
"OK" = "OK";
"Cancel" = "Cancel";
"Yes" = "Yes";
"No" = "No";
/* alarms */
"Reminder:" = "Upomínka:";
"Start:" = "Začátek:";

View File

@@ -65,6 +65,12 @@
"delegate is a participant" = "The delegate is already a participant.";
"delegate is a group" = "The specified address corresponds to a group. You can only delegate to a unique person.";
/* common buttons */
"OK" = "OK";
"Cancel" = "Cancel";
"Yes" = "Ja";
"No" = "Nee";
/* alarms */
"Reminder:" = "Alarm:";
"Start:" = "Begin:";

View File

@@ -65,6 +65,12 @@
"delegate is a participant" = "The delegate is already a participant.";
"delegate is a group" = "The specified address corresponds to a group. You can only delegate to a unique person.";
/* common buttons */
"OK" = "OK";
"Cancel" = "Cancel";
"Yes" = "Yes";
"No" = "No";
/* alarms */
"Reminder:" = "Reminder:";
"Start:" = "Start:";

View File

@@ -65,6 +65,12 @@
"delegate is a participant" = "Le délégué est déjà un participant.";
"delegate is a group" = "L'adresse spécifiée correspond à un groupe. Vous ne pouvez déléguer qu'à une personne.";
/* common buttons */
"OK" = "OK";
"Cancel" = "Annuler";
"Yes" = "Oui";
"No" = "Non";
/* alarms */
"Reminder:" = "Rappel :";
"Start:" = "Début :";

View File

@@ -65,6 +65,12 @@
"delegate is a participant" = "Der Vertreter ist bereits ein Teilnehmer.";
"delegate is a group" = "Die angegebene Adresse gehört einer Gruppe. Es kann nur zu einer einzelnen Person delegiert werden.";
/* common buttons */
"OK" = "OK";
"Cancel" = "Cancel";
"Yes" = "Ja";
"No" = "Nein";
/* alarms */
"Reminder:" = "Alarm:";
"Start:" = "Beginn:";

View File

@@ -65,6 +65,12 @@
"delegate is a participant" = "The delegate is already a participant.";
"delegate is a group" = "The specified address corresponds to a group. You can only delegate to a unique person.";
/* common buttons */
"OK" = "OK";
"Cancel" = "Cancel";
"Yes" = "Yes";
"No" = "No";
/* alarms */
"Reminder:" = "Emlékeztető:";
"Start:" = "Kezdés:";

View File

@@ -65,6 +65,12 @@
"delegate is a participant" = "The delegate is already a participant.";
"delegate is a group" = "The specified address corresponds to a group. You can only delegate to a unique person.";
/* common buttons */
"OK" = "OK";
"Cancel" = "Cancel";
"Yes" = "Si";
"No" = "No";
/* alarms */
"Reminder:" = "Promemoria:";
"Start:" = "Inizio:";

View File

@@ -65,6 +65,12 @@
"delegate is a participant" = "The delegate is already a participant.";
"delegate is a group" = "The specified address corresponds to a group. You can only delegate to a unique person.";
/* common buttons */
"OK" = "OK";
"Cancel" = "Cancel";
"Yes" = "Yes";
"No" = "No";
/* alarms */
"Reminder:" = "Напоминание:";
"Start:" = "Начало:";

View File

@@ -65,6 +65,12 @@
"delegate is a participant" = "The delegate is already a participant.";
"delegate is a group" = "The specified address corresponds to a group. You can only delegate to a unique person.";
/* common buttons */
"OK" = "OK";
"Cancel" = "Cancel";
"Yes" = "Yes";
"No" = "No";
/* alarms */
"Reminder:" = "Recordatorio:";
"Start:" = "Desde:";

View File

@@ -65,6 +65,12 @@
"delegate is a participant" = "Personen du delegerar till är redan en deltagare.";
"delegate is a group" = "Adressen du skrivit går till en grupp. Du kan bara delegera till en unik person.";
/* common buttons */
"OK" = "OK";
"Cancel" = "Cancel";
"Yes" = "Yes";
"No" = "No";
/* alarms */
"Reminder:" = "Påminnelse:";
"Start:" = "Start:";

View File

@@ -40,6 +40,8 @@
NSString *toolbar;
id item;
BOOL isPopup;
NSArray *udKeys;
NSArray *usKeys;
NSMutableArray *additionalCSSFiles;
NSMutableArray *additionalJSFiles;
}

View File

@@ -21,10 +21,12 @@
*/
#import <Foundation/NSEnumerator.h>
#import <Foundation/NSNull.h>
#import <Foundation/NSString.h>
#import <NGObjWeb/WOResourceManager.h>
#import <SOGo/NSArray+Utilities.h>
#import <SOGo/NSDictionary+Utilities.h>
#import <SOGo/SOGoUser.h>
#import <SOGo/SOGoUserDefaults.h>
@@ -44,6 +46,8 @@
item = nil;
title = nil;
toolbar = nil;
udKeys = nil;
usKeys = nil;
additionalJSFiles = nil;
additionalCSSFiles = nil;
}
@@ -56,6 +60,8 @@
[item release];
[title release];
[toolbar release];
[udKeys release];
[usKeys release];
[additionalJSFiles release];
[additionalCSSFiles release];
[super dealloc];
@@ -462,30 +468,73 @@
return [ud language];
}
- (NSString *) userSettings
/* UserDefaults, UserSettings */
- (NSString *) _dictionaryWithKeys: (NSArray *) keys
fromSource: (SOGoDefaultsSource *) source
{
SOGoUserSettings *us;
NSString *jsonResult;
NSString *key;
int count, max;
NSMutableDictionary *dict;
NSNull *nsNull;
id value;
us = [[context activeUser] userSettings];
jsonResult = [[us source] jsonRepresentation];
if (!jsonResult)
jsonResult = @"{}";
nsNull = [NSNull null];
return jsonResult;
max = [keys count];
dict = [NSMutableDictionary dictionaryWithCapacity: max];
for (count = 0; count < max; count++)
{
key = [keys objectAtIndex: count];
value = [source objectForKey: key];
if (!value)
value = nsNull;
[dict setObject: value forKey: key];
}
return [dict jsonRepresentation];
}
- (void) setUserDefaultsKeys: (NSString *) newKeys
{
[udKeys release];
udKeys = [[newKeys componentsSeparatedByString: @","] trimmedComponents];
[udKeys retain];
}
- (BOOL) hasUserDefaultsKeys
{
return ([udKeys count] > 0);
}
- (NSString *) userDefaults
{
SOGoUserDefaults *ud;
NSString *jsonResult;
ud = [[context activeUser] userDefaults];
jsonResult = [[ud source] jsonRepresentation];
if (!jsonResult)
jsonResult = @"{}";
return jsonResult;
return [self _dictionaryWithKeys: udKeys fromSource: ud];
}
- (void) setUserSettingsKeys: (NSString *) newKeys
{
[usKeys release];
usKeys = [[newKeys componentsSeparatedByString: @","] trimmedComponents];
[usKeys retain];
}
- (BOOL) hasUserSettingsKeys
{
return ([usKeys count] > 0);
}
- (NSString *) userSettings
{
SOGoUserSettings *us;
us = [[context activeUser] userSettings];
return [self _dictionaryWithKeys: usKeys fromSource: us];
}
/* browser/os identification */

View File

@@ -61,6 +61,12 @@
"delegate is a participant" = "Цього учасника вже запрошено.";
"delegate is a group" = "Зазначена адреса є групою. Ви можете запросити лише окрему особу.";
/* common buttons */
"OK" = "OK";
"Cancel" = "Cancel";
"Yes" = "Yes";
"No" = "No";
/* alarms */
"Reminder:" = "Нагадування:";
"Start:" = "Початок:";

View File

@@ -65,6 +65,12 @@
"delegate is a participant" = "The delegate is already a participant.";
"delegate is a group" = "The specified address corresponds to a group. You can only delegate to a unique person.";
/* common buttons */
"OK" = "OK";
"Cancel" = "Cancel";
"Yes" = "Yes";
"No" = "No";
/* alarms */
"Reminder:" = "Atgoffa:";
"Start:" = "Dechrau:";

View File

@@ -35,7 +35,6 @@
NSString *trashFolderName;
}
- (WOResponse *) statusFoldersAction;
- (WOResponse *) listMailboxesAction;
@end

View File

@@ -31,8 +31,6 @@
#import <NGImap4/NGImap4Connection.h>
#import <NGImap4/NGImap4Client.h>
#import <EOControl/EOQualifier.h>
#import <Mailer/SOGoMailAccount.h>
#import <Mailer/SOGoDraftObject.h>
#import <Mailer/SOGoDraftsFolder.h>
@@ -41,7 +39,6 @@
#import <SOGo/NSString+Utilities.h>
#import <SOGo/SOGoDomainDefaults.h>
#import <SOGo/SOGoUser.h>
#import <SOGo/SOGoUserDefaults.h>
#import "../Common/WODirectAction+SOGo.h"
@@ -125,50 +122,6 @@
return folders;
}
- (NSDictionary *) _statusFolders
{
EOQualifier *searchQualifier;
NSArray *searchResult;
NSDictionary *imapResult;
NGImap4Client *client;
NSNumber *unseen;
SOGoMailFolder *inbox;
SOGoMailAccount *co;
co = [self clientObject];
inbox = [co inboxFolderInContext: context];
client = [[inbox imap4Connection] client];
unseen = nil;
if ([client select: [inbox relativeImap4Name]])
{
searchQualifier = [EOQualifier qualifierWithQualifierFormat: @"flags = %@ AND not flags = %@", @"unseen", @"deleted"];
imapResult = [client searchWithQualifier: searchQualifier];
searchResult = [[imapResult objectForKey: @"RawResponse"] objectForKey: @"search"];
unseen = [NSNumber numberWithInt: [searchResult count]];
}
if (!unseen)
unseen = [NSNumber numberWithInt: 0];
return [NSDictionary dictionaryWithObjectsAndKeys: unseen, @"unseen", nil];
}
- (WOResponse *) statusFoldersAction
{
WOResponse *response;
NSDictionary *data;
response = [self responseWithStatus: 200];
data = [self _statusFolders];
[response setHeader: @"text/plain; charset=utf-8"
forKey: @"content-type"];
[response appendContentString: [data jsonRepresentation]];
return response;
}
- (WOResponse *) listMailboxesAction
{
SOGoMailAccount *co;
@@ -215,13 +168,12 @@
// The parameter order is important here, as if the server doesn't support
// quota, inboxQuota will be nil and it'll terminate the list of objects/keys.
data = [NSDictionary dictionaryWithObjectsAndKeys: folders, @"mailboxes",
[self _statusFolders], @"status",
inboxQuota, @"quotas",
nil];
response = [self responseWithStatus: 200];
[response setHeader: @"text/plain; charset=utf-8"
response = [self responseWithStatus: 200
andString: [data jsonRepresentation]];
[response setHeader: @"application/json"
forKey: @"content-type"];
[response appendContentString: [data jsonRepresentation]];
return response;
}
@@ -232,7 +184,6 @@
{
SOGoDraftsFolder *drafts;
SOGoDraftObject *newDraftMessage;
SOGoUserDefaults *ud;
NSString *urlBase, *url, *value, *signature;
NSArray *mailTo;
NSMutableDictionary *headers;
@@ -262,8 +213,7 @@
if (save)
[newDraftMessage setHeaders: headers];
ud = [[context activeUser] userDefaults];
signature = [ud mailSignature];
signature = [[self clientObject] signature];
if ([signature length])
{
[newDraftMessage

View File

@@ -347,19 +347,21 @@ static NSArray *infoKeys = nil;
- (NSArray *) fromEMails
{
NSArray *allIdentities;
NSArray *identities;
int count, max;
NSString *email;
SOGoMailAccount *account;
if (!fromEMails)
{
allIdentities = [[context activeUser] allIdentities];
fromEMails = [NSMutableArray new];
max = [allIdentities count];
account = [[self clientObject] mailAccountFolder];
identities = [account identities];
max = [identities count];
fromEMails = [[NSMutableArray alloc] initWithCapacity: max];
for (count = 0; count < max; count++)
{
email
= [self _emailFromIdentity: [allIdentities objectAtIndex: count]];
= [self _emailFromIdentity: [identities objectAtIndex: count]];
[fromEMails addObjectUniquely: email];
}
}

View File

@@ -24,6 +24,7 @@
#import <Foundation/NSDictionary.h>
#import <Foundation/NSEnumerator.h>
#import <Foundation/NSURL.h>
#import <Foundation/NSValue.h>
#import <NGObjWeb/WOContext.h>
#import <NGObjWeb/WOContext+SoObjects.h>
@@ -31,12 +32,15 @@
#import <NGObjWeb/WORequest.h>
#import <NGImap4/NGImap4Connection.h>
#import <NGImap4/NGImap4Client.h>
#import <EOControl/EOQualifier.h>
#import <Mailer/SOGoMailFolder.h>
#import <Mailer/SOGoTrashFolder.h>
#import <Mailer/SOGoMailAccount.h>
#import <Mailer/SOGoMailFolder.h>
#import <Mailer/SOGoMailObject.h>
#import <Mailer/SOGoTrashFolder.h>
#import <SOGo/NSObject+Utilities.h>
#import <SOGo/SOGoDomainDefaults.h>
#import <SOGo/SOGoUser.h>
#import <SOGo/SOGoUserDefaults.h>
#import <SOGo/SOGoUserSettings.h>
@@ -351,26 +355,94 @@
return response;
}
- (void) _setFolderPurposeOnMainAccount: (NSString *) purpose
inUserDefaults: (SOGoUserDefaults *) ud
to: (NSString *) value
{
NSString *selName;
SEL setter;
selName = [NSString stringWithFormat: @"set%@FolderName:", purpose];
setter = NSSelectorFromString (selName);
[ud performSelector: setter withObject: value];
}
- (WOResponse *) _setFolderPurpose: (NSString *) purpose
onAuxAccount: (int) accountIdx
inUserDefaults: (SOGoUserDefaults *) ud
to: (NSString *) value
{
NSArray *accounts;
int realIdx;
NSMutableDictionary *account, *mailboxes;
WOResponse *response;
if (accountIdx > 0)
{
realIdx = accountIdx - 1;
accounts = [ud auxiliaryMailAccounts];
if ([accounts count] > realIdx)
{
account = [accounts objectAtIndex: realIdx];
mailboxes = [account objectForKey: @"mailboxes"];
if (!mailboxes)
{
mailboxes = [NSMutableDictionary new];
[account setObject: mailboxes forKey: @"mailboxes"];
[mailboxes release];
}
[mailboxes setObject: value forKey: purpose];
[ud setAuxiliaryMailAccounts: accounts];
response = [self responseWith204];
}
else
response
= [self responseWithStatus: 500
andString: @"You reached an impossible end."];
}
else
response
= [self responseWithStatus: 500
andString: @"You reached an impossible end."];
return response;
}
- (WOResponse *) _setFolderPurpose: (NSString *) purpose
{
SOGoMailFolder *co;
WOResponse *response;
SOGoUserSettings *us;
NSMutableDictionary *mailSettings;
SOGoUser *owner;
SOGoUserDefaults *ud;
NSString *accountIdx, *traversal;
co = [self clientObject];
if ([NSStringFromClass ([co class]) isEqualToString: @"SOGoMailFolder"])
if ([co isKindOfClass: [SOGoMailFolder class]])
{
us = [[context activeUser] userSettings];
mailSettings = [us objectForKey: @"Mail"];
if (!mailSettings)
mailSettings = [NSMutableDictionary dictionary];
[us setObject: mailSettings forKey: @"Mail"];
[mailSettings setObject: [co traversalFromMailAccount]
forKey: [NSString stringWithFormat: @"%@Folder",
purpose]];
[us synchronize];
response = [self responseWith204];
accountIdx = [[co mailAccountFolder] nameInContainer];
owner = [SOGoUser userWithLogin: [co ownerInContext: nil]];
ud = [owner userDefaults];
traversal = [co traversalFromMailAccount];
if ([accountIdx isEqualToString: @"0"])
{
/* default account: we directly set the corresponding pref in the ud
*/
[self _setFolderPurposeOnMainAccount: purpose
inUserDefaults: ud
to: traversal];
response = [self responseWith204];
}
else if ([[owner domainDefaults] mailAuxiliaryUserAccountsEnabled])
response = [self _setFolderPurpose: purpose
onAuxAccount: [accountIdx intValue]
inUserDefaults: ud
to: traversal];
else
response
= [self responseWithStatus: 500
andString: @"You reached an impossible end."];
if ([response status] == 204)
[ud synchronize];
}
else
{
@@ -515,4 +587,51 @@
return response;
}
- (NSDictionary *) _unseenCount
{
EOQualifier *searchQualifier;
NSArray *searchResult;
NSDictionary *imapResult;
NGImap4Connection *connection;
NGImap4Client *client;
int unseen;
SOGoMailFolder *folder;
folder = [self clientObject];
connection = [folder imap4Connection];
client = [connection client];
if ([connection selectFolder: [folder imap4URL]])
{
searchQualifier
= [EOQualifier qualifierWithQualifierFormat: @"flags = %@ AND not flags = %@",
@"unseen", @"deleted"];
imapResult = [client searchWithQualifier: searchQualifier];
searchResult = [[imapResult objectForKey: @"RawResponse"] objectForKey: @"search"];
unseen = [searchResult count];
}
else
unseen = 0;
return [NSDictionary
dictionaryWithObject: [NSNumber numberWithInt: unseen]
forKey: @"unseen"];
}
- (WOResponse *) unseenCountAction
{
WOResponse *response;
NSDictionary *data;
response = [self responseWithStatus: 200];
data = [self _unseenCount];
[response setHeader: @"text/plain; charset=utf-8"
forKey: @"content-type"];
[response appendContentString: [data jsonRepresentation]];
return response;
}
@end

View File

@@ -103,33 +103,12 @@
- (NSString *) mailAccounts
{
SOGoMailAccounts *accounts;
NSDictionary *accountKeys;
NSArray *keys, *entry;
NSMutableArray *values;
NSString *key;
int i, max;
NSArray *accounts, *names;
accounts = [self clientObject];
accountKeys = [accounts accountKeys];
keys = [accountKeys allKeys];
values = [NSMutableArray array];
accounts = [[self clientObject] mailAccounts];
names = [accounts objectsForKey: @"name" notFoundMarker: nil];
max = [keys count];
for (i = 0; i < max; i++)
{
key = [keys objectAtIndex: i];
entry = [NSArray arrayWithObjects: key, [accountKeys objectForKey: key], nil];
[values addObject: entry];
}
return [values jsonRepresentation];
}
- (NSString *) defaultColumnsOrder
{
return [[[self columnsDisplayOrder] objectsForKey: @"value"
notFoundMarker: @""] jsonRepresentation];
return [names jsonRepresentation];
}
- (NSString *) pageFormURL
@@ -182,21 +161,18 @@
SOGoMailAccounts *accounts;
SOGoMailAccount *account;
SOGoMailFolder *inbox;
NSString *firstAccount;
NSDictionary *data;
SOGoUser *activeUser;
UIxMailListActions *actions;
[self _setupContext];
#warning this code is dirty: we should not invoke UIxMailListActions from here!
actions = [[[UIxMailListActions new] initWithRequest: [context request]] autorelease];
activeUser = [context activeUser];
accounts = [self clientObject];
firstAccount = [[[accounts accountKeys] allKeys]
objectAtIndex: 0];
account = [accounts lookupName: firstAccount inContext: context acquire: NO];
account = [accounts lookupName: @"0" inContext: context acquire: NO];
inbox = [account inboxFolderInContext: context];
data = [actions getUIDsAndHeadersInFolder: inbox];
@@ -207,8 +183,8 @@
- (id <WOActionResults>) composeAction
{
id contact;
NSArray *accounts, *contactsId, *cards;
NSString *firstAccount, *firstEscapedAccount, *newLocation, *parameters, *folderId, *uid, *formattedMail;
NSArray *contactsId, *cards;
NSString *newLocation, *parameters, *folderId, *uid, *formattedMail;
NSEnumerator *uids;
NSMutableArray *addresses;
NGVCard *card;
@@ -222,11 +198,6 @@
parameters = nil;
co = [self clientObject];
// We use the first mail account
accounts = [[context activeUser] mailAccounts];
firstAccount = [[accounts objectsForKey: @"name" notFoundMarker: nil]
objectAtIndex: 0];
firstEscapedAccount = [firstAccount asCSSIdentifier];
request = [context request];
if ((folderId = [request formValueForKey: @"folder"]) &&
@@ -290,9 +261,8 @@
// No parameter passed; simply open the compose window
parameters = @"?mailto=";
newLocation = [NSString stringWithFormat: @"%@/%@/compose%@",
newLocation = [NSString stringWithFormat: @"%@/0/compose%@",
[co baseURLInContext: context],
firstEscapedAccount,
parameters];
return [self redirectToLocation: newLocation];
@@ -643,5 +613,14 @@
return [self labelForKey: [currentColumn objectForKey: @"value"]];
}
- (NSString *) getUnseenCountForAllFolders
{
SOGoDomainDefaults *dd;
dd = [[context activeUser] domainDefaults];
return ([dd mailCheckAllUnseenCounts] ? @"true" : @"false");
}
@end /* UIxMailMainFrame */

View File

@@ -171,6 +171,11 @@
protectedBy = "ReadAcls";
pageName = "UIxMailUserRightsEditor";
};
unseenCount = {
protectedBy = "View";
actionClass = "UIxMailFolderActions";
actionName = "unseenCount";
};
saveUserRights = {
protectedBy = "Change Permissions";
pageName = "UIxMailUserRightsEditor";
@@ -383,11 +388,6 @@
actionClass = "UIxMailFolderActions";
actionName = "createFolder";
};
statusFolders = {
protectedBy = "View";
actionClass = "UIxMailAccountActions";
actionName = "statusFolders";
};
};
};

View File

@@ -6,10 +6,9 @@
"General" = "Geral";
"Calendar Options" = "Calendário";
"Mail Options" = "Correio";
"Signature" = "Assinatura";
"IMAP Accounts" = "IMAP Accounts";
"Vacation" = "Vacation";
"Forward" = "Forward";
"Identities" = "Identidade";
"Password" = "Senha";
"Categories" = "Categories";
"Name" = "Name";
@@ -119,9 +118,6 @@
"messageforward_inline" = "No corpo da mensagem";
"messageforward_attached" = "Como anexo";
/* Identities */
"Default identity:" = "Identidade Padrão:";
"Manage identities..." = "Gerenciar Identidades...";
"replyplacement_above" = "Começar minha resposta acima das citações";
"replyplacement_below" = "Começar minha resposta abaixo das citações";
"And place my signature" = "E colocar minha assinatura";
@@ -131,7 +127,21 @@
"composemessagestype_html" = "HTML";
"composemessagestype_text" = "Plain text";
"composeMessageChanged" = "Changing your messages composition type requires to save your preferences immediately. Continue?";
/* IMAP Accounts */
"New Mail Account" = "New Mail Account";
"Server Name:" = "Server Name:";
"Port:" = "Port:";
"User Name:" = "User Name:";
"Password:" = "Senha:";
"Full Name:" = "Full Name:";
"Email:" = "Email:";
"Signature:" = "Assinatura:";
"(Click to create)" = "(Click to create)";
"Signature" = "Assinatura";
"Please enter your signature below:" = "Please enter your signature below:";
/* Additional Parameters */
"Additional Parameters" = "Parâmetros Adicionais";

View File

@@ -6,10 +6,9 @@
"General" = "Obecný";
"Calendar Options" = "Možnosti kalendáře";
"Mail Options" = "Možnosti pošty";
"Signature" = "Podpis";
"IMAP Accounts" = "IMAP Accounts";
"Vacation" = "Nepřítomnost";
"Forward" = "Přeposílání";
"Identities" = "Identity";
"Password" = "Heslo";
"Categories" = "Kategorie";
"Name" = "Jméno";
@@ -119,9 +118,6 @@
"messageforward_inline" = "Do řady";
"messageforward_attached" = "Jako přílohu";
/* Identities */
"Default identity:" = "Výchozí identita:";
"Manage identities..." = "Spravovat identity...";
"replyplacement_above" = "Začít mojí odpověď nad citací";
"replyplacement_below" = "Začít mojí odpověď pod citací";
"And place my signature" = "A umístit můj podpis";
@@ -131,7 +127,21 @@
"composemessagestype_html" = "HTML";
"composemessagestype_text" = "Prostý text";
"composeMessageChanged" = "Změna formátu vytváření zprávy vyžaduje okamžité uložení předvoleb. Pokračovat?";
/* IMAP Accounts */
"New Mail Account" = "New Mail Account";
"Server Name:" = "Server Name:";
"Port:" = "Port:";
"User Name:" = "User Name:";
"Password:" = "Heslo:";
"Full Name:" = "Full Name:";
"Email:" = "Email:";
"Signature:" = "Podpis:";
"(Click to create)" = "(Click to create)";
"Signature" = "Podpis";
"Please enter your signature below:" = "Please enter your signature below:";
/* Additional Parameters */
"Additional Parameters" = "Dodatečné parametry";

View File

@@ -6,10 +6,9 @@
"General" = "Algemeen";
"Calendar Options" = "Agenda";
"Mail Options" = "E-mail";
"Signature" = "Ondertekening";
"IMAP Accounts" = "IMAP Accounts";
"Vacation" = "Vacation";
"Forward" = "Forward";
"Identities" = "Identiteit";
"Password" = "Wachtwoord";
"Categories" = "Categories";
"Name" = "Name";
@@ -119,9 +118,6 @@
"messageforward_inline" = "In het bericht";
"messageforward_attached" = "Als bijlage";
/* Identities */
"Default identity:" = "Standaardidentiteit:";
"Manage identities..." = "Identiteiten beheren...";
"replyplacement_above" = "reactie boven orgineletekst";
"replyplacement_below" = "reactie onder orgineletekst";
"And place my signature" = "Ondertekening plaatsen";
@@ -131,7 +127,21 @@
"composemessagestype_html" = "HTML";
"composemessagestype_text" = "Plain text";
"composeMessageChanged" = "Changing your messages composition type requires to save your preferences immediately. Continue?";
/* IMAP Accounts */
"New Mail Account" = "New Mail Account";
"Server Name:" = "Server Name:";
"Port:" = "Port:";
"User Name:" = "User Name:";
"Password:" = "Wachtwoord:";
"Full Name:" = "Full Name:";
"Email:" = "Email:";
"Signature:" = "Ondertekening:";
"(Click to create)" = "(Click to create)";
"Signature" = "Ondertekening";
"Please enter your signature below:" = "Please enter your signature below:";
/* Additional Parameters */
"Additional Parameters" = "Extra Parameters";

View File

@@ -6,10 +6,9 @@
"General" = "General";
"Calendar Options" = "Calendar Options";
"Mail Options" = "Mail Options";
"Signature" = "Signature";
"IMAP Accounts" = "IMAP Accounts";
"Vacation" = "Vacation";
"Forward" = "Forward";
"Identities" = "Identities";
"Password" = "Password";
"Categories" = "Categories";
"Name" = "Name";
@@ -119,9 +118,6 @@
"messageforward_inline" = "Inline";
"messageforward_attached" = "As Attachment";
/* Identities */
"Default identity:" = "Default identity:";
"Manage identities..." = "Manage identities...";
"replyplacement_above" = "Start my reply above the quote";
"replyplacement_below" = "Start my reply below the quote";
"And place my signature" = "And place my signature";
@@ -131,7 +127,21 @@
"composemessagestype_html" = "HTML";
"composemessagestype_text" = "Plain text";
"composeMessageChanged" = "Changing your messages composition type requires to save your preferences immediately. Continue?";
/* IMAP Accounts */
"New Mail Account" = "New Mail Account";
"Server Name:" = "Server Name:";
"Port:" = "Port:";
"User Name:" = "User Name:";
"Password:" = "Password:";
"Full Name:" = "Full Name:";
"Email:" = "Email:";
"Signature:" = "Signature:";
"(Click to create)" = "(Click to create)";
"Signature" = "Signature";
"Please enter your signature below:" = "Please enter your signature below:";
/* Additional Parameters */
"Additional Parameters" = "Additional Parameters";

View File

@@ -6,10 +6,9 @@
"General" = "Général";
"Calendar Options" = "Calendrier";
"Mail Options" = "Courrier";
"Signature" = "Signature";
"IMAP Accounts" = "Comptes IMAP";
"Vacation" = "Absence prolongée";
"Forward" = "Transfert";
"Identities" = "Identités";
"Password" = "Mot de passe";
"Categories" = "Catégories";
"Name" = "Nom";
@@ -119,9 +118,6 @@
"messageforward_inline" = "intégrés";
"messageforward_attached" = "en pièces jointes";
/* Identities */
"Default identity:" = "Identité par défaut :";
"Manage identities..." = "Gérer les identitiés...";
"replyplacement_above" = "Placer ma réponse avant la citation";
"replyplacement_below" = "Placer ma réponse après la citation";
"And place my signature" = "Et placer ma signature";
@@ -131,7 +127,21 @@
"composemessagestype_html" = "HTML";
"composemessagestype_text" = "Texte";
"composeMessageChanged" = "Changer la composition des messages nécessite d'enregistrer immédiatement vos préférences.\nVoulez-vous continuer?";
/* IMAP Accounts */
"New Mail Account" = "Nouveau compte";
"Server Name:" = "Serveur :";
"Port:" = "Port :";
"User Name:" = "Utilisateur :";
"Password:" = "Mot de passe :";
"(Click to create)" = "(Signature vide)";
"Full Name:" = "Nom complet :";
"Email:" = "Email :";
"Signature:" = "Signature :";
"Signature" = "Signature";
"Please enter your signature below:" = "Introduisez votre signature ci-dessous :";
/* Additional Parameters */
"Additional Parameters" = "Paramètres supplémentaires";

View File

@@ -6,10 +6,9 @@
"General" = "Allgemein";
"Calendar Options" = "Kalender";
"Mail Options" = "E-Mail";
"Signature" = "Signatur";
"IMAP Accounts" = "IMAP Accounts";
"Vacation" = "Vacation";
"Forward" = "Forward";
"Identities" = "Identität";
"Password" = "Passwort";
"Categories" = "Kategorien";
"Name" = "Name";
@@ -119,9 +118,6 @@
"messageforward_inline" = "Eingebunden";
"messageforward_attached" = "Als Anhang";
/* Identities */
"Default identity:" = "Standard Identität:";
"Manage identities..." = "Identitäten verwalten...";
"replyplacement_above" = "Antwort oberhalb";
"replyplacement_below" = "Antwort unterhalb";
"And place my signature" = "Und setze meine Signatur";
@@ -131,7 +127,21 @@
"composemessagestype_html" = "HTML";
"composemessagestype_text" = "Reintext";
"composeMessageChanged" = "Das Ändern des Typs erfordert das sofortige Speichern der Einstellungen. Fortfahren?";
/* IMAP Accounts */
"New Mail Account" = "New Mail Account";
"Server Name:" = "Server Name:";
"Port:" = "Port:";
"User Name:" = "User Name:";
"Password:" = "Passwort:";
"Full Name:" = "Full Name:";
"Email:" = "Email:";
"Signature:" = "Signatur:";
"(Click to create)" = "(Click to create)";
"Signature" = "Signatur";
"Please enter your signature below:" = "Please enter your signature below:";
/* Additional Parameters */
"Additional Parameters" = "Zusätzliche Einstellungen";

View File

@@ -6,7 +6,7 @@
"General" = "Általános";
"Calendar Options" = "Naptár";
"Mail Options" = "Levelezés";
"Identities" = "Identitások";
"IMAP Accounts" = "IMAP Accounts";
"Password" = "Jelszó";
"Categories" = "Categories";
"Name" = "Name";
@@ -102,10 +102,6 @@
"messageforward_inline" = "Levélként";
"messageforward_attached" = "Mellékletként";
/* Identities */
"Default identity:" = "Alapértelmezett identitás:";
"Manage identities..." = "Identitások kezelése...";
"Signature" = "Aláírás";
"replyplacement_above" = "Válasz elhelyezése az idézet fölött";
"replyplacement_below" = "Válasz elhelyezése az idézet alatt";
"And place my signature" = "Aláírás beszúrása";
@@ -115,7 +111,21 @@
"composemessagestype_html" = "HTML";
"composemessagestype_text" = "Egyszerű szöveg";
"composeMessageChanged" = "Changing your messages composition type requires to save your preferences immediately. Continue?";
/* IMAP Accounts */
"New Mail Account" = "New Mail Account";
"Server Name:" = "Server Name:";
"Port:" = "Port:";
"User Name:" = "User Name:";
"Password:" = "Jelszó:";
"Full Name:" = "Full Name:";
"Email:" = "Email:";
"Signature:" = "Aláírás:";
"(Click to create)" = "(Click to create)";
"Signature" = "Aláírás";
"Please enter your signature below:" = "Please enter your signature below:";
/* Additional Parameters */
"Additional Parameters" = "További beállítások";

View File

@@ -6,10 +6,9 @@
"General" = "Generale";
"Calendar Options" = "Opzioni calendario";
"Mail Options" = "Opzioni di posta";
"Signature" = "Firma";
"IMAP Accounts" = "IMAP Accounts";
"Vacation" = "Vacation";
"Forward" = "Forward";
"Identities" = "Identità";
"Password" = "Password";
"Categories" = "Categories";
"Name" = "Name";
@@ -119,9 +118,6 @@
"messageforward_inline" = "Parte del messaggio";
"messageforward_attached" = "Allegato";
/* Identities */
"Default identity:" = "Identità principale:";
"Manage identities..." = "Gestisci identità...";
"replyplacement_above" = "Inizia la risposta sopra il testo a cui si risponde";
"replyplacement_below" = "Inizia la risposta sotto il testo a cui si risponde";
"And place my signature" = "Metti la firma";
@@ -131,7 +127,21 @@
"composemessagestype_html" = "HTML";
"composemessagestype_text" = "Plain text";
"composeMessageChanged" = "Changing your messages composition type requires to save your preferences immediately. Continue?";
/* IMAP Accounts */
"New Mail Account" = "New Mail Account";
"Server Name:" = "Server Name:";
"Port:" = "Port:";
"User Name:" = "User Name:";
"Password:" = "Password:";
"Full Name:" = "Full Name:";
"Email:" = "Email:";
"Signature:" = "Firma:";
"(Click to create)" = "(Click to create)";
"Signature" = "Firma";
"Please enter your signature below:" = "Please enter your signature below:";
/* Additional Parameters */
"Additional Parameters" = "Parametri addizionali";

View File

@@ -6,10 +6,9 @@
"General" = "Общее";
"Calendar Options" = "Календарь";
"Mail Options" = "Почта";
"Signature" = "Подпись";
"IMAP Accounts" = "IMAP Accounts";
"Vacation" = "Vacation";
"Forward" = "Forward";
"Identities" = "Identities";
"Password" = "Пароль";
"Categories" = "Categories";
"Name" = "Name";
@@ -124,9 +123,6 @@
"messageforward_inline" = "В теле письма";
"messageforward_attached" = "Приложенным файлом";
/* Identities */
"Default identity:" = "Обысно я представляюсь как:";
"Manage identities..." = "Управлять именами...";
"replyplacement_above" = "Начинать мой ответ над цитируемым текстом";
"replyplacement_below" = "Начинать мой ответ под цитируемым текстом";
"And place my signature" = "И поместить мою подпись";
@@ -136,7 +132,21 @@
"composemessagestype_html" = "HTML";
"composemessagestype_text" = "Plain text";
"composeMessageChanged" = "Changing your messages composition type requires to save your preferences immediately. Continue?";
/* IMAP Accounts */
"New Mail Account" = "New Mail Account";
"Server Name:" = "Server Name:";
"Port:" = "Port:";
"User Name:" = "User Name:";
"Password:" = "Пароль:";
"Full Name:" = "Full Name:";
"Email:" = "Email:";
"Signature:" = "Подпись:";
"(Click to create)" = "(Click to create)";
"Signature" = "Подпись";
"Please enter your signature below:" = "Please enter your signature below:";
/* Additional Parameters */
"Additional Parameters" = "Дополнительные параметры";

View File

@@ -6,10 +6,9 @@
"General" = "General";
"Calendar Options" = "Opciones de calendario";
"Mail Options" = "Opciones de correo";
"Signature" = "Firma";
"IMAP Accounts" = "IMAP Accounts";
"Vacation" = "Vacation";
"Forward" = "Forward";
"Identities" = "Identidades";
"Password" = "Contraseña";
"Categories" = "Categories";
"Name" = "Name";
@@ -121,9 +120,6 @@
"messageforward_inline" = "Incorporado";
"messageforward_attached" = "Como adjunto";
/* Identities */
"Default identity:" = "Identidad por defecto:";
"Manage identities..." = "Gestionar identidades...";
"replyplacement_above" = "replyplacement_above";
"replyplacement_below" = "replyplacement_below";
"And place my signature" = "And place my signature";
@@ -133,7 +129,21 @@
"composemessagestype_html" = "HTML";
"composemessagestype_text" = "Plain text";
"composeMessageChanged" = "Changing your messages composition type requires to save your preferences immediately. Continue?";
/* IMAP Accounts */
"New Mail Account" = "New Mail Account";
"Server Name:" = "Server Name:";
"Port:" = "Port:";
"User Name:" = "User Name:";
"Password:" = "Contraseña:";
"Full Name:" = "Full Name:";
"Email:" = "Email:";
"Signature:" = "Firma:";
"(Click to create)" = "(Click to create)";
"Signature" = "Firma";
"Please enter your signature below:" = "Please enter your signature below:";
/* Additional Parameters */
"Additional Parameters" = "Additional Parameters";

View File

@@ -6,10 +6,9 @@
"General" = "Allmänt";
"Calendar Options" = "Kalenderinställningar";
"Mail Options" = "E-postinställningar";
"Signature" = "Signatur";
"IMAP Accounts" = "IMAP Accounts";
"Vacation" = "Frånvaro";
"Forward" = "Vidarebefordring";
"Identities" = "Identiteter";
"Password" = "Lösenord";
"Categories" = "Kategorier";
"Name" = "Namn";
@@ -119,9 +118,6 @@
"messageforward_inline" = "Infogade";
"messageforward_attached" = "Bifogade";
/* Identities */
"Default identity:" = "Standardidentitet:";
"Manage identities..." = "Redigera identiteter...";
"replyplacement_above" = "Börja mitt svar ovanför det infogade meddelandet";
"replyplacement_below" = "Börja mitt svar under det infogade meddelandet";
"And place my signature" = "Lägg till min signatur";
@@ -131,7 +127,21 @@
"composemessagestype_html" = "HTML";
"composemessagestype_text" = "Oformaterad text";
"composeMessageChanged" = "Ändring av din meddelandekodning sparar dina inställingar direkt. Fortsätt?";
/* IMAP Accounts */
"New Mail Account" = "New Mail Account";
"Server Name:" = "Server Name:";
"Port:" = "Port:";
"User Name:" = "User Name:";
"Password:" = "Lösenord:";
"Full Name:" = "Full Name:";
"Email:" = "Email:";
"Signature:" = "Signatur:";
"(Click to create)" = "(Click to create)";
"Signature" = "Signatur";
"Please enter your signature below:" = "Please enter your signature below:";
/* Additional Parameters */
"Additional Parameters" = "Övriga parametrar";

View File

@@ -80,26 +80,4 @@
return filterId;
}
- (NSString *) firstMailAccount
{
NSArray *accounts;
NSDictionary *account;
NSString *login, *accountName;
SOGoUser *ownerUser;
login = [[self clientObject] nameInContainer];
ownerUser = [SOGoUser userWithLogin: login];
accounts = [ownerUser mailAccounts];
if ([accounts count] > 0)
{
account = [accounts objectAtIndex: 0];
accountName = [[account objectForKey: @"name"] asCSSIdentifier];
}
else
accountName = @"";
return accountName;
}
@end

View File

@@ -67,6 +67,7 @@
if ((self = [super init]))
{
item = nil;
#warning user should be the owner rather than the activeUser
ASSIGN (user, [context activeUser]);
ASSIGN (userDefaults, [user userDefaults]);
ASSIGN (today, [NSCalendarDate date]);
@@ -575,35 +576,6 @@
[userDefaults setMailMessageForwarding: newMessageForwarding];
}
/*
// <label><var:string label:value="Default identity:"/>
// <var:popup list="identitiesList" item="item"
// string="itemIdentityText" selection="defaultIdentity"/></label>
- (NSArray *) identitiesList
{
NSDictionary *primaryAccount;
#warning we manage only one account per user at this time...
primaryAccount = [[user mailAccounts] objectAtIndex: 0];
return [primaryAccount objectForKey: @"identities"];
}
- (NSString *) itemIdentityText
{
return [(NSDictionary *) item keysWithFormat: @"%{fullName} <%{email}>"];
} */
- (NSString *) signature
{
return [userDefaults mailSignature];
}
- (void) setSignature: (NSString *) newSignature
{
[userDefaults setMailSignature: newSignature];
}
- (NSArray *) replyPlacementList
{
return [NSArray arrayWithObjects: @"above", @"below", nil];
@@ -676,7 +648,7 @@
- (NSString *) sieveCapabilities
{
#warning this should be deduced from the server
#warning sieve caps should be deduced from the server
static NSArray *capabilities = nil;
if (!capabilities)
@@ -931,8 +903,7 @@
if ([[request method] isEqualToString: @"POST"])
{
SOGoMailAccount *account;
id mailAccounts;
id folder;
SOGoMailAccounts *folder;
dd = [[context activeUser] domainDefaults];
if ([dd sieveScriptsEnabled])
@@ -943,13 +914,10 @@
[userDefaults setForwardOptions: forwardOptions];
[userDefaults synchronize];
mailAccounts = [[[context activeUser] mailAccounts] objectAtIndex: 0];
folder = [[self clientObject] mailAccountsFolder: @"Mail"
inContext: context];
account = [folder lookupName: [[mailAccounts objectForKey: @"name"] asCSSIdentifier]
inContext: context
acquire: NO];
account = [folder lookupName: @"0" inContext: context acquire: NO];
[account updateFilters];
if (hasChanged)
@@ -1093,4 +1061,221 @@
return [self labelForKey: item];
}
- (BOOL) mailAuxiliaryUserAccountsEnabled
{
return [[user domainDefaults] mailAuxiliaryUserAccountsEnabled];
}
- (void) _extractMainSignature: (NSDictionary *) account
{
/* We perform some validation here as we have no guaranty on the input
validity. */
NSString *signature;
NSArray *identities;
NSDictionary *identity;
if ([account isKindOfClass: [NSDictionary class]])
{
identities = [account objectForKey: @"identities"];
if ([identities isKindOfClass: [NSArray class]])
{
signature = nil;
if ([identities count] > 0)
{
identity = [identities objectAtIndex: 0];
if ([identity isKindOfClass: [NSDictionary class]])
{
signature = [identity objectForKey: @"signature"];
if (!signature)
signature = @"";
[userDefaults setMailSignature: signature];
}
}
}
}
}
- (BOOL) _validateAccountIdentities: (NSArray *) identities
{
static NSString *identityKeys[] = { @"fullName", @"email", nil };
static NSArray *knownKeys = nil;
NSString **key, *value;
NSDictionary *identity;
NSMutableDictionary *clone;
BOOL valid;
int count, max;
if (!knownKeys)
{
knownKeys = [NSArray arrayWithObjects: @"fullName", @"email",
@"signature", nil];
[knownKeys retain];
}
valid = [identities isKindOfClass: [NSArray class]];
if (valid)
{
max = [identities count];
valid = (max > 0);
for (count = 0; valid && count < max; count++)
{
identity = [identities objectAtIndex: count];
clone = [identity mutableCopy];
[clone removeObjectsForKeys: knownKeys];
valid = ([clone count] == 0);
[clone autorelease];
if (valid)
{
key = identityKeys;
while (valid && *key)
{
value = [identity objectForKey: *key];
if ([value isKindOfClass: [NSString class]]
&& [value length] > 0)
key++;
else
valid = NO;
}
if (valid)
{
value = [identity objectForKey: @"signature"];
valid = (!value || [value isKindOfClass: [NSString class]]);
}
}
}
}
return valid;
}
- (BOOL) _validateAccount: (NSDictionary *) account
{
static NSString *accountKeys[] = { @"name", @"serverName", @"userName",
nil };
static NSArray *knownKeys = nil;
NSMutableDictionary *clone;
NSString **key, *value;
BOOL valid;
if (!knownKeys)
{
knownKeys = [NSArray arrayWithObjects: @"name", @"serverName",
@"userName", @"password", @"encryption",
@"identities", @"mailboxes", nil];
[knownKeys retain];
}
valid = [account isKindOfClass: [NSDictionary class]];
if (valid)
{
clone = [account mutableCopy];
[clone removeObjectsForKeys: knownKeys];
valid = ([clone count] == 0);
[clone autorelease];
key = accountKeys;
while (valid && *key)
{
value = [account objectForKey: *key];
if ([value isKindOfClass: [NSString class]]
&& [value length] > 0)
key++;
else
valid = NO;
}
if (valid)
{
value = [account objectForKey: @"security"];
if (value)
valid = ([value isKindOfClass: [NSString class]]
&& ([value isEqualToString: @"none"]
|| [value isEqualToString: @"ssl"]
|| [value isEqualToString: @"tls"]));
valid &= [self _validateAccountIdentities: [account objectForKey: @"identities"]];
}
}
return valid;
}
- (void) _extractAuxiliaryAccounts: (NSArray *) accounts
{
int count, max, oldMax;
NSArray *oldAccounts;
NSMutableArray *auxAccounts;
NSDictionary *oldAccount;
NSMutableDictionary *account;
NSString *password;
oldAccounts = [user mailAccounts];
oldMax = [oldAccounts count];
max = [accounts count];
auxAccounts = [NSMutableArray arrayWithCapacity: max];
for (count = 1; count < max; count++)
{
account = [accounts objectAtIndex: count];
if ([self _validateAccount: account])
{
password = [account objectForKey: @"password"];
if (!password)
{
if (count < oldMax)
{
oldAccount = [oldAccounts objectAtIndex: count];
password = [oldAccount objectForKey: @"password"];
}
if (!password)
password = @"";
[account setObject: password forKey: @"password"];
}
[auxAccounts addObject: account];
}
}
[userDefaults setAuxiliaryMailAccounts: auxAccounts];
}
- (void) setMailAccounts: (NSString *) newMailAccounts
{
NSArray *accounts;
NSScanner *scanner;
int max;
scanner = [NSScanner scannerWithString: newMailAccounts];
[scanner scanJSONArray: &accounts];
if (accounts && [accounts isKindOfClass: [NSArray class]])
{
max = [accounts count];
if (max > 0)
{
[self _extractMainSignature: [accounts objectAtIndex: 0]];
if ([self mailAuxiliaryUserAccountsEnabled])
[self _extractAuxiliaryAccounts: accounts];
}
}
}
- (NSString *) mailAccounts
{
NSArray *accounts;
NSMutableDictionary *account;
int count, max;
accounts = [user mailAccounts];
max = [accounts count];
for (count = 0; count < max; count++)
{
account = [accounts objectAtIndex: count];
[account removeObjectForKey: @"password"];
}
return [accounts jsonRepresentation];
}
@end

View File

@@ -6,10 +6,9 @@
"General" = "Загальне";
"Calendar Options" = "Календар";
"Mail Options" = "Пошта";
"Signature" = "Підпис";
"IMAP Accounts" = "IMAP Accounts";
"Vacation" = "Відпустка";
"Forward" = "Перенаправлення";
"Identities" = "Облікові записи";
"Password" = "Пароль";
"Categories" = "Категорії";
"Name" = "Назва";
@@ -121,9 +120,6 @@
"messageforward_inline" = "В тілі листа";
"messageforward_attached" = "Вкладеним файлом";
/* Identities */
"Default identity:" = "Зазвичай я представляюсь як:";
"Manage identities..." = "Керувати іменами...";
"replyplacement_above" = "Починати мою відповідь над текстом, що цитується";
"replyplacement_below" = "Починати мою відповідь під текстом, що цитується";
"And place my signature" = "Та додати мій підпис";
@@ -133,7 +129,22 @@
"composemessagestype_html" = "HTML";
"composemessagestype_text" = "Звичайний текст";
"composeMessageChanged" = "Щоб змінити спосіб створення повідомлення потрібно зараз зберегти Ваші налаштування. Продовжити?";
/* IMAP Accounts */
"New Mail Account" = "New Mail Account";
"Server Name:" = "Server Name:";
"Port:" = "Port:";
"User Name:" = "User Name:";
"Password:" = "Пароль:";
"Full Name:" = "Full Name:";
"Email:" = "Email:";
"Signature:" = "Підпис:";
"(Click to create)" = "(Click to create)";
"Signature" = "Підпис";
"Please enter your signature below:" = "Please enter your signature below:";
/* Additional Parameters */
"Additional Parameters" = "Додаткові параметри";

View File

@@ -6,10 +6,9 @@
"General" = "Cyffredinol";
"Calendar Options" = "Opsiynau Calendr";
"Mail Options" = "Opsiynau Ebost";
"Signature" = "Llofnod";
"IMAP Accounts" = "IMAP Accounts";
"Vacation" = "Vacation";
"Forward" = "Forward";
"Identities" = "hunaniaethau";
"Password" = "Cyfrinair";
"Categories" = "Categories";
"Name" = "Name";
@@ -82,8 +81,7 @@
"Day start time must be prior to day end time." = "Day start time must be prior to day end time.";
"First week of year :" = "Wythnos cyntaf y flwyddyn :";
"Enable reminders for Calendar items" = "Galluogu atgoffa ar gyfer eitemau calendr";
"Play a sound when a reminder comes due"
= "Chwarae swn pan fydd amser atgoffa";
"Play a sound when a reminder comes due" = "Chwarae swn pan fydd amser atgoffa";
"Default reminder :" = "Atgoffa gwreiddiol :";
"firstWeekOfYear_January1" = "Dechrau ar Ionawr 1";
@@ -119,9 +117,6 @@
"messageforward_inline" = "Mewn llinell";
"messageforward_attached" = "Fel Atodiad";
/* Identities */
"Default identity:" = "Hunaniaeth Gwreiddiol:";
"Manage identities..." = "Rheoli hunaniaethau...";
"replyplacement_above" = "Dechrau fy ymateb uwchben y dyfynnod";
"replyplacement_below" = "Dechrau fy ymateb o dan y dyfynnod";
"And place my signature" = "A rhowch fy llofnod";
@@ -131,7 +126,21 @@
"composemessagestype_html" = "HTML";
"composemessagestype_text" = "Plain text";
"composeMessageChanged" = "Changing your messages composition type requires to save your preferences immediately. Continue?";
/* IMAP Accounts */
"New Mail Account" = "New Mail Account";
"Server Name:" = "Server Name:";
"Port:" = "Port:";
"User Name:" = "User Name:";
"Password:" = "Cyfrinair:";
"Full Name:" = "Full Name:";
"Email:" = "Email:";
"Signature:" = "Llofnod:";
"(Click to create)" = "(Click to create)";
"Signature" = "Llofnod";
"Please enter your signature below:" = "Please enter your signature below:";
/* Additional Parameters */
"Additional Parameters" = "Additional Parameters";

View File

@@ -10,6 +10,7 @@
className="UIxPageFrame"
title="panelTitle"
const:popup="YES"
const:userDefaultsKeys="SOGoMailComposeMessageType,SOGoMailReplyPlacement,SOGoMailSignature"
const:jsFiles="UIxMailToSelection.js,ckeditor/ckeditor.js,SOGoAutoCompletion.js">
<script type="text/javascript">
var mailIsReply = <var:string value="isMailReply"/>;

View File

@@ -7,11 +7,13 @@
xmlns:label="OGo:label"
className="UIxPageFrame"
title="title"
const:userDefaultsKeys="SOGoMailMessageCheck,SOGoMailListViewColumnsOrder"
const:userSettingsKeys="Mail"
const:jsFiles="dtree.js,MailerUIdTree.js,SOGoAutoCompletion.js,SOGoResizableTable.js,SOGoMailDataSource.js,SOGoDataTable.js">
<script type="text/javascript">
var textMailAccounts = '<var:string value="mailAccounts" const:escapeHTML="NO"/>';
var textDefaultColumnsOrder = '<var:string value="defaultColumnsOrder" const:escapeHTML="NO"/>';
var mailAccounts = <var:string value="mailAccounts" const:escapeHTML="NO"/>;
var inboxData = <var:string value="inboxData" const:escapeHTML="NO"/>;
var getUnseenCountForAllFolders = <var:string value="getUnseenCountForAllFolders"/>;
</script>
<style type="text/css">
<var:if condition="horizontalDragHandleStyle">

View File

@@ -13,7 +13,6 @@
>
<script type="text/javascript">
var filterId = '<var:string value="filterId"/>';
var firstMailAccount = '<var:string value="firstMailAccount"/>';
</script>
<form id="mainForm" var:href="ownPath">
<div id="filterNameContainer" class="container">

View File

@@ -10,7 +10,7 @@
className="UIxPageFrame"
title="title"
const:popup="YES"
const:jsFiles="PasswordPolicy.js,ckeditor/ckeditor.js"
const:jsFiles="RowEditionController.js,PasswordPolicy.js,ckeditor/ckeditor.js"
>
<script type="text/javascript">
var localeCode = '<var:string value="localeCode"/>';
@@ -27,8 +27,8 @@
><var:if condition="userHasMailAccess">
<li target="mailOptionsView"><span><var:string
label:value="Mail Options"/></span></li>
<li target="identitiesView"><span><var:string
label:value="Signature"/></span></li>
<li target="mailAccountsView"><span><var:string
label:value="IMAP Accounts"/></span></li>
<var:if condition="isVacationEnabled"><li target="vacationView"><span><var:string
label:value="Vacation"/></span></li></var:if
><var:if condition="isForwardEnabled"><li target="forwardView"><span><var:string
@@ -122,8 +122,9 @@
></tr>
</var:foreach>
</tbody>
</table></div>
<div class="bottomToolbar">
</table>
</div>
<div const:id="categoriesToolbar" class="bottomToolbar">
<a const:id="categoryAdd" class="bottomButton" href="#">
<span><img rsrc:src="add-icon.png" label:title="Add" />
</span></a>
@@ -195,8 +196,9 @@
<tbody><!--space --></tbody>
</table>
<input type="hidden" const:name="sieveFilters" const:id="sieveFilters"
var:value="sieveFiltersValue"/></div>
<div class="bottomToolbar">
var:value="sieveFiltersValue"/>
</div>
<div const:id="filtersToolbar" class="bottomToolbar">
<a const:id="filterAdd" class="bottomButton" href="#">
<span><img rsrc:src="add-icon.png" label:title="Add" />
</span></a>
@@ -212,10 +214,57 @@
</div>
</var:if>
</div>
<div id="identitiesView" class="tab"
><textarea const:id="signature" const:name="signature"
var:value="signature"
/></div>
<div id="mailAccountsView" class="tab">
<input type="hidden" const:name="mailAccountsJSON" const:id="mailAccountsJSON"
var:value="mailAccounts"/>
<div id="mailAccountsListWrapper"
><ul id="mailAccountsList"
><!-- space --></ul
></div>
<var:if condition="mailAuxiliaryUserAccountsEnabled">
<div const:id="mailAccountsToolbar" class="bottomToolbar">
<a const:id="mailAccountAdd" class="bottomButton" href="#">
<span><img rsrc:src="add-icon.png" label:title="Add" />
</span></a>
<a const:id="mailAccountDelete" class="bottomButton" href="#">
<span><img rsrc:src="remove-icon.png" label:title="Delete" />
</span></a>
</div>
</var:if>
<div id="mailAccountEditor">
<fieldset const:id="accountInfo">
<label><var:string label:value="Server Name:"/>
<input const:name="serverName" const:id="serverName" type="text" const:value=""/></label>
<label><var:string label:value="Port:"/>
<input const:name="port" const:id="port" type="text" const:value=""/></label><br/><br/>
<label><var:string label:value="User Name:"/>
<input const:name="userName" const:id="userName" type="text" const:value=""/></label><br/>
<label><var:string label:value="Password:"/>
<input const:name="password" const:id="password" type="password" const:value=""/></label>
<input const:name="encryption" type="hidden" const:value="none"/>
<!-- <hr/> -->
<!-- <var:string label:value="Encryption:"/> -->
<!-- <label><input const:name="encryption" type="radio" const:value="none"/> -->
<!-- <var:string label:value="None"/></label> -->
<!-- <label><input const:name="encryption" type="radio" const:value="ssl"/> -->
<!-- <var:string label:value="SSL"/></label> -->
<!-- <label><input const:name="encryption" type="radio" const:value="tls"/> -->
<!-- <var:string label:value="TLS"/></label> -->
</fieldset>
<fieldset const:id="identityInfo">
<label><var:string label:value="Full Name:"/>
<input const:name="fullName" const:id="fullName" type="text" const:value=""
/></label><br/>
<label><var:string label:value="Email:"/>
<input const:name="email" const:id="email" type="text" const:value=""/></label><br/><br/>
<var:string label:value="Signature:"/>
<span id="actSignature"><!--space --></span>
</fieldset>
</div>
</div>
<var:if condition="isVacationEnabled">
<div id="vacationView" class="tab">
<label><input type="checkbox"

View File

@@ -7,6 +7,8 @@
xmlns:rsrc="OGo:url"
xmlns:label="OGo:label"
className="UIxPageFrame"
const:userDefaultsKeys="SOGoCalendarCategoriesColors"
const:userSettingsKeys="Calendar,ShowCompletedTasks"
const:jsFiles="SchedulerUIDnD.js"
title="title">
<script type="text/javascript">

View File

@@ -42,10 +42,21 @@
<body var:class="bodyClasses"
><var:if condition="isCompatibleBrowser"
><var:if condition="isPopup" const:negate="YES"
><var:if condition="shortUserNameForDisplay" const:value="anonymous"
const:negate="YES"><var:if condition="hasUserSettingsKeys"
><script type="text/javascript">
var UserSettings = <var:string value="userSettings" const:escapeHTML="NO"/>;
</script
></var:if><var:if condition="hasUserDefaultsKeys">
<script type="text/javascript">
var UserDefaults = <var:string value="userDefaults" const:escapeHTML="NO"/>;
</script
></var:if
></var:if
><var:if condition="isPopup" const:negate="YES"
><var:if condition="isUIxDebugEnabled"
><div id="logConsole"><!-- space --></div></var:if>
<div id="linkBanner" class="linkbanner">
<div id="linkBanner" class="linkbanner">
<var:if condition="canLogoff"
><a id="logoff" var:href="logoffPath"
><var:string label:value="Disconnect"/> </a
@@ -133,8 +144,7 @@
>var UserFolderURL = '<var:string value="userFolderPath" const:escapeHTML="NO"/>';
var UserLogin = '<var:string value="shortUserNameForDisplay" const:escapeHTML="NO"/>';
var UserLanguage = '<var:string value="userLanguage" const:escapeHTML="NO"/>';
var UserSettings = <var:string value="userSettings" const:escapeHTML="NO"/>;
var UserDefaults = <var:string value="userDefaults" const:escapeHTML="NO"/>;</var:if>
</var:if>
<var:string value="commonLocalizableStrings" const:escapeHTML="NO"/>
<var:string value="productLocalizableStrings" const:escapeHTML="NO"/>
</script>

View File

@@ -9,7 +9,8 @@ var usersRightsWindowWidth = 450;
var Contact = {
currentAddressBook: null,
currentContact: null,
deleteContactsRequestCount: null
deleteContactsRequestCount: null,
dialogs: {}
};
function validateEditorInput(sender) {
@@ -274,9 +275,9 @@ function actionContactCallback(http) {
var error = html.select("p").first().firstChild.nodeValue.trim();
log("actionContactCallback failed: error " + http.status + " (" + error + ")");
if (parseInt(http.status) == 403)
window.alert(_("You don't have the required privileges to perform the operation."));
showAlertDialog(_("You don't have the required privileges to perform the operation."));
else if (error)
window.alert(labels[error]);
showAlertDialog(labels[error]);
refreshCurrentFolder();
}
}
@@ -396,7 +397,7 @@ function onToolbarEditSelectedContacts(event) {
var rows = contactsList.getSelectedRowsId();
if (rows.length == 0) {
window.alert(_("Please select a contact."));
showAlertDialog(_("Please select a contact."));
return false;
}
@@ -433,25 +434,51 @@ function onToolbarDeleteSelectedContacts(event) {
var rows = contactsList.getSelectedRowsId();
if (rows.length) {
var label = _("Are you sure you want to delete the selected contacts?");
if (window.confirm(label)) {
for (var i = 0; i < rows.length; i++) {
delete cachedContacts[Contact.currentAddressBook + "/" + rows[i]];
var urlstr = (URLForFolderID(Contact.currentAddressBook) + "/"
+ rows[i] + "/delete");
Contact.deleteContactsRequestCount++;
triggerAjaxRequest(urlstr, onContactDeleteEventCallback,
rows[i]);
}
var dialogId = "deleteContactsDialog";
var dialog = Contact.dialogs[dialogId];
if (dialog) {
dialog.show();
$("bgDialogDiv").show();
}
else {
var label = _("Are you sure you want to delete the selected contacts?");
var fields = createElement("p");
fields.appendChild(createButton("confirmBtn", _("Yes"), onToolbarDeleteSelectedContactsConfirm.bind(fields, dialogId)));
fields.appendChild(createButton("cancelBtn", _("No"), onBodyClickDialogHandler));
var dialog = createDialog(dialogId,
_("Confirmation"),
label,
fields,
"none");
document.body.appendChild(dialog);
dialog.show();
Contact.dialogs[dialogId] = dialog;
}
return false;
}
else {
window.alert(_("Please select a contact."));
}
else
showAlertDialog(_("Please select a contact."));
return false;
}
function onToolbarDeleteSelectedContactsConfirm(dialogId) {
var contactsList = $('contactsList');
var rows = contactsList.getSelectedRowsId();
for (var i = 0; i < rows.length; i++) {
// hide row?
$(rows[i]).hide();
delete cachedContacts[Contact.currentAddressBook + "/" + rows[i]];
var urlstr = (URLForFolderID(Contact.currentAddressBook) + "/"
+ rows[i] + "/delete");
Contact.deleteContactsRequestCount++;
triggerAjaxRequest(urlstr, onContactDeleteEventCallback,
rows[i]);
}
onBodyClickDialogHandler();
}
function onContactDeleteEventCallback(http) {
if (http.readyState == 4) {
if (isHttpStatus204(http.status)) {
@@ -471,13 +498,15 @@ function onContactDeleteEventCallback(http) {
loadContact(Contact.currentContact);
}
}
row.deselect();
row.parentNode.removeChild(row);
}
else if (parseInt(http.status) == 403) {
var row = $(http.callbackData);
row.show();
var displayName = row.readAttribute("contactname");
Contact.deleteContactsRequestCount--;
window.alert(labels["You cannot delete the card of \"%{0}\"."].formatted(displayName));
showAlertDialog(_("You cannot delete the card of \"%{0}\".").formatted(displayName));
}
}
}
@@ -619,6 +648,7 @@ function refreshContacts(cname) {
}
function onAddressBookNew(event) {
var dialogId = "newAddressBookDialog";
createFolder(window.prompt(_("Name of the Address Book"), ""),
appendAddressBook);
preventDefault(event);
@@ -755,7 +785,7 @@ function onAddressBookRemove(event) {
var owner = node.getAttribute("owner");
if (owner == "nobody") {
var label = _("You cannot remove nor unsubscribe from a public addressbook.");
window.alert(label);
showAlertDialog(label);
}
else if (owner == UserLogin) {
var folderIdElements = node.getAttribute("id").split(":");
@@ -774,25 +804,53 @@ function onAddressBookRemove(event) {
function deletePersonalAddressBook(folderId) {
if (folderId == "personal") {
var label = _("You cannot remove nor unsubscribe from your personal addressbook.");
window.alert(label);
showAlertDialog(_("You cannot remove nor unsubscribe from your personal addressbook."));
}
else {
var label
= _("Are you sure you want to delete the selected address book?");
if (window.confirm(label)) {
if (document.deletePersonalABAjaxRequest) {
document.deletePersonalABAjaxRequest.aborted = true;
document.deletePersonalABAjaxRequest.abort();
}
var url = ApplicationBaseURL + folderId + "/delete";
document.deletePersonalABAjaxRequest
= triggerAjaxRequest(url, deletePersonalAddressBookCallback,
folderId);
var dialogId = "deleteAddressBookDialog";
var dialog = Contact.dialogs[dialogId];
if (dialog) {
$("bgDialogDiv").show();
}
else {
var label = _("Are you sure you want to delete the selected address book?");
var fields = createElement("p");
fields.appendChild(createButton(dialogId + "confirmBtn",
"Yes",
deletePersonalAddressBookConfirm.bind(fields)));
fields.appendChild(createButton(dialogId + "cancelBtn",
"No",
onBodyClickDialogHandler));
dialog = createDialog(dialogId,
_("Confirmation"),
label,
fields,
"none");
document.body.appendChild(dialog);
Contact.dialogs[dialogId] = dialog;
}
dialog.folderId = folderId;
dialog.show();
}
return false;
}
function deletePersonalAddressBookConfirm(event) {
if (document.deletePersonalABAjaxRequest) {
document.deletePersonalABAjaxRequest.aborted = true;
document.deletePersonalABAjaxRequest.abort();
}
var dialog = $(this).up("DIV.dialog");
var folderId = dialog.folderId;
var url = ApplicationBaseURL + folderId + "/delete";
document.deletePersonalABAjaxRequest
= triggerAjaxRequest(url, deletePersonalAddressBookCallback,
folderId);
onBodyClickDialogHandler();
}
function deletePersonalAddressBookCallback(http) {
if (http.readyState == 4) {
if (isHttpStatus204(http.status)) {
@@ -970,7 +1028,7 @@ function onAddressBookModify(event) {
{node: selected, name: newName});
}
} else
window.alert(_("Unable to rename that folder!"));
showAlertDialog(_("Unable to rename that folder!"));
}
function folderRenameCallback(http) {
@@ -990,7 +1048,7 @@ function onMenuSharing(event) {
var selected = folders.getSelectedNodes()[0];
var owner = selected.getAttribute("owner");
if (owner == "nobody")
window.alert(clabels["The user rights cannot be"
showAlertDialog(clabels["The user rights cannot be"
+ " edited for this object!"]);
else {
var title = this.innerHTML;
@@ -1146,7 +1204,7 @@ function onDocumentKeydown(event) {
nextRow = row.previous("tr");
if (nextRow) {
row.up().deselectAll();
// Adjust the scollbar
var viewPort = $("contactsListContent");
var divDimensions = viewPort.getDimensions();
@@ -1167,6 +1225,10 @@ function onDocumentKeydown(event) {
Event.stop(event);
}
}
else if (event.ctrlKey == 1 && event.keyCode == 65) { // Ctrl-A
$("contactsList").selectAll();
Event.stop(event);
}
}
/*function fixSearchFieldPosition () {
@@ -1193,10 +1255,7 @@ function initContacts(event) {
$("uploadOK").observe("click", hideImportResults);
}
if (Prototype.Browser.Gecko)
Event.observe(document, "keypress", onDocumentKeydown); // for FF2
else
Event.observe(document, "keydown", onDocumentKeydown);
Event.observe(document, "keydown", onDocumentKeydown);
configureAddressBooks();
updateAddressBooksMenus();

View File

@@ -13,7 +13,7 @@ Element.addMethods({
element = $(element);
var matchingNodes = new Array();
var tagName = tagName.toUpperCase();
tagName = tagName.toUpperCase();
for (var i = 0; i < element.childNodes.length; i++) {
var childNode = $(element.childNodes[i]);
@@ -202,6 +202,17 @@ Element.addMethods({
}
},
selectAll: function(element) {
element = $(element);
if (element.tagName == 'UL')
rows = element.getElementsByTagName('LI');
else
rows = element.select('TBODY TR');
for (var i = 0; i < rows.length; i++)
if (rows[i].nodeType == 1)
$(rows[i]).selectElement();
},
deselect: function(element) {
element = $(element);
element.removeClassName('_selected');
@@ -218,8 +229,9 @@ Element.addMethods({
for (var i = 0; i < element.selectedElements.length; i++)
element.selectedElements[i].removeClassName('_selected');
element.selectedElements = null;
element.selectedIds = null;
}
if (element.selectedIds) {
else if (element.selectedIds) {
for (var i = 0; i < element.selectedIds.length; i++) {
var e = element.down('#' + element.selectedIds[i]);
if (e && e.hasClassName('_selected'))

File diff suppressed because it is too large Load Diff

View File

@@ -1,66 +1,71 @@
/* -*- Mode: java; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
var MailerUIdTreeExtension = {
elementCounter: 1,
folderIcons: { account: "tbtv_account_17x17.png",
inbox: "tbtv_inbox_17x17.png",
sent: "tbtv_sent_17x17.png",
draft: "tbtv_drafts_17x17.png",
trash: "tbtv_trash_17x17.png" },
folderNames: { inbox: _("InboxFolderName"),
sent: _("SentFolderName"),
draft: _("DraftsFolderName"),
trash: _("TrashFolderName") },
_addFolderNode: function (parent, name, fullName, type, unseen) {
var icon = this.folderIcons[type];
if (icon)
icon = ResourcesURL + "/" + icon;
else
icon = "";
var displayName = this.folderNames[type];
var hasUnseen = false;
if (!displayName)
displayName = name;
if (typeof unseen != "undefined") {
hasUnseen = true;
displayName += "<span id=\"unseenCount\"> (<span>" + parseInt(unseen) + "</span>)</span>";
}
this.add(this.elementCounter, parent, displayName, 1, '#', fullName,
type, '', '', icon, icon, hasUnseen);
this.elementCounter++;
},
_addFolder: function (parent, folder) {
var thisCounter = this.elementCounter;
this._addFolderNode(parent, folder.displayName, folder.fullName(), folder.type, folder.unseen);
for (var i = 0; i < folder.children.length; i++)
this._addFolder(thisCounter, folder.children[i]);
},
addMailAccount: function (mailAccount) {
this._addFolder(0, mailAccount);
},
setCookie: function(cookieName, cookieValue, expires, path, domain, secure) {
},
getCookie: function(cookieName) {
return ("");
},
updateCookie: function () {
if (Mailer.foldersStateTimer)
clearTimeout(Mailer.foldersStateTimer);
Mailer.foldersStateTimer = setTimeout('saveFoldersState()', 3000); // 3 seconds
},
getFoldersState: function () {
var expandedFolders = new Array();
for (var n = 0; n < this.aNodes.length; n++) {
if (this.aNodes[n]._io && this.aNodes[n].pid != this.root.id) {
expandedFolders.push(this.aNodes[n].dataname);
}
elementCounter: 1,
folderIcons: { account: "tbtv_account_17x17.png",
inbox: "tbtv_inbox_17x17.png",
sent: "tbtv_sent_17x17.png",
draft: "tbtv_drafts_17x17.png",
trash: "tbtv_trash_17x17.png" },
folderNames: { inbox: _("InboxFolderName"),
sent: _("SentFolderName"),
draft: _("DraftsFolderName"),
trash: _("TrashFolderName") },
_addFolderNode: function (parent, name, fullName, type, unseen) {
var icon = this.folderIcons[type];
if (icon)
icon = ResourcesURL + "/" + icon;
else
icon = "";
var displayName = this.folderNames[type];
if (!displayName)
displayName = name;
displayName += "<span class=\"unseenCount hidden\"> (" + parseInt(unseen) + ")</span>";
this.add(this.elementCounter, parent, displayName, 1, '#', fullName,
type, '', '', icon, icon, true);
this.elementCounter++;
},
_addFolder: function (parent, folder) {
var thisCounter = this.elementCounter;
this._addFolderNode(parent, folder.displayName, folder.fullName(), folder.type, folder.unseen);
for (var i = 0; i < folder.children.length; i++)
this._addFolder(thisCounter, folder.children[i]);
},
addMailAccount: function (mailAccount) {
this._addFolder(0, mailAccount);
},
setCookie: function(cookieName, cookieValue, expires, path, domain, secure) {
},
getCookie: function(cookieName) {
return ("");
},
updateCookie: function () {
if (Mailer.foldersStateTimer)
clearTimeout(Mailer.foldersStateTimer);
Mailer.foldersStateTimer = setTimeout('saveFoldersState()', 3000); // 3 seconds
},
getFoldersState: function () {
var expandedFolders = new Array();
for (var n = 0; n < this.aNodes.length; n++) {
if (this.aNodes[n]._io && this.aNodes[n].pid != this.root.id) {
expandedFolders.push(this.aNodes[n].dataname);
}
}
return expandedFolders.toJSON();
},
autoSync: function() {
this.config.useCookies = true;
},
getMailboxNode: function(mailbox) {
var childNode = null;
for (var i = 0; (childNode == null) && (i < this.aNodes.length); i++) {
var aNode = this.aNodes[i];
if (aNode.dataname == mailbox) {
childNode = $("smailboxTree" + aNode.id);
}
}
return ((childNode) ? childNode.parentNode : null);
}
return expandedFolders.toJSON();
},
autoSync: function() {
this.config.useCookies = true;
}
};
Object.extend(dTree.prototype, MailerUIdTreeExtension);

View File

@@ -0,0 +1,130 @@
/* bind method: attachToRowElement(row: TD or LI)
* callback methods:
notifyStartEditingCallback(this)
notifyEndEditingCallback(this)
notifyNewValueCallback(this, newValue),
*/
function RowEditionController() {
}
RowEditionController.prototype = {
initialValue: null,
rowElement: null,
textField: null,
/* notification callbacks */
notifyStartEditingCallback: null,
notifyEndEditingCallback: null,
notifyNewValueCallback: null,
/* bind method */
attachToRowElement: function REC_attachToRowElement(rowElement) {
var onRowDblClickBound = this.onRowDblClick.bindAsEventListener(this);
rowElement.observe("dblclick", onRowDblClickBound);
this.rowElement = rowElement;
rowElement.editionController = this;
},
/* internal */
emptyRow: function REC_emptyRow() {
var rowElement = this.rowElement;
while (rowElement.firstChild) {
rowElement.removeChild(rowElement.firstChild);
}
},
startEditing: function REC_startEditing() {
var rowElement = this.rowElement;
rowElement.addClassName("editing");
var value = "";
for (var i = 0; i < rowElement.childNodes.length; i++) {
var child = rowElement.childNodes[i];
if (child.nodeType == Node.TEXT_NODE) {
value += child.nodeValue;
}
}
this.initialValue = value;
this.emptyRow();
this.showInputField(value);
this.onBodyMouseDownBound = this.onBodyMouseDown.bindAsEventListener(this);
$(document.body).observe("mousedown", this.onBodyMouseDownBound);
if (this.notifyStartEditingCallback) {
this.notifyStartEditingCallback(this);
}
},
showInputField: function REC_showInputField(value) {
var textField = createElement("input", null, null, {"type": "text"});
this.textField = textField;
if (value) {
textField.value = value;
}
this.rowElement.appendChild(textField);
var onInputKeyDownBound = this.onInputKeyDown.bindAsEventListener(this);
textField.observe("keydown", onInputKeyDownBound);
textField.focus();
textField.select();
},
stopEditing: function REC_stopEditing(accept) {
var displayValue = (accept ? this.textField.value : this.initialValue);
this.textField = null;
this.emptyRow();
var rowElement = this.rowElement;
rowElement.removeClassName("editing");
rowElement.appendChild(document.createTextNode(displayValue));
this.initialValue = null;
$(document.body).stopObserving("mousedown", this.onBodyMouseDownBound);
this.onBodyMouseDownBound = null;
if (this.notifyEndEditingCallback) {
this.notifyEndEditingCallback(this);
}
},
acceptEdition: function REC_acceptEdition() {
var newValue = this.textField.value;
var isValid = (newValue && newValue.length > 0);
if (this.initialValue != newValue
&& isValid
&& this.notifyNewValueCallback) {
this.notifyNewValueCallback(this, newValue);
}
this.stopEditing(isValid);
},
cancelEdition: function REC_acceptEdition() {
this.stopEditing(false);
},
/* event handlers */
onRowDblClick: function REC_onRowDblClick(event) {
if (!this.textField) {
this.startEditing();
event.stop();
}
},
onInputKeyDown: function REC_onInputKeyDown(event) {
if (event.keyCode == Event.KEY_ESC) {
this.cancelEdition();
event.stop();
}
else if (event.keyCode == Event.KEY_RETURN) {
this.acceptEdition();
event.stop();
}
else if (event.keyCode == Event.KEY_TAB) {
this.acceptEdition();
}
},
onBodyMouseDown: function REC_onBodyMouseDown(event) {
if (event.target != this.textField) {
this.acceptEdition();
}
}
};

View File

@@ -24,11 +24,12 @@ var SOGoResizableTableInterface = {
}
SOGoResizableTable._resize(this, $(cell), i, null, cell.getWidth());
}
this.computeColumnsWidths();
Event.observe(window, "resize", this.resize.bind(this));
},
resize: function(e) {
// Only resize the columns after a certain delay, otherwise it slow
// Only resize the columns after a certain delay, otherwise it slows
// down the interface.
if (this.delayedResize) window.clearTimeout(this.delayedResize);
this.delayedResize = this._resize.bind(this).delay(0.2);
@@ -62,7 +63,7 @@ var SOGoResizableTableInterface = {
this.ratios = relativeWidths;
},
saveColumnsState: function() {
computeColumnsWidths: function() {
this.ratios = new Hash();
var tableWidth = 100/this.getWidth();
var cells = $(this).down('tr').childElements();
@@ -71,6 +72,10 @@ var SOGoResizableTableInterface = {
if (cell.hasClassName('resizable'))
this.ratios.set(cell.id, Math.round(cell.getWidth()*tableWidth));
}
},
saveColumnsState: function() {
this.computeColumnsWidths();
if (!$(document.body).hasClassName("popup")) {
var url = ApplicationBaseURL + "saveColumnsState";
var data = this.ratios;

View File

@@ -161,7 +161,7 @@ function editEvent() {
var nodes = listOfSelection.getSelectedRows();
if (nodes.length == 0) {
window.alert(_("Please select an event or a task."));
showAlertDialog(_("Please select an event or a task."));
return false;
}
@@ -175,7 +175,7 @@ function editEvent() {
_editEventId(selectedCalendarCell[0].cname,
selectedCalendarCell[0].calendar);
} else {
window.alert(_("Please select an event or a task."));
showAlertDialog(_("Please select an event or a task."));
}
return false; /* stop following the link */
@@ -198,8 +198,8 @@ function deleteEvent() {
var nodes = listOfSelection.getSelectedRows();
if (nodes.length > 0) {
var label = "";
if (!nodes[0].erasable) {
window.alert(_("You don't have the required privileges to perform the operation."));
if (!nodes[0].erasable && !IsSuperUser) {
showAlertDialog(_("You don't have the required privileges to perform the operation."));
return false;
}
if (listOfSelection == $("tasksList"))
@@ -235,32 +235,43 @@ function deleteEvent() {
}
}
} else {
window.alert(_("Please select an event or a task."));
showAlertDialog(_("Please select an event or a task."));
}
}
else if (selectedCalendarCell) {
if (!selectedCalendarCell[0].erasable) {
window.alert(_("You don't have the required privileges to perform the operation."));
return false;
}
if (selectedCalendarCell[0].recurrenceTime) {
if (selectedCalendarCell.length == 1
&& selectedCalendarCell[0].recurrenceTime) {
_editRecurrenceDialog(selectedCalendarCell[0], "confirmDeletion");
}
else {
var label = _("eventDeleteConfirmation");
if (confirm(label)) {
if (document.deleteEventAjaxRequest) {
document.deleteEventAjaxRequest.aborted = true;
document.deleteEventAjaxRequest.abort();
}
eventsToDelete.push([selectedCalendarCell[0].cname]);
calendarsOfEventsToDelete.push(selectedCalendarCell[0].calendar);
_batchDeleteEvents();
else if (confirm(_("eventDeleteConfirmation"))) {
if (document.deleteEventAjaxRequest) {
document.deleteEventAjaxRequest.aborted = true;
document.deleteEventAjaxRequest.abort();
}
var canDelete = true;
var sortedNodes = [];
var calendars = [];
for (var i = 0; i < selectedCalendarCell.length; i++) {
canDelete = canDelete && (selectedCalendarCell[i].erasable || IsSuperUser);
if (canDelete) {
var calendar = selectedCalendarCell[i].calendar;
if (!sortedNodes[calendar]) {
sortedNodes[calendar] = [];
calendars.push(calendar);
}
sortedNodes[calendar].push(selectedCalendarCell[i].cname);
}
}
for (var i = 0; i < calendars.length; i++) {
calendarsOfEventsToDelete.push(calendars[i]);
eventsToDelete.push(sortedNodes[calendars[i]]);
}
_batchDeleteEvents();
}
}
else
window.alert(_("Please select an event or a task."));
showAlertDialog(_("Please select an event or a task."));
return false;
}
@@ -318,10 +329,10 @@ function modifyEventCallback(http) {
} else {
msg = "delegate is a participant";
}
window.alert(_(msg));
showAlertDialog(_(msg));
}
else {
window.alert(_("eventPartStatModificationError"));
showAlertDialog(_("eventPartStatModificationError"));
}
document.modifyEventAjaxRequest = null;
}
@@ -475,7 +486,7 @@ function deleteEventCallback(http) {
document.deleteEventAjaxRequest = null;
}
else if (parseInt(http.status) == 403)
window.alert(_("You don't have the required privileges to perform the operation."));
showAlertDialog(_("You don't have the required privileges to perform the operation."));
else
log ("deleteEventCallback Ajax error (" + http.status + ")");
}
@@ -668,13 +679,6 @@ function performDeleteEventCallback(http) {
}
}
function onSelectAll() {
var list = $("eventsList");
list.selectRowsMatchingClass("eventRow");
return false;
}
/* in dateselector */
function onDaySelect(node) {
var day = node.getAttribute('day');
@@ -1854,30 +1858,89 @@ function _eventBlocksMatching(calendar, cname, recurrenceTime) {
function selectCalendarEvent(calendar, cname, recurrenceTime) {
// Select event in calendar view
if (selectedCalendarCell)
for (var i = 0; i < selectedCalendarCell.length; i++)
selectedCalendarCell[i].deselect();
var selection = _eventBlocksMatching(calendar, cname, recurrenceTime);
if (selection) {
for (var i = 0; i < selection.length; i++)
selection[i].selectElement();
selectedCalendarCell = selection;
if (selectedCalendarCell)
selectedCalendarCell = selectedCalendarCell.concat(selection);
else
selectedCalendarCell = selection;
}
return selection;
}
function onCalendarSelectEvent(event) {
selectCalendarEvent(this.calendar, this.cname, this.recurrenceTime);
function onSelectAll(event) {
if (listOfSelection)
listOfSelection.selectAll();
else {
// Select events cells
var selectedBlocks = [];
for (var c in calendarEvents) {
var events = calendarEvents[c];
for (var e in events) {
var occurrences = events[e];
for (var i = 0; i < occurrences.length; i++)
selectedBlocks = selectedBlocks.concat(occurrences[i].blocks);
}
}
for (var i = 0; i < selectedBlocks.length; i++)
selectedBlocks[i].selectElement();
selectedCalendarCell = selectedBlocks;
}
// Select event in events list
var list = $("eventsList");
$(list.tBodies[0]).deselectAll();
return false;
}
function onCalendarSelectEvent(event) {
var list = $("eventsList").down("TBODY");
var alreadySelected = false;
// Look for event in events list
// TODO: event will likely not be found if an Ajax query is refreshing
// the events list.
var rowID = this.calendar + "-" + this.cname;
if (this.recurrenceTime)
rowID += "-" + this.recurrenceTime;
var row = $(rowID);
// Check if event is already selected
if (selectedCalendarCell)
for (var i = 0; i < selectedCalendarCell.length; i++)
if (selectedCalendarCell[i] == this) {
alreadySelected = true;
break;
}
if ((isMac() && event.metaKey == 1) || (!isMac() && event.ctrlKey == 1)) {
// If meta or ctrl key is pressed, inverse the selection
if (alreadySelected) {
this.deselect();
selectedCalendarCell.splice(i, 1);
if (row)
row.deselect();
return true;
}
}
else if (event.shiftKey == 0) {
// Unselect entries in events list and calendar view
list.deselectAll();
if (selectedCalendarCell) {
for (var i = 0; i < selectedCalendarCell.length; i++)
if (selectedCalendarCell[i] != this)
selectedCalendarCell[i].deselect();
}
selectedCalendarCell = [this];
}
if (!alreadySelected) {
// Select event in calendar view
selectCalendarEvent(this.calendar, this.cname, this.recurrenceTime);
}
// Select event in events list
if (row) {
var div = row.parentNode.parentNode.parentNode;
div.scrollTop = row.offsetTop - (div.offsetHeight / 2);
@@ -2402,7 +2465,7 @@ function appendCalendar(folderName, folderPath) {
//log ("append name: " + folderName + "; path: " + folderPath + "; owner: " + owner);
if ($(folderPath))
window.alert(_("You have already subscribed to that folder!"));
showAlertDialog(_("You have already subscribed to that folder!"));
else {
var calendarList = $("calendarList");
var items = calendarList.select("li");
@@ -2501,7 +2564,7 @@ function onCalendarRemove(event) {
if (folderId == "/personal") {
var label = labels["You cannot remove nor unsubscribe from your"
+ " personal calendar."];
window.alert(label);
showAlertDialog(label);
}
else {
var folderIdElements = folderId.split(":");
@@ -2649,13 +2712,16 @@ function onDocumentKeydown(event) {
deleteEvent();
event.stop();
}
else if (event.ctrlKey == 1 && event.keyCode == 65) { // Ctrl-A
onSelectAll(event);
Event.stop(event);
}
}
}
function initCalendars() {
sorting["attribute"] = "start";
sorting["ascending"] = true;
showCompletedTasks = parseInt(UserSettings['ShowCompletedTasks']);
if (!$(document.body).hasClassName("popup")) {
var node = $("filterpopup");
@@ -2668,6 +2734,7 @@ function initCalendars() {
initDateSelectorEvents();
initCalendarSelector();
configureSearchField();
showCompletedTasks = parseInt(UserSettings['ShowCompletedTasks']);
configureLists();
$("calendarList").attachMenu("calendarsMenu");
$(document.body).observe("click", onBodyClickHandler);
@@ -2675,10 +2742,7 @@ function initCalendars() {
$("uploadCancel").observe("click", hideCalendarImport);
$("uploadOK").observe("click", hideImportResults);
if (Prototype.Browser.Gecko)
Event.observe(document, "keypress", onDocumentKeydown); // for FF2
else
Event.observe(document, "keydown", onDocumentKeydown);
Event.observe(document, "keydown", onDocumentKeydown);
}
onWindowResize.defer();

View File

@@ -1,4 +1,8 @@
/* -*- Mode: java; tab-width: 2; c-label-minimum-indentation: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* bind method: attachToEventCells(cells), attachToDayNode(dayNode)
* callback method: updateDropCallback(this, eventCells, delta),
createDropCallback(this, dayDate, currentCoordinates);
*/
var SOGoEventDragDayLength = 24 * 4; /* quarters */
var SOGoEventDragHandleSize = 8; /* handles for dragging mode */

View File

@@ -402,7 +402,7 @@ function onOkButtonClick (e) {
action = 'tentative';
else if (value == 4) {
var url = ApplicationBaseURL + activeCalendar + '/' + activeComponent;
document.modifyEventAjaxRequest = delegateInvitation(url, modifyEventCallback);
delegateInvitation(url, modifyEventCallback);
}
if (action != "")

View File

@@ -38,8 +38,7 @@ function onLoadHandler() {
}
function loadMailboxes() {
var url = (ApplicationBaseURL + "Mail/"
+ encodeURI(firstMailAccount) + "/mailboxes");
var url = ApplicationBaseURL + "Mail/0/mailboxes";
triggerAjaxRequest(url, onLoadMailboxesCallback);
}

View File

@@ -5,10 +5,21 @@ DIV#preferencesTabs
right: 5px;
bottom: 5px; }
TEXTAREA#signature
DIV.bottomToolbar
{ position: absolute;
width: 100%;
height: 100%; }
height: 20px;
margin: 0px;
padding: 0px; }
#categoriesToolbar, #filtersToolbar
{ left: 2em;
right: 2em;
bottom: 8px; }
#mailAccountsToolbar
{ left: 5px;
bottom: 9px;
width: 130px; }
DIV#categoriesListWrapper
{ overflow: auto;
@@ -43,14 +54,7 @@ DIV.colorBox
margin: 0 3px 0 0;
width: 1em; }
DIV#windowButtons
{ position: fixed;
bottom: 20px;
left: 0px;
right: 25px;
vertical-align: middle;
text-align: right; }
/* vacation */
#vacation, #forward
{ padding-left: 2.5em; }
#vacation LABEL
@@ -67,13 +71,6 @@ DIV#windowButtons
#passwordView BR
{ clear: both; }
DIV.bottomToolbar
{ position: absolute;
bottom: 9px;
left: 2em;
right: 2em;
margin: 0px; }
/* mail filters */
DIV#filtersListWrapper
{ overflow: auto;
@@ -114,4 +111,82 @@ P.errorMessage#passwordError
{ color: #f00; }
P.infoMessage#passwordError
{ color: #00f; }
{ color: #00f; }
/* mail accounts */
#mailAccountsListWrapper, #mailAccountEditor
{ position: absolute;
overflow: auto;
bottom: 30px;
top: 5px;
left: 0px;
margin: 0px;
padding: 0px; }
#mailAccountsListWrapper
{ overflow-x: hidden;
left: 5px;
width: 130px;
background: #ccddec;
border-top: 1px solid #9b9b9b;
border-left: 1px solid #9b9b9b; }
#mailAccountsList
{ position: absolute;
top: 0px;
bottom: 0px;
left: 0px;
right: 0px;
margin: 0px;
padding: 0px; }
#mailAccountsList LI
{ cursor: pointer;
white-space: nowrap;
padding-left: 5px;
height: 20px; }
#mailAccountsList LI.readonly
{ cursor: default;
font-style: italic; }
#mailAccountEditor
{ left: 140px;
padding-top: 5px;
right: 5px; }
#serverName
{ width: 100px; }
#port
{ width: 30px; }
#userName, #password
{ width: 180px; }
#fullName, #email
{ width: 180px; }
#actSignature
{ color: #55f;
cursor: pointer;
text-decoration: underline; }
#actSignature.disabled
{ color: #999;
cursor: default;
text-decoration: none; }
#signatureDialog
{ position: absolute;
left: 10px;
width: auto !important;
right: 10px;
height: 200px;
top: 50px; }
#signature
{ width: 100%;
height: 90px;
margin: 0px auto;
margin-bottom: 10px; }

View File

@@ -1,7 +1,7 @@
/* -*- Mode: java; tab-width: 2; c-label-minimum-indentation: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
var isSieveScriptsEnabled = false;
var filters = [];
var mailAccounts = null;
var dialogs = {};
function savePreferences(sender) {
var sendForm = true;
@@ -11,7 +11,6 @@ function savePreferences(sender) {
sigList.disabled = false;
if ($("categoriesList")) {
endAllEditables();
serializeCategories();
}
@@ -46,6 +45,8 @@ function savePreferences(sender) {
$("sieveFilters").setValue(jsonFilters.toJSON());
}
saveMailAccounts();
if (sendForm)
$("mainForm").submit();
@@ -78,7 +79,7 @@ function prototypeIfyFilters() {
return newFilters;
}
function _setupEvents(enable) {
function _setupEvents() {
var widgets = [ "timezone", "shortDateFormat", "longDateFormat",
"timeFormat", "weekStartDay", "dayStartTime", "dayEndTime",
"firstWeek", "messageCheck", "subscribedFoldersOnly",
@@ -86,10 +87,7 @@ function _setupEvents(enable) {
for (var i = 0; i < widgets.length; i++) {
var widget = $(widgets[i]);
if (widget) {
if (enable)
widget.observe("change", onChoiceChanged);
else
widget.stopObserving("change", onChoiceChanged);
widget.observe("change", onChoiceChanged);
}
}
@@ -107,14 +105,12 @@ function _setupEvents(enable) {
function onChoiceChanged(event) {
var hasChanged = $("hasChanged");
hasChanged.value = "1";
_setupEvents(false);
}
function addDefaultEmailAddresses(event) {
var defaultAddresses = $("defaultEmailAddresses").value.split(/, */);
var addresses = $("autoReplyEmailAddresses").value.trim();
if (addresses) addresses = addresses.split(/, */);
else addresses = new Array();
@@ -125,7 +121,7 @@ function addDefaultEmailAddresses(event) {
if (i == addresses.length)
addresses.push(adr);
});
$("autoReplyEmailAddresses").value = addresses.join(", ");
event.stop();
@@ -140,14 +136,10 @@ function initPreferences() {
if (filtersListWrapper) {
isSieveScriptsEnabled = true;
}
_setupEvents(true);
_setupEvents();
if (typeof (initAdditionalPreferences) != "undefined")
initAdditionalPreferences();
if ($("signature")) {
onComposeMessagesTypeChange();
}
var table = $("categoriesList");
if (table) {
resetCategoriesColors(null);
@@ -173,11 +165,12 @@ function initPreferences() {
if (button)
button.observe("click", addDefaultEmailAddresses);
var button = $("changePasswordBtn");
button = $("changePasswordBtn");
if (button)
button.observe("click", onChangePasswordClick);
initSieveFilters();
initMailAccounts();
}
function initSieveFilters() {
@@ -343,7 +336,7 @@ function _copyFilterElement(filterElement) { /* element = rule or action */
for (var k in filterElement) {
var value = filterElement[k];
if (typeof(value) == "string" || typeof(value) == "number") {
newElement[k] = value;
newElement[k] = value;
}
}
@@ -377,62 +370,332 @@ function updateFilterFromEditor(filterId, filter) {
}
}
/* mail accounts */
function initMailAccounts() {
var mailAccountsJSON = $("mailAccountsJSON");
mailAccounts = mailAccountsJSON.value.evalJSON();
var mailAccountsList = $("mailAccountsList");
if (mailAccountsList) {
var li = createMailAccountLI(mailAccounts[0], true);
mailAccountsList.appendChild(li);
for (var i = 1; i < mailAccounts.length; i++) {
li = createMailAccountLI(mailAccounts[i]);
mailAccountsList.appendChild(li);
}
var lis = mailAccountsList.childNodesWithTag("li");
lis[0].readOnly = true;
lis[0].selectElement();
var button = $("mailAccountAdd");
if (button) {
button.observe("click", onMailAccountAdd);
}
button = $("mailAccountDelete");
if (button) {
button.observe("click", onMailAccountDelete);
}
}
var info = $("accountInfo");
var inputs = info.getElementsByTagName("input");
for (var i = 0; i < inputs.length; i++) {
$(inputs[i]).observe("change", onMailAccountInfoChange);
}
info = $("identityInfo");
inputs = info.getElementsByTagName("input");
for (var i = 0; i < inputs.length; i++) {
$(inputs[i]).observe("change", onMailIdentityInfoChange);
}
$("actSignature").observe("click", onMailIdentitySignatureClick);
displayMailAccount(mailAccounts[0], true);
}
function onMailAccountInfoChange(event) {
this.mailAccount[this.name] = this.value;
var hasChanged = $("hasChanged");
hasChanged.value = "1";
}
function onMailIdentityInfoChange(event) {
if (!this.mailAccount["identities"]) {
this.mailAccount["identities"] = [{}];
}
var identity = this.mailAccount["identities"][0];
identity[this.name] = this.value;
var hasChanged = $("hasChanged");
hasChanged.value = "1";
}
function onMailIdentitySignatureClick(event) {
if (!this.readOnly) {
var dialogId = "signatureDialog";
var dialog = dialogs[dialogId];
if (!dialog) {
var label = _("Please enter your signature below:");
var fields = createElement("p");
fields.appendChild(createElement("textarea", "signature"));
fields.appendChild(createElement("br"));
fields.appendChild(createButton("okBtn", _("OK"),
onMailIdentitySignatureOK));
fields.appendChild(createButton("cancelBtn", _("Cancel"),
onBodyClickDialogHandler.bind(document.body, dialogId)));
var dialog = createDialog(dialogId,
_("Signature"),
label,
fields,
"none");
document.body.appendChild(dialog);
dialog.show();
dialogs[dialogId] = dialog;
if ($("composeMessagesType").value != 0) {
CKEDITOR.replace('signature',
{ height: "70px",
toolbar: [['Bold', 'Italic', '-', 'Link',
'Font','FontSize','-','TextColor',
'BGColor']
],
language: localeCode,
scayt_sLang: localeCode });
}
}
dialog.mailAccount = this.mailAccount;
if (!this.mailAccount["identities"]) {
this.mailAccount["identities"] = [{}];
}
var identity = this.mailAccount["identities"][0];
var area = $("signature");
area.value = identity["signature"];
dialog.show();
$("bgDialogDiv").show();
if (!CKEDITOR.instances["signature"])
area.focus();
event.stop();
}
}
function onMailIdentitySignatureOK(event) {
var dialog = $("signatureDialog");
var mailAccount = dialog.mailAccount;
if (!mailAccount["identities"]) {
mailAccount["identities"] = [{}];
}
var identity = mailAccount["identities"][0];
var content = (CKEDITOR.instances["signature"]
? CKEDITOR.instances["signature"].getData()
: $("signature").value);
identity["signature"] = content;
displayAccountSignature(mailAccount);
dialog.hide();
$("bgDialogDiv").hide();
dialog.mailAccount = null;
var hasChanged = $("hasChanged");
hasChanged.value = "1";
}
function createMailAccountLI(mailAccount, readOnly) {
var li = createElement("li");
li.appendChild(document.createTextNode(mailAccount["name"]));
li.observe("click", onMailAccountEntryClick);
li.observe("mousedown", onRowClick);
if (readOnly) {
li.addClassName("readonly");
}
else {
var editionCtlr = new RowEditionController();
editionCtlr.attachToRowElement(li);
editionCtlr.notifyNewValueCallback = function(ignore, newValue) {
mailAccount["name"] = newValue;
};
li.editionController = editionCtlr;
}
li.mailAccount = mailAccount;
return li;
}
function onMailAccountEntryClick(event) {
displayMailAccount(this.mailAccount, this.readOnly);
}
function displayMailAccount(mailAccount, readOnly) {
var editor = $("mailAccountEditor");
var inputs = editor.getElementsByTagName("input");
for (var i = 0; i < inputs.length; i++) {
inputs[i].disabled = readOnly;
inputs[i].mailAccount = mailAccount;
}
var encryption = "none";
var encRadioValues = [ "none", "ssl", "tls" ];
if (mailAccount["encryption"]) {
encryption = mailAccount["encryption"];
}
var form = $("mainForm");
form.setRadioValue("encryption", encRadioValues.indexOf(encryption));
var port;
if (mailAccount["port"]) {
port = mailAccount["port"];
}
else {
if (encryption == "ssl") {
port = 993;
}
else {
port = 143;
}
}
$("port").value = port;
$("serverName").value = mailAccount["serverName"];
$("userName").value = mailAccount["userName"];
$("password").value = mailAccount["password"];
var identity = (mailAccount["identities"]
? mailAccount["identities"][0]
: {} );
$("fullName").value = identity["fullName"] || "";
$("email").value = identity["email"] || "";
displayAccountSignature(mailAccount);
}
function displayAccountSignature(mailAccount) {
var actSignature = $("actSignature");
actSignature.mailAccount = mailAccount;
var actSignatureValue;
var identity = (mailAccount["identities"]
? mailAccount["identities"][0]
: {} );
var value = identity["signature"];
if (value && value.length > 0) {
if (value.length < 30) {
actSignatureValue = value;
}
else {
actSignatureValue = value.substr(0, 30) + "...";
}
}
else {
actSignatureValue = _("(Click to create)");
}
while (actSignature.firstChild) {
actSignature.removeChild(actSignature.firstChild);
}
actSignature.appendChild(document.createTextNode(actSignatureValue));
}
function createMailAccount() {
var firstIdentity = mailAccounts[0]["identities"][0];
var newIdentity = {};
for (var k in firstIdentity) {
newIdentity[k] = firstIdentity[k];
}
delete newIdentity["isDefault"];
var newMailAccount = { name: _("New Mail Account"),
serverName: "mailserver",
userName: UserLogin,
password: "",
identities: [ newIdentity ] };
return newMailAccount;
}
function onMailAccountAdd(event) {
var newMailAccount = createMailAccount();
mailAccounts.push(newMailAccount);
var li = createMailAccountLI(newMailAccount);
var mailAccountsList = $("mailAccountsList");
mailAccountsList.appendChild(li);
var selection = mailAccountsList.getSelectedNodes();
for (var i = 0; i < selection.length; i++) {
selection[i].deselect();
}
displayMailAccount(newMailAccount, false);
li.selectElement();
li.editionController.startEditing();
var hasChanged = $("hasChanged");
hasChanged.value = "1";
event.stop();
}
function onMailAccountDelete(event) {
var mailAccountsList = $("mailAccountsList");
var selection = mailAccountsList.getSelectedNodes();
if (selection.length > 0) {
var li = selection[0];
if (!li.readOnly) {
li.deselect();
li.editionController = null;
var next = li.next();
if (!next) {
next = li.previous();
}
mailAccountsList.removeChild(li);
var index = mailAccounts.indexOf(li.mailAccount);
mailAccounts.splice(index, 1);
next.selectElement();
displayMailAccount(next.mailAccount, next.readOnly);
var hasChanged = $("hasChanged");
hasChanged.value = "1";
}
}
event.stop();
}
function saveMailAccounts() {
/* This removal enables us to avoid a few warning from SOPE for the inputs
that were created dynamically. */
var editor = $("mailAccountEditor");
editor.parentNode.removeChild(editor);
compactMailAccounts();
var mailAccountsJSON = $("mailAccountsJSON");
mailAccountsJSON.value = mailAccounts.toJSON();
}
function compactMailAccounts() {
for (var i = 1; i < mailAccounts.length; i++) {
var account = mailAccounts[i];
var encryption = account["encryption"];
if (encryption) {
if (encryption == "none") {
delete account["encryption"];
}
}
else {
encryption = "none";
}
var port = account["port"];
if (port) {
if ((encryption == "ssl" && port == 993)
|| port == 143) {
delete account["port"];
}
}
}
}
/* categories */
function resetTableActions() {
var r = $$("TABLE#categoriesList tbody tr");
for (var i = 0; i < r.length; i++) {
var row = $(r[i]);
row.observe("mousedown", onRowClick);
var tds = row.childElements();
tds[0].observe("mousedown", endAllEditables);
tds[0].observe("dblclick", onNameEdit);
tds[1].observe("mousedown", endAllEditables);
tds[1].childElements()[0].observe ("dblclick", onColorEdit);
}
}
function makeEditable (element) {
element.addClassName ("editing");
element.removeClassName ("categoryListCell");
var tmp = element.innerHTML;
element.innerHTML = "";
var textField = new Element ("input", {"type": "text"});
textField.value = tmp;
textField.setStyle({ width: '98%' });
textField.observe ("keydown", interceptEnter);
element.appendChild (textField);
textField.focus ();
textField.select ();
}
function interceptEnter (e) {
if (e.keyCode == Event.KEY_RETURN) {
endAllEditables (null);
preventDefault (e);
return false;
}
}
function endEditable (element) {
var tmp = element.childElements ().first ().value;
element.innerHTML = tmp;
element.removeClassName ("editing");
element.addClassName ("categoryListCell");
if (parseInt($("hasChanged").value) == 0)
onChoiceChanged(null);
}
function endAllEditables (e) {
var r = $$("TABLE#categoriesList tbody tr td");
for (var i=0; i<r.length; i++) {
if (r[i] != this && r[i].hasClassName ("editing"))
endEditable ($(r[i]));
}
}
function onNameEdit (e) {
endAllEditables ();
if (!this.hasClassName ("editing")) {
makeEditable (this);
var editionCtlr = new RowEditionController();
editionCtlr.attachToRowElement(tds[0]);
tds[1].childElements()[0].observe("dblclick", onColorEdit);
}
}
@@ -442,7 +705,7 @@ function onColorEdit (e) {
r[i].removeClassName ("colorEditing");
this.addClassName ("colorEditing");
var cPicker = window.open(ApplicationBaseURL + "../" + UserLogin
var cPicker = window.open(ApplicationBaseURL + "../" + UserLogin
+ "/Calendar/colorPicker", "colorPicker",
"width=250,height=200,resizable=0,scrollbars=0"
+ "toolbar=0,location=0,directories=0,status=0,"
@@ -458,24 +721,22 @@ function onColorPickerChoice (newColor) {
// div.removeClassName ("colorEditing");
div.showColor = newColor;
div.style.background = newColor;
if (parseInt($("hasChanged").value) == 0)
onChoiceChanged(null);
if (parseInt($("hasChanged").value) == 0) {
var hasChanged = $("hasChanged");
hasChanged.value = "1";
}
}
function onCategoryAdd (e) {
var row = new Element ("tr");
var nametd = new Element ("td").update ("");
var colortd = new Element ("td");
var colordiv = new Element ("div", {"class": "colorBox"});
endAllEditables();
row.identify ();
row.addClassName ("categoryListRow");
nametd.addClassName ("categoryListCell");
colortd.addClassName ("categoryListCell");
colordiv.innerHTML = "&nbsp;";
colordiv.showColor = "#F0F0F0";
@@ -485,9 +746,9 @@ function onCategoryAdd (e) {
row.appendChild (nametd);
row.appendChild (colortd);
$("categoriesList").tBodies[0].appendChild (row);
makeEditable (nametd);
resetTableActions ();
nametd.editionController.startEditing();
}
function onCategoryDelete (e) {
@@ -496,9 +757,9 @@ function onCategoryDelete (e) {
var count = rows.length;
for (var i=0; i < count; i++) {
rows[i].editionController = null;
rows[i].remove ();
}
}
function serializeCategories() {
@@ -542,7 +803,7 @@ function onReplyPlacementListChange() {
function onComposeMessagesTypeChange(event) {
// var textArea = $('signature');
if ($("composeMessagesType").value == 0) /* text */ {
if (this.value == 0) /* text */ {
if (CKEDITOR.instances["signature"]) {
var content = CKEDITOR.instances["signature"].getData();
var htmlEditorWidget = $('cke_signature');
@@ -554,16 +815,16 @@ function onComposeMessagesTypeChange(event) {
textArea.style.visibility = "";
}
} else {
if (!CKEDITOR.instances["signature"]) {
if ($("signature") && !CKEDITOR.instances["signature"]) {
CKEDITOR.replace('signature',
{
height: "290px",
toolbar : [['Bold', 'Italic', '-', 'Link',
'Font','FontSize','-','TextColor',
'BGColor']
],
language : localeCode,
scayt_sLang : localeCode
height: "70px",
toolbar: [['Bold', 'Italic', '-', 'Link',
'Font','FontSize','-','TextColor',
'BGColor']
],
language: localeCode,
scayt_sLang: localeCode
}
);
}

View File

@@ -1,17 +1,16 @@
/* -*- Mode: java; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*--------------------------------------------------|
| dTree 2.05 | www.destroydrop.com/javascript/tree/ |
|---------------------------------------------------|
| Copyright (c) 2002-2003 Geir Landrö |
| |
| This script can be used freely as long as all |
| copyright messages are intact. |
| |
| Updated: 17.04.2003 |
|--------------------------------------------------*/
| dTree 2.05 | www.destroydrop.com/javascript/tree/ |
|---------------------------------------------------|
| Copyright (c) 2002-2003 Geir Landrö |
| |
| This script can be used freely as long as all |
| copyright messages are intact. |
| |
| Updated: 17.04.2003 |
|--------------------------------------------------*/
/* The content of attribute values should be quoted properly by using the
equivalent entities. */
equivalent entities. */
function dTreeQuote(str) {
return (str
.replace(/&/g, "&amp;")
@@ -22,49 +21,48 @@ function dTreeQuote(str) {
// Node object
function Node(id, pid, name, isParent, url, dataname, datatype, title, target,
icon, iconOpen, open, hasUnseen) {
this.isParent = isParent;
this.id = id;
this.pid = pid;
this.name = name;
this.url = url;
this.title = title;
this.target = target;
this.icon = icon;
this.iconOpen = iconOpen;
this.dataname = dataname;
this.datatype = datatype;
this.hasUnseen = hasUnseen;
this._io = open || false;
this._is = false;
this._ls = false;
this._hc = false;
this._ai = 0;
this._p;
icon, iconOpen, open) {
this.isParent = isParent;
this.id = id;
this.pid = pid;
this.name = name;
this.url = url;
this.title = title;
this.target = target;
this.icon = icon;
this.iconOpen = iconOpen;
this.dataname = dataname;
this.datatype = datatype;
this._io = open || false;
this._is = false;
this._ls = false;
this._hc = false;
this._ai = 0;
this._p;
};
// Tree object
function dTree(objName) {
this.obj = objName;
this.config = {
target: null,
useCookies: false
target: null,
useCookies: false
};
this.icon = {
root: 'img/base.gif',
folder: 'img/folder.gif',
folderOpen: 'img/folderopen.gif',
node: 'img/page.gif',
empty: 'img/empty.gif',
line: 'img/line.gif',
join: 'img/join.gif',
joinBottom: 'img/joinbottom.gif',
plus: 'img/plus.gif',
plusBottom: 'img/plusbottom.gif',
minus: 'img/minus.gif',
minusBottom: 'img/minusbottom.gif',
nlPlus: 'img/nolines_plus.gif',
nlMinus: 'img/nolines_minus.gif'
root: 'img/base.gif',
folder: 'img/folder.gif',
folderOpen: 'img/folderopen.gif',
node: 'img/page.gif',
empty: 'img/empty.gif',
line: 'img/line.gif',
join: 'img/join.gif',
joinBottom: 'img/joinbottom.gif',
plus: 'img/plus.gif',
plusBottom: 'img/plusbottom.gif',
minus: 'img/minus.gif',
minusBottom: 'img/minusbottom.gif',
nlPlus: 'img/nolines_plus.gif',
nlMinus: 'img/nolines_minus.gif'
};
this.images = {};
this.objects = {};
@@ -74,7 +72,7 @@ function dTree(objName) {
this.selectedNode = null;
this.selectedFound = false;
this.completed = false;
return this;
};
@@ -91,10 +89,10 @@ dTree.prototype = {
// Adds a new node to the node array
add: function(id, pid, name, isParent, url, datatype,
title, target, icon, iconOpen, open, hasUnseen) {
title, target, icon, iconOpen, open) {
this.aNodes[this.aNodes.length] = new Node(id, pid, name, isParent, url,
datatype, title, target, icon,
iconOpen, open, false, hasUnseen);
iconOpen, open, false);
},
preload: function () {
@@ -115,7 +113,7 @@ dTree.prototype = {
this.objects['namespan'] = new Element ("span", {"class": "nodeName"});
this.objects['image'] = new Element ("img");
},
// Open/close all nodes
openAll: function() {
@@ -131,7 +129,7 @@ dTree.prototype = {
div.id = this.obj;
div.addClassName ("dtree");
if (this.config.useCookies)
this.selectedNode = this.getSelected();
this.selectedNode = this.getSelected();
this.addNode (this.root, div);
if (!this.selectedFound) this.selectedNode = null;
this.completed = true;
@@ -166,26 +164,26 @@ dTree.prototype = {
this.aNodes[nodeId] = node;
if (this.root.id != node.pid) {
var div = this.objects['nodediv'].cloneNode (true);
if (node.datatype)
div.writeAttribute ("datatype", dTreeQuote(node.datatype));
if (node.datatype)
div.writeAttribute ("datatype", dTreeQuote(node.datatype));
if (node.dataname)
div.writeAttribute ("dataname", dTreeQuote(node.dataname));
div.writeAttribute ("dataname", dTreeQuote(node.dataname));
this.indent (node, nodeId, div);
var link = this.objects['nodelink'].cloneNode (true);
link.id = 's' + this.obj + nodeId;
link.href = dTreeQuote(node.url);
if (node.title)
link.writeAttribute ("title", dTreeQuote(node.title));
link.writeAttribute ("title", dTreeQuote(node.title));
if (node.target)
link.writeAttribute ("target", dTreeQuote(node.target));
link.writeAttribute ("target", dTreeQuote(node.target));
link.observe ("click", this.s.bindAsEventListener(this, parseInt(nodeId)));
if (!node.icon)
node.icon = (this.root.id == node.pid) ?
if (!node.icon)
node.icon = (this.root.id == node.pid) ?
this.icon.root : ((node._hc) ? this.icon.folder : this.icon.node);
if (!node.iconOpen)
node.iconOpen = (node._hc) ?
if (!node.iconOpen)
node.iconOpen = (node._hc) ?
this.icon.folderOpen : this.icon.node;
if (this.root.id == node.pid) {
@@ -199,28 +197,26 @@ dTree.prototype = {
var span = this.objects['namespan'].cloneNode (true);
if (!node.isParent)
span.addClassName ("leaf");
if (node.hasUnseen)
span.addClassName ("unseen");
span.addClassName ("leaf");
span.update (node.name);
link.appendChild (img);
link.appendChild (span);
div.appendChild (link);
if (container)
container.appendChild (div);
container.appendChild (div);
else
rc = div;
rc = div;
}
if (node._hc) {
var div = this.objects['clipdiv'].cloneNode (true);
div.id = 'd' + this.obj + nodeId;
div.setStyle ({"display":
((this.root.id == node.pid || node._io) ?
'block' : 'none')});
div.setStyle ({"display":
((this.root.id == node.pid || node._io) ?
'block' : 'none')});
this.addNode(node, div);
if (container)
container.appendChild (div);
container.appendChild (div);
}
this.aIndent.pop();
return rc;
@@ -230,8 +226,8 @@ dTree.prototype = {
indent: function(node, nodeId, container) {
if (this.root.id != node.pid) {
for (var n=0; n<this.aIndent.length; n++) {
var img = (this.aIndent[n] == 1) ?
this.images['line'] : this.images['empty'];
var img = (this.aIndent[n] == 1) ?
this.images['line'] : this.images['empty'];
container.appendChild (img.cloneNode (true));
}
(node._ls) ? this.aIndent.push(0) : this.aIndent.push(1);
@@ -241,9 +237,9 @@ dTree.prototype = {
link.observe ("click", this.o.bindAsEventListener(this, parseInt(nodeId)));
var img;
if (node._io)
img = ((node._ls) ? this.images['minusbottom'] : this.images['minus']);
img = ((node._ls) ? this.images['minusbottom'] : this.images['minus']);
else
img = ((node._ls) ? this.images['plusbottom'] : this.images['plus']);
img = ((node._ls) ? this.images['plusbottom'] : this.images['plus']);
img = img.cloneNode (true);
img.id = 'j' + this.obj + nodeId;
link.appendChild (img);
@@ -275,7 +271,7 @@ dTree.prototype = {
// Highlights the selected node
s: function(id, withEvent) {
if (withEvent)
id = withEvent;
id = withEvent;
var cn = this.aNodes[id];
if (this.selectedNode != id) {
if (this.selectedNode || this.selectedNode==0) {
@@ -292,12 +288,12 @@ dTree.prototype = {
// Toggle Open or close
o: function(id, withEvent) {
if (withEvent)
id = withEvent;
id = withEvent;
var cn = this.aNodes[id];
this.nodeStatus(!cn._io, id, cn._ls);
cn._io = !cn._io;
if (this.config.useCookies) this.updateCookie();
return false;
},
@@ -349,18 +345,18 @@ dTree.prototype = {
if (this.aNodes[n].pid == node.id && this.aNodes[n]._hc) {
if (this.aNodes[n]._io) this.nodeStatus(false, n, this.aNodes[n]._ls);
this.aNodes[n]._io = false;
this.closeAllChildren(this.aNodes[n]);
this.closeAllChildren(this.aNodes[n]);
}
}
},
// Change the status of a node(open or closed)
nodeStatus: function(status, id, bottom) {
eDiv = document.getElementById('d' + this.obj + id);
if (eDiv) {
eJoin = $('j' + this.obj + id);
eIcon = document.getElementById('i' + this.obj + id);
eIcon.src = (status) ? this.aNodes[id].iconOpen : this.aNodes[id].icon;
eIcon = document.getElementById('i' + this.obj + id);
eIcon.src = (status) ? this.aNodes[id].iconOpen : this.aNodes[id].icon;
eJoin.src = ((status)?((bottom)?this.icon.minusBottom:this.icon.minus):((bottom)?this.icon.plusBottom:this.icon.plus));
eDiv.style.display = (status) ? 'block': 'none';
}
@@ -373,7 +369,7 @@ dTree.prototype = {
this.setCookie('co'+this.obj, 'cookieValue', yesterday);
this.setCookie('cs'+this.obj, 'cookieValue', yesterday);
},
// [Cookie] Sets value in a cookie
setCookie: function(cookieName, cookieValue, expires, path, domain, secure) {
document.cookie =
@@ -408,7 +404,7 @@ dTree.prototype = {
}
this.setCookie('co' + this.obj, str);
},
// [Cookie] Checks if a node id is in a cookie
isOpen: function(id) {
var aOpen = this.getCookie('co' + this.obj).split('.');
@@ -420,18 +416,18 @@ dTree.prototype = {
// If Push and pop is not implemented by the browser
if (!Array.prototype.push) {
Array.prototype.push = function array_push() {
for(var i=0;i<arguments.length;i++)
this[this.length]=arguments[i];
return this.length;
}
Array.prototype.push = function array_push() {
for(var i=0;i<arguments.length;i++)
this[this.length]=arguments[i];
return this.length;
}
};
if (!Array.prototype.pop) {
Array.prototype.pop = function array_pop() {
lastElement = this[this.length-1];
this.length = Math.max(this.length-1,0);
return lastElement;
}
Array.prototype.pop = function array_pop() {
lastElement = this[this.length-1];
this.length = Math.max(this.length-1,0);
return lastElement;
}
};

View File

@@ -113,27 +113,6 @@ div#header span#navtitle
div#header a:hover
{ text-decoration: none; }
/* the dock */
a.skydockfont
{ text-decoration: none;
color: #06348B; }
font.skydockfont
{ text-decoration: none;
color: #06348B; }
font.skydockfont_inactiveMail
{ text-decoration: none;
color: #CCCCCC;
font-weight: bold; }
font.skydockfont_newMail
{ text-decoration: none;
color: #06348B;
font-weight: bold; }
table.skytextdocktable
{ padding: 0px;
table-layout: auto; }
DIV.linkbanner
{ background-color: #222;
width: 100%;
@@ -504,7 +483,7 @@ LI[class~="_selected"].denied
background-color: #f33;
}
DIV.hidden
.hidden
{ visibility: hidden; }
/* folder tree (js) )*/
@@ -566,7 +545,7 @@ DIV.resize-handle
color: #fff !important;
}
DIV.dialog
DIV.dialog
{ position: absolute;
width: 350px;
z-index: 50; }
@@ -597,6 +576,27 @@ DIV.dialog.right DIV
margin-right: 19px;
text-align: right; }
DIV.dialog.none
{ position: relative;
opacity: 1;
margin: 100px auto; /* top margin could be dynamically set depending on window height */
z-index: 10000; }
DIV.dialog.none DIV
{ padding: 10px;
padding-bottom: 31px; }
DIV.dialog.none H3
{ margin: 0; }
DIV#bgDialogDiv
{ position: absolute;
top: 0px; left: 0px;
bottom: 0px;
right: 0px;
opacity: .3;
background-color: #555 !important; }
DIV#uploadDialog,
DIV#uploadResults
{ border-width: 1px;
@@ -674,6 +674,7 @@ A.button {
height: 23px;
padding-right: 3px; /* sliding doors padding */
text-decoration: none;
text-align: center;
color: inherit;
cursor: pointer;
}
@@ -684,6 +685,7 @@ A.button SPAN {
line-height: 13px;
padding: 5px 2px 5px 5px;
cursor: pointer;
min-width: 70px;
}
A.button.actionButton SPAN
@@ -946,3 +948,11 @@ DIV.bottomToolbar A.bottomButton SPAN
{ background: transparent url('thead_bg.png') repeat-x top right; }
DIV.bottomToolbar A.bottomButton SPAN:active
{ background-position: bottom left; }
/* row editing */
.editing > INPUT[type="text"]
{ width: 98%; }
/* modalbox */
DIV#modalbox SPAN
{ float: right; }

View File

@@ -1,27 +1,23 @@
/* -*- Mode: java; tab-width: 2; c-label-minimum-indentation: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
/* generic.js - this file is part of SOGo
Copyright (C) 2005 SKYRIX Software AG
Copyright (C) 2006-2010 Inverse
This file is part of SOGo
SOGo 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.
SOGo 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.
SOGo 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.
SOGo 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 SOGo; see the file COPYING. If not, write to the
Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
02111-1307, USA.
*/
/* some generic JavaScript code for SOGo */
You should have received a copy of the GNU Lesser General Public
License along with SOGo; see the file COPYING. If not, write to the
Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
02111-1307, USA.
*/
var logConsole;
var logWindow = null;
@@ -32,6 +28,9 @@ var recoveryRequest = null;
var menus = new Array();
var search = {};
var sorting = {};
var dialogs = {};
var dialogActive = false;
var dialogsStack = new Array();
var lastClickedRow = -1;
@@ -227,12 +226,12 @@ function openMailTo(senderMailTo) {
function deleteDraft(url) {
/* this is called by UIxMailEditor with window.opener */
new Ajax.Request(url, {
asynchronous: false,
method: 'post',
onFailure: function(transport) {
log("draftDeleteCallback: problem during ajax request: " + transport.status);
}
});
asynchronous: false,
method: 'post',
onFailure: function(transport) {
log("draftDeleteCallback: problem during ajax request: " + transport.status);
}
});
}
function refreshFolderByType(type) {
@@ -335,13 +334,7 @@ function triggerAjaxRequest(url, callback, userdata, content, headers) {
hasContentLength = true;
http.setRequestHeader(i, headers[i]);
}
} /*
if (!hasContentLength) {
var cLength = "0";
if (content)
cLength = "" + content.length;
http.setRequestHeader("Content-Length", "" + cLength);
} */
}
http.send(content ? content : "");
}
else {
@@ -351,6 +344,44 @@ function triggerAjaxRequest(url, callback, userdata, content, headers) {
return http;
}
function AjaxRequestsChain(callback, callbackData) {
this.requests = [];
this.counter = 0;
this.callback = callback;
this.callbackData = callbackData;
}
AjaxRequestsChain.prototype = {
requests: null,
counter: 0,
callback: null,
callbackData: null,
_step: function ARC__step() {
if (this.counter < this.requests.length) {
var request = this.requests[this.counter];
this.counter++;
var chain = this;
var origCallback = request[1];
request[1] = function ARC__step_callback(http) {
if (origCallback) {
http.callback = origCallback;
origCallback.apply(http, [http]);
}
chain._step();
};
triggerAjaxRequest.apply(window, request);
}
else {
this.callback.apply(this, [this.callbackData]);
}
},
start: function ARC_start() {
this._step();
}
};
function startAnimation(parent, nextNode) {
var anim = $("progressIndicator");
if (!anim) {
@@ -376,8 +407,9 @@ function checkAjaxRequestsState() {
startAnimation(toolbar);
}
else if (!activeAjaxRequests
&& progressImage)
&& progressImage) {
progressImage.parentNode.removeChild(progressImage);
}
}
function isMac() {
@@ -440,14 +472,19 @@ function refreshOpener() {
/* selection mechanism */
function eventIsLeftClick(event) {
var isLeftClick = true;
var isLeftClick = true;
if (isMac() && isSafari()) {
if (event.ctrlKey == 1)
isLeftClick = false; // Control-click is equivalent to right-click under Mac OS X
else if (event.metaKey == 1) // Command-click
if (event.ctrlKey == 1) {
// Control-click is equivalent to right-click under Mac OS X
isLeftClick = false;
}
else if (event.metaKey == 1) {
// Command-click
isLeftClick = true;
else
}
else {
isLeftClick = Event.isLeftClick(event);
}
}
else {
isLeftClick = event.isLeftClick();
@@ -517,7 +554,7 @@ function onRowClick(event) {
var initialSelection = $(node.parentNode).getSelectedNodes();
if (initialSelection.length > 0
if (initialSelection.length > 0
&& initialSelection.indexOf(node) >= 0
&& !eventIsLeftClick(event))
// Ignore non primary-click (ie right-click) inside current selection
@@ -589,15 +626,16 @@ function popupMenu(event, menuId, target) {
menuLeft -= (popup.offsetWidth + 1);
var isVisible = true;
if (popup.prepareVisibility)
if (popup.prepareVisibility) {
if (!popup.prepareVisibility())
isVisible = false;
}
Event.stop(event);
if (isVisible) {
popup.setStyle({ top: menuTop + "px",
left: menuLeft + "px",
visibility: "visible" });
left: menuLeft + "px",
visibility: "visible" });
document.currentPopupMenu = popup;
@@ -613,11 +651,12 @@ function getParentMenu(node) {
var menure = new RegExp("(^|\s+)menu(\s+|$)", "i");
while (menuNode == null
&& currentNode)
&& currentNode) {
if (menure.test(currentNode.className))
menuNode = currentNode;
else
currentNode = currentNode.parentNode;
}
return menuNode;
}
@@ -756,7 +795,7 @@ function log(message) {
return;
}
if (message[message.length-1] == "\n") {
message = message.substr(0, message.length-2);
message = message.substr(0, message.length-1);
}
var lines = message.split("\n");
for (var i = 0; i < lines.length; i++) {
@@ -815,11 +854,12 @@ function popupSubmenu(event) {
+ this.offsetTop);
if (window.height()
< (menuTop + submenuNode.offsetHeight))
< (menuTop + submenuNode.offsetHeight)) {
if (submenuNode.offsetHeight < window.height())
menuTop = window.height() - submenuNode.offsetHeight;
else
menuTop = 0;
}
var menuLeft = (parentNode.offsetLeft + parentNode.offsetWidth - 3);
if (window.width()
@@ -834,8 +874,8 @@ function popupSubmenu(event) {
parentNode.observe("mouseover", onMouseEnteredParentMenu);
$(this).addClassName("submenu-selected");
submenuNode.setStyle({ top: menuTop + "px",
left: menuLeft + "px",
visibility: "visible" });
left: menuLeft + "px",
visibility: "visible" });
preventDefault(event);
}
}
@@ -872,8 +912,8 @@ function popupSearchMenu(event) {
var popup = $(menuId);
offset = Position.positionedOffset(this);
popup.setStyle({ top: this.offsetHeight + "px",
left: (offset[0] + 3) + "px",
visibility: "visible" });
left: (offset[0] + 3) + "px",
visibility: "visible" });
document.currentPopupMenu = popup;
$(document.body).observe("click", onBodyClickMenuHandler);
@@ -1033,8 +1073,9 @@ function popupToolbarMenu(node, menuId) {
hideMenu(document.currentPopupMenu);
var popup = $(menuId);
if (popup.prepareVisibility)
if (popup.prepareVisibility) {
popup.prepareVisibility();
}
var offset = $(node).cumulativeOffset();
var top = offset.top + node.offsetHeight;
@@ -1055,7 +1096,7 @@ function folderSubscriptionCallback(http) {
http.callbackData["method"](http.callbackData["data"]);
}
else
window.alert(_("Unable to subscribe to that folder!"));
showAlertDialog(_("Unable to subscribe to that folder!"));
document.subscriptionAjaxRequest = null;
}
else
@@ -1091,7 +1132,7 @@ function folderUnsubscriptionCallback(http) {
http.callbackData["method"](http.callbackData["data"]);
}
else
window.alert(_("Unable to unsubscribe from that folder!"));
showAlertDialog(_("Unable to unsubscribe from that folder!"));
}
}
@@ -1111,7 +1152,7 @@ function unsubscribeFromFolder(folderUrl, owner, refreshCallback,
triggerAjaxRequest(url, folderUnsubscriptionCallback, rfCbData);
}
else
window.alert(_("You cannot unsubscribe from a folder that you own!"));
showAlertDialog(_("You cannot unsubscribe from a folder that you own!"));
}
}
@@ -1144,7 +1185,7 @@ function getListIndexForFolder(items, owner, folderName) {
var i;
var previousOwner = null;
for (var i = 0; i < items.length; i++) {
for (i = 0; i < items.length; i++) {
var currentFolderName = items[i].lastChild.nodeValue.strip();
var currentOwner = items[i].readAttribute('owner');
if (currentOwner == owner) {
@@ -1152,11 +1193,13 @@ function getListIndexForFolder(items, owner, folderName) {
if (currentFolderName > folderName)
break;
}
else if (previousOwner ||
(currentOwner != UserLogin && currentOwner > owner))
else if (previousOwner ||
(currentOwner != UserLogin && currentOwner > owner)) {
break;
else if (currentOwner == "nobody")
}
else if (currentOwner == "nobody") {
break;
}
}
return i;
@@ -1181,7 +1224,7 @@ function refreshAlarms() {
if (document.alarmsListAjaxRequest)
return false;
url = UserFolderURL + "Calendar/alarmslist?browserTime=" + utc;
document.alarmsListAjaxRequest
document.alarmsListAjaxRequest
= triggerAjaxRequest(url, refreshAlarmsCallback);
return true;
@@ -1420,33 +1463,6 @@ function indexColor(number) {
return color;
}
function reloadPreferences() {
var url = UserFolderURL + "jsonDefaults";
var http = createHTTPClient();
http.open("GET", url, false);
http.send("");
if (http.status == 200) {
if (http.responseText.length > 0) {
UserDefaults = http.responseText.evalJSON(true);
if (!UserDefaults)
UserDefaults = {};
}
else
UserDefaults = {};
}
url = UserFolderURL + "jsonSettings";
http.open("GET", url, false);
http.send("");
if (http.status == 200) {
if (http.responseText.length > 0)
UserSettings = http.responseText.evalJSON(true);
else
UserSettings = {};
}
}
function onLoadHandler(event) {
queryParameters = parseQueryParameters('' + window.location);
if (!$(document.body).hasClassName("popup")) {
@@ -1496,7 +1512,7 @@ function onLinkBannerClick() {
function onPreferencesClick(event) {
var urlstr = UserFolderURL + "preferences";
var w = window.open(urlstr, "_blank",
"width=440,height=450,resizable=1,scrollbars=0,location=0");
"width=480,height=450,resizable=1,scrollbars=0,location=0");
w.opener = window;
w.focus();
@@ -1595,19 +1611,22 @@ function createFolderCallback(http) {
function delegateInvitation(componentUrl, callbackFunction, callbackData) {
var input = $("delegatedTo");
var delegatedTo = null;
if (input.readAttribute("uid") != null)
if (input.readAttribute("uid") != null) {
delegatedTo = input.readAttribute("uid");
else if (input.value.blank())
}
else if (input.value.blank()) {
alert(_("noEmailForDelegation"));
else
}
else {
delegatedTo = input.value;
}
if (delegatedTo) {
var receiveUpdates = false; //confirm("Do you want to keep receiving updates on the event?");
var urlstr = componentUrl + "/delegate";
var parameters = "to=" + delegatedTo + "&receiveUpdates=" + (receiveUpdates?"YES":"NO");
return triggerAjaxRequest(urlstr, callbackFunction, callbackData, parameters,
{ "Content-type": "application/x-www-form-urlencoded" });
triggerAjaxRequest(urlstr, callbackFunction, callbackData, parameters,
{ "Content-type": "application/x-www-form-urlencoded" });
}
}
@@ -1661,11 +1680,11 @@ function _(key) {
**/
AIM = {
frame : function(c) {
frame: function(c) {
var d = new Element ('div');
var n = d.identify ();
d.innerHTML = '<iframe style="display:none" src="about:blank" id="'
+ n + '" name="' + n + '" onload="AIM.loaded(\'' + n + '\')"></iframe>';
d.innerHTML = '<iframe style="display:none" src="about:blank" id="'
+ n + '" name="' + n + '" onload="AIM.loaded(\'' + n + '\')"></iframe>';
document.body.appendChild(d);
var i = $(n); // TODO: useful?
if (c && typeof(c.onComplete) == 'function')
@@ -1673,11 +1692,11 @@ AIM = {
return n;
},
form : function(f, name) {
form: function(f, name) {
f.writeAttribute('target', name);
},
submit : function(f, c) {
submit: function(f, c) {
AIM.form(f, AIM.frame(c));
if (c && typeof(c.onStart) == 'function')
return c.onStart();
@@ -1685,14 +1704,17 @@ AIM = {
return true;
},
loaded : function(id) {
loaded: function(id) {
var i = $(id);
if (i.contentDocument)
if (i.contentDocument) {
var d = i.contentDocument;
else if (i.contentWindow)
}
else if (i.contentWindow) {
var d = i.contentWindow.document;
else
}
else {
var d = window.frames[id].document;
}
if (d.location.href == "about:blank")
return;
@@ -1707,6 +1729,18 @@ function createDialog(id, title, legend, content, positionClass) {
var newDialog = createElement("div", id, ["dialog", positionClass]);
newDialog.setStyle({"display": "none"});
if (positionClass == "none") {
var bgDiv = $("bgDialogDiv");
if (bgDiv) {
bgDiv.show();
}
else {
bgDiv = createElement("div", "bgDialogDiv", ["bgDialog"]);
document.body.appendChild(bgDiv);
bgDiv.observe("click", onBodyClickDialogHandler);
}
}
var subdiv = createElement("div", null, null, null, null, newDialog);
if (title && title.length > 0) {
var titleh3 = createElement("h3", null, null, null, null, subdiv);
@@ -1734,6 +1768,55 @@ function createButton(id, caption, action) {
return newButton;
}
function showAlertDialog(label) {
var div = $("bgDialogDiv");
if (div && div.visible()) {
dialogsStack.push(label);
return;
}
else {
_showAlertDialog(label);
}
}
function _showAlertDialog(label) {
var dialog = null;
if (dialogs[label])
dialog = dialogs[label];
if (dialog) {
$("bgDialogDiv").show();
}
else {
var fields = createElement("p");
fields.appendChild(createButton(null,
_("OK"),
onBodyClickDialogHandler));
dialog = createDialog(null,
_("Warning"),
label,
fields,
"none");
document.body.appendChild(dialog);
dialogs[label] = dialog;
}
dialog.show();
}
function onBodyClickDialogHandler() {
$$("DIV.dialog").each(function(div) {
if (div.visible())
div.hide();
});
if (dialogsStack.length > 0) {
var label = dialogsStack.first();
dialogsStack.splice(0, 1);
_showAlertDialog.delay(0.1, label);
}
else
$("bgDialogDiv").hide();
}
function readCookie(name) {
var foundCookie = null;

View File

@@ -7,6 +7,9 @@ FORM
margin: 0px; }
/* generic */
DIV#bgDialogDiv
{ filter: alpha(opacity=30); }
SPAN.disabledToolbarButton
{ filter: alpha(opacity=40); }
@@ -31,6 +34,9 @@ UL#userList
DIV.tabsContainer > DIV.scrollToolbar > A > SPAN
{ line-height: 12px; }
DIV#bgDialogDiv
{ filter: alpha(opacity=30); }
/* MailerUI */
IMG.dragMessage

View File

@@ -326,7 +326,6 @@ var Draggable = Class.create({
this.offset = [0,1].map( function(i) { return (pointer[i] - pos[i]) });
Draggables.activate(this);
Event.stop(event);
}
},