Monotone-Parent: 5652b12a99be3b69adbf207347b951ec2e5f6c89

Monotone-Revision: 45f4738cc8fd2e71713e3625b32c19bd7ac54ce4

Monotone-Author: flachapelle@inverse.ca
Monotone-Date: 2008-07-09T17:11:18
Monotone-Branch: ca.inverse.sogo
maint-2.0.2
Francis Lachapelle 2008-07-09 17:11:18 +00:00
parent 812c7c535e
commit 4a29cda5b0
9 changed files with 299 additions and 73 deletions

View File

@ -1,3 +1,19 @@
2008-07-15 Francis Lachapelle <flachapelle@inverse.ca>
* UI/Contacts/UIxContactFolderActions.m: new class for actions
on contacts of a specific address book.
* UI/Contacts/UIxContactFolderActions.m
([_moveContacts:toFolder:andKeepCopy:]): private method to
move and optionnaly copy one or many contacts to another
address book.
* UI/Contacts/UIxContactFolderActions.m ([WOActionResults
copyAction]): copy one or many contacts to another address book.
* UI/Contacts/UIxContactFolderActions.m ([WOActionResults
moveAction]): move one or many contacts to another address book.
2008-07-14 Wolfgang Sourdeau <wsourdeau@inverse.ca>
* SoObjects/SOGo/SOGoContentObject.m ([SOGoContentObject

2
NEWS
View File

@ -7,6 +7,8 @@
- added address completion in the web mail editor
- implemented support for CalDAV methods which were missing for supporting iCal 3
- added support to write to multiple contacts from the Address Book module
- added support to move and copy one or many contacts to another
address book in the Address Book module
- added icons to folders in Address Book module
- fixed various bugs occuring with Safari 3.1
- fixed various bugs occuring with Firefox 3

View File

@ -133,3 +133,10 @@
= "The selected contact has no email address.";
"Please select a contact." = "Please select a contact.";
/* Error messages for move and copy */
"SoAccessDeniedException" = "You cannot write to this address book.";
"Forbidden" = "You cannot write to this address book.";
"Invalid Contact" = "The selected contact no longer exists.";
"Unknown Destination Folder" = "The chosen destination address book no longer exists.";

View File

@ -145,3 +145,10 @@
= "Cette personne n'a pas d'adresse courriel.";
"Please select a contact." = "Veuillez sélectionner un contact..";
/* Error messages for move and copy */
"SoAccessDeniedException" = "Vous ne pouvez pas ajouter de fiches à ce carnet.";
"Forbidden" = "Vous ne pouvez pas ajouter de fiches à ce carnet.";
"Invalid Contact" = "La fiche sélectionnée n'existe plus.";
"Unknown Destination Folder" = "Le carnet d'adresses choisi n'existe plus.";

View File

@ -31,8 +31,8 @@
{
}
- (WOResponse *) copyAction;
- (WOResponse *) moveAction;
- (id <WOActionResults>) copyAction;
- (id <WOActionResults>) moveAction;
@end

View File

@ -24,6 +24,9 @@
#import <Foundation/NSDictionary.h>
#import <Foundation/NSEnumerator.h>
#import <NGObjWeb/NSException+HTTP.h>
#import <NGObjWeb/SoPermissions.h>
#import <NGObjWeb/SoSecurityManager.h>
#import <NGObjWeb/WOContext+SoObjects.h>
#import <NGObjWeb/WORequest.h>
#import <NGObjWeb/WOResponse.h>
@ -46,83 +49,157 @@
@implementation UIxContactFolderActions
- (id) init
- (NSException*) _moveContacts: (NSArray*) contactsId
toFolder: (NSString*) destinationFolderId
andKeepCopy: (BOOL) keepCopy
{
if ((self = [super init]))
{
}
return self;
}
- (void) dealloc
{
[super dealloc];
}
- (WOResponse *) copyAction
{
WORequest *request;
NSArray *contactsId;
NSEnumerator *uids;
NSString *folderId, *uid, *newUid;
SOGoContactFolders *folders;
SOGoParentFolder *folder, *destinationFolder;
id <SOGoContactObject> contact;
SOGoContentObject *newContact;
NGVCard *card;
NSEnumerator *uids;
NSException *ex;
NSString *uid, *newUid;
id <SOGoContactObject> contact;
SOGoContactFolders *folders;
SOGoParentFolder *sourceFolder, *destinationFolder;
SOGoContentObject *newContact;
SoSecurityManager *sm;
WORequest *request;
unsigned int errorCount;
sm = [SoSecurityManager sharedSecurityManager];
request = [context request];
ex = nil;
errorCount = 0;
if ((folderId = [request formValueForKey: @"folder"]) &&
(contactsId = [request formValuesForKey: @"uid"]))
// Search the specified destination folder
sourceFolder = [self clientObject];
folders = [(SOGoUserFolder*)[[sourceFolder container] container] privateContacts: @"Contacts"
inContext: nil];
destinationFolder = [folders lookupName: destinationFolderId
inContext: nil
acquire: NO];
if (destinationFolder)
{
// Search the specified destination folder
folder = [self clientObject];
folders = [(SOGoUserFolder*)[[folder container] container] privateContacts: @"Contacts"
inContext: nil];
destinationFolder = [folders lookupName: folderId
inContext: nil
acquire: NO];
if (destinationFolder)
// Verify write access to the folder
ex = [sm validatePermission: SoPerm_AddDocumentsImagesAndFiles
onObject: destinationFolder
inContext: context];
if (ex == nil)
{
uids = [contactsId objectEnumerator];
uids = [contactsId objectEnumerator];
uid = [uids nextObject];
while (uid)
{
contact = [folder lookupName: uid
inContext: [self context]
acquire: NO];
if (![(NSObject*)contact isKindOfClass: [NSException class]])
// Search the contact ID
contact = [sourceFolder lookupName: uid
inContext: [self context]
acquire: NO];
if ([(NSObject*)contact isKindOfClass: [NSException class]])
errorCount++;
else
{
newUid = [SOGoObject globallyUniqueObjectId];
card = [contact vCard];
[card setUid: newUid];
//[contact setContent: [card versitString]];
//[contact copyTo: destinationFolder]; // verify if exception
newContact = [SOGoContentObject objectWithName: newUid
inContainer: destinationFolder];
if (keepCopy)
{
// Change the contact UID
newUid = [SOGoObject globallyUniqueObjectId];
[card setUid: newUid];
newContact = [SOGoContentObject objectWithName: newUid
inContainer: destinationFolder];
}
else
// Don't change the contact UID
newContact = [SOGoContentObject objectWithName: [contact nameInContainer]
inContainer: (SOGoGCSFolder*)destinationFolder];
ex = [newContact saveContentString: [card versitString]];
if (ex == nil && !keepCopy)
// Delete the original contact if necessary
ex = [contact delete];
if (ex != nil)
errorCount++;
}
}
uid = [uids nextObject];
}
}
}
else
ex = [NSException exceptionWithName: @"UnkownDestinationFolder"
reason: @"Unknown Destination Folder"
userInfo: nil];
return [self responseWithStatus: 204];
if (errorCount > 0)
// At least one contact was not copied
ex = [NSException exceptionWithHTTPStatus: 400
reason: @"Invalid Contact"];
else if (ex != nil)
// Destination address book doesn't exist or is not writable
ex = [NSException exceptionWithHTTPStatus: 403
reason: [ex name]];
return ex;
}
- (WOResponse *) moveAction
- (id <WOActionResults>) copyAction
{
return [self responseWithStatus: 204];
WORequest *request;
id <WOActionResults> response;
NSString *destinationFolderId;
NSArray *contactsId;
NSException *ex;
request = [context request];
ex = nil;
if ((destinationFolderId = [request formValueForKey: @"folder"]) &&
(contactsId = [request formValuesForKey: @"uid"]))
{
ex = [self _moveContacts: contactsId
toFolder: destinationFolderId
andKeepCopy: YES];
if (ex != nil)
response = (id)ex;
}
else
response = [NSException exceptionWithHTTPStatus: 400
reason: @"missing 'folder' and/or 'uid' parameter"];
if (ex == nil)
response = [self responseWith204];
return response;
}
- (id <WOActionResults>) moveAction
{
WORequest *request;
id <WOActionResults> response;
NSString *destinationFolderId;
NSArray *contactsId;
NSException *ex;
request = [context request];
ex = nil;
if ((destinationFolderId = [request formValueForKey: @"folder"]) &&
(contactsId = [request formValuesForKey: @"uid"]))
{
ex = [self _moveContacts: contactsId
toFolder: destinationFolderId
andKeepCopy: NO];
if (ex != nil)
response = (id)ex;
}
else
response = [NSException exceptionWithHTTPStatus: 400
reason: @"missing 'folder' and/or 'uid' parameter"];
if (ex == nil)
response = [self responseWith204];
return response;
}
@end

View File

@ -97,12 +97,12 @@
actionName = "saveUserRights";
};
copy = {
protectedBy = "View";
protectedBy = "Access Contents Information";
actionClass = "UIxContactFolderActions";
actionName = "copy";
};
move = {
protectedBy = "View";
protectedBy = "Delete Objects";
actionClass = "UIxContactFolderActions";
actionName = "move";
};
@ -137,15 +137,10 @@
actionName = "canAccessContent";
};
copy = {
protectedBy = "<public>";
protectedBy = "Access Contents Information";
actionClass = "UIxContactFolderActions";
actionName = "copy";
};
move = {
protectedBy = "<public>";
actionClass = "UIxContactFolderActions";
actionName = "move";
};
};
};

View File

@ -185,9 +185,11 @@ function onAddressBooksContextMenu(event) {
}
function onContactContextMenu(event) {
var contactsList = $("contactsList");
var menu = $("contactMenu");
menu.observe("hideMenu", onContactContextMenuHide);
popupMenu(event, "contactMenu", this);
if (contactsList)
popupMenu(event, "contactMenu", contactsList.getSelectedRows());
}
function onContactContextMenuHide(event) {
@ -231,6 +233,50 @@ function onFolderMenuHide(event) {
topNode.selectedEntry.selectElement();
}
function _onContactMenuAction(folderItem, action, refresh) {
var selectedFolders = $("contactFolders").getSelectedNodes();
var folderId = $(folderItem).readAttribute("folderId").substring(1);
if (Object.isArray(document.menuTarget) && selectedFolders.length > 0) {
var selectedFolderId = $(selectedFolders[0]).readAttribute("id");
var contactIds = $(document.menuTarget).collect(function(row) {
return row.getAttribute("id");
});
var url = ApplicationBaseURL + selectedFolderId + "/" + action + "?folder=" + folderId + "&uid="
+ contactIds.join("&uid=");
if (refresh)
triggerAjaxRequest(url, actionContactCallback, selectedFolderId);
else
triggerAjaxRequest(url, actionContactCallback);
}
}
function onContactMenuCopy(event) {
_onContactMenuAction(this, "copy", false);
}
function onContactMenuMove(event) {
_onContactMenuAction(this, "move", true);
}
function actionContactCallback(http) {
if (http.readyState == 4)
if (isHttpStatus204(http.status)) {
var refreshFolderId = http.callbackData;
if (refreshFolderId)
openContactsFolder(refreshFolderId, true);
}
else {
var html = new Element("div").update(http.responseText);
var error = html.select("p").first().firstChild.nodeValue.trim();
log("actionContactCallback failed: error " + http.status + " (" + error + ")");
if (error)
window.alert(labels[error]);
refreshCurrentFolder();
}
}
function loadContact(idx) {
if (document.contactAjaxRequest) {
document.contactAjaxRequest.aborted = true;
@ -392,7 +438,7 @@ function onToolbarDeleteSelectedContacts(event) {
new Ajax.Request(url, {
method: 'post',
onFailure: function(transport) {
log("Ajax error: can't delete contact");
window.alert(labels["You cannot delete the selected contact(s)"]);
refreshCurrentFolder();
},
onSuccess: function(transport) {
@ -555,10 +601,12 @@ function appendAddressBook(name, folder) {
li.setAttribute("id", folder);
li.setAttribute("owner", owner);
li.addClassName("local");
li.appendChild(document.createTextNode(name
.replace("&lt;", "<", "g")
.replace("&gt;", ">", "g")));
setEventsOnAddressBook(li);
updateAddressBooksMenus();
}
return result;
@ -720,6 +768,59 @@ function configureAddressBooks() {
}
}
function onAddressBookMenuPrepareVisibility() {
var selectedFolder = $("contactFolders").getSelectedNodes()[0];
if (selectedFolder) {
var selectedFolderId = selectedFolder.readAttribute("id");
$(this).select("li").each(function(menuEntry) {
if (menuEntry.readAttribute("folderId") == selectedFolderId)
menuEntry.addClassName("disabled");
else
menuEntry.removeClassName("disabled");
});
}
}
function updateAddressBooksMenus() {
var contactFoldersList = $("contactFolders");
if (contactFoldersList) {
var contactFolders = contactFoldersList.select("li");
var contactActions = new Hash({ move: onContactMenuMove,
copy: onContactMenuCopy });
contactActions.keys().each(function(key) {
var callbacks = new Array();
var menuId = key + "ContactMenu";
var menuDIV = $(menuId);
if (menuDIV)
menuDIV.parentNode.removeChild(menuDIV);
menuDIV = document.createElement("div");
pageContent = $("pageContent");
pageContent.appendChild(menuDIV);
var menu = document.createElement("ul");
menuDIV.appendChild(menu);
$(menuDIV).addClassName("menu");
menuDIV.setAttribute("id", menuId);
var submenuIds = new Array();
for (var i = 0; i < contactFolders.length; i++) {
if (contactFolders[i].hasClassName("local")) {
var menuEntry = new Element("li",
{ folderId: contactFolders[i].readAttribute("id"),
owner: contactFolders[i].readAttribute("owner") }
).update(contactFolders[i].innerHTML);
menu.appendChild(menuEntry);
callbacks.push(contactActions.get(key));
}
}
menuDIV.prepareVisibility = onAddressBookMenuPrepareVisibility;
initMenu(menuDIV, callbacks);
});
}
}
function setEventsOnAddressBook(folder) {
var node = $(folder);
@ -797,23 +898,42 @@ function onAddressBooksMenuPrepareVisibility() {
}
function onContactMenuPrepareVisibility() {
var contactId = document.menuTarget.getAttribute('id');
var contactRow = $(contactId);
var elements = $(this).down("ul").childElements();
var contactRows = document.menuTarget;
var selectedFolder = $("contactFolders").getSelectedNodes()[0];
var options = { write: false,
aim: false };
var elements = $(this).down("ul").childElements();
var writeOption = elements[2];
var emailCell = contactRow.down('td', 1);
if (emailCell.firstChild)
var aimOption = elements[3];
var deleteOption = elements[5];
var moveOption = elements[7];
$A(contactRows).each(function(contactRow) {
var emailCell = contactRow.down('td', 1);
options.write |= (emailCell.firstChild != null);
var aimCell = contactRow.down('td', 2);
options.aim |= (aimCell.firstChild != null);
});
if (options.write)
writeOption.removeClassName("disabled");
else
writeOption.addClassName("disabled");
var aimOption = elements[3];
var aimCell = contactRow.down('td', 2);
if (aimCell.firstChild)
if (options.aim)
aimOption.removeClassName("disabled");
else
aimOption.addClassName("disabled");
if ($(selectedFolder).hasClassName("remote")) {
// Remote address books are always read-only
deleteOption.addClassName("disabled");
moveOption.addClassName("disabled");
}
else {
deleteOption.removeClassName("disabled");
moveOption.removeClassName("disabled");
}
}
function getMenus() {
@ -823,7 +943,8 @@ function getMenus() {
onMenuSharing);
menus["contactMenu"] = new Array(onMenuEditContact, "-",
onMenuWriteToContact, onMenuAIMContact,
"-", onMenuDeleteContact);
"-", onMenuDeleteContact, "-",
"moveContactMenu", "copyContactMenu");
menus["searchMenu"] = new Array(setSearchCriteria);
var contactFoldersMenu = $("contactFoldersMenu");
@ -850,6 +971,7 @@ function initContacts(event) {
configureSelectionButtons();
configureAbToolbar();
configureAddressBooks();
updateAddressBooksMenus();
// initDnd();
var table = $("contactsList");

View File

@ -780,7 +780,7 @@ function backtrace() {
}
function popupSubmenu(event) {
if (this.submenu && this.submenu != "") {
if (this.submenu && this.submenu != "" && !$(this).hasClassName("disabled")) {
var submenuNode = $(this.submenu);
var parentNode = getParentMenu(this);
if (parentNode.submenu)