Monotone-Parent: 5652b12a99be3b69adbf207347b951ec2e5f6c89
Monotone-Revision: 45f4738cc8fd2e71713e3625b32c19bd7ac54ce4 Monotone-Author: flachapelle@inverse.ca Monotone-Date: 2008-07-09T17:11:18 Monotone-Branch: ca.inverse.sogomaint-2.0.2
parent
812c7c535e
commit
4a29cda5b0
16
ChangeLog
16
ChangeLog
|
@ -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
2
NEWS
|
@ -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
|
||||
|
|
|
@ -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.";
|
|
@ -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.";
|
|
@ -31,8 +31,8 @@
|
|||
{
|
||||
}
|
||||
|
||||
- (WOResponse *) copyAction;
|
||||
- (WOResponse *) moveAction;
|
||||
- (id <WOActionResults>) copyAction;
|
||||
- (id <WOActionResults>) moveAction;
|
||||
|
||||
@end
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
@ -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("<", "<", "g")
|
||||
.replace(">", ">", "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");
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue