mirror of
https://github.com/inverse-inc/sogo.git
synced 2026-04-22 13:29:29 +00:00
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:
378
ChangeLog
378
ChangeLog
@@ -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,
|
||||
|
||||
Binary file not shown.
@@ -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
|
||||
|
||||
@@ -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:):
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -115,6 +115,11 @@
|
||||
return @"text/x-vcard";
|
||||
}
|
||||
|
||||
- (NSString *) davAddressData
|
||||
{
|
||||
return [self contentAsString];
|
||||
}
|
||||
|
||||
/* specialized actions */
|
||||
|
||||
- (void) save
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -41,12 +41,10 @@
|
||||
@class NSMutableDictionary;
|
||||
|
||||
@interface SOGoMailAccounts : SOGoFolder
|
||||
{
|
||||
NSMutableDictionary *accountKeys;
|
||||
}
|
||||
|
||||
- (NSArray *) mailAccounts;
|
||||
|
||||
- (NSArray *) toManyRelationshipKeys;
|
||||
- (NSDictionary *) accountKeys;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@@ -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 */];
|
||||
}
|
||||
|
||||
@@ -69,7 +69,6 @@
|
||||
- (NSMutableString *) traversalFromMailAccount;
|
||||
|
||||
- (NSURL *) imap4URL;
|
||||
- (NSString *) imap4Login;
|
||||
- (NSString *) imap4PasswordRenewed: (BOOL) renew;
|
||||
|
||||
- (void) flushMailCaches;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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"];
|
||||
|
||||
@@ -121,6 +121,10 @@
|
||||
|
||||
- (NSString *) davCollectionTag;
|
||||
|
||||
/* multiget helper */
|
||||
- (WOResponse *) performMultigetInContext: (WOContext *) queryContext
|
||||
inNamespace: (NSString *) namespace;
|
||||
|
||||
@end
|
||||
|
||||
#endif /* __SOGo_SOGoGCSFolder_H__ */
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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"];
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
|
||||
#import <Foundation/NSObject.h>
|
||||
|
||||
#include "SOGoSource.h"
|
||||
#import "SOGoSource.h"
|
||||
|
||||
@class NSArray;
|
||||
@class NSDictionary;
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
- (BOOL) isIPhone;
|
||||
- (BOOL) isICal;
|
||||
- (BOOL) isICal4;
|
||||
- (BOOL) isAddressBookApp;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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:";
|
||||
|
||||
@@ -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:";
|
||||
|
||||
@@ -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:";
|
||||
|
||||
@@ -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:";
|
||||
|
||||
@@ -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 :";
|
||||
|
||||
@@ -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:";
|
||||
|
||||
@@ -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:";
|
||||
|
||||
@@ -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:";
|
||||
|
||||
@@ -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:" = "Начало:";
|
||||
|
||||
@@ -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:";
|
||||
|
||||
@@ -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:";
|
||||
|
||||
@@ -40,6 +40,8 @@
|
||||
NSString *toolbar;
|
||||
id item;
|
||||
BOOL isPopup;
|
||||
NSArray *udKeys;
|
||||
NSArray *usKeys;
|
||||
NSMutableArray *additionalCSSFiles;
|
||||
NSMutableArray *additionalJSFiles;
|
||||
}
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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:" = "Початок:";
|
||||
|
||||
@@ -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:";
|
||||
|
||||
@@ -35,7 +35,6 @@
|
||||
NSString *trashFolderName;
|
||||
}
|
||||
|
||||
- (WOResponse *) statusFoldersAction;
|
||||
- (WOResponse *) listMailboxesAction;
|
||||
|
||||
@end
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 */
|
||||
|
||||
|
||||
@@ -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";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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" = "Дополнительные параметры";
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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" = "Додаткові параметри";
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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"/>;
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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
@@ -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);
|
||||
|
||||
130
UI/WebServerResources/RowEditionController.js
Normal file
130
UI/WebServerResources/RowEditionController.js
Normal 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();
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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 != "")
|
||||
|
||||
@@ -38,8 +38,7 @@ function onLoadHandler() {
|
||||
}
|
||||
|
||||
function loadMailboxes() {
|
||||
var url = (ApplicationBaseURL + "Mail/"
|
||||
+ encodeURI(firstMailAccount) + "/mailboxes");
|
||||
var url = ApplicationBaseURL + "Mail/0/mailboxes";
|
||||
triggerAjaxRequest(url, onLoadMailboxesCallback);
|
||||
}
|
||||
|
||||
|
||||
@@ -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; }
|
||||
|
||||
@@ -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 = " ";
|
||||
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
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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, "&")
|
||||
@@ -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;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -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; }
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
Reference in New Issue
Block a user