diff --git a/SoObjects/Appointments/SOGoCalendarComponent.m b/SoObjects/Appointments/SOGoCalendarComponent.m index 25728f14f..de77d9bc9 100644 --- a/SoObjects/Appointments/SOGoCalendarComponent.m +++ b/SoObjects/Appointments/SOGoCalendarComponent.m @@ -750,18 +750,39 @@ _occurenceHasID (iCalRepeatableEntityObject *occurence, NSString *recID) uids = [NSMutableArray array]; persons = [iCalPersons objectEnumerator]; - currentPerson = [persons nextObject]; - while (currentPerson) + while ((currentPerson = [persons nextObject])) { uid = [currentPerson uid]; if (uid) [uids addObject: uid]; - currentPerson = [persons nextObject]; } return uids; } +- (NSException *) copyToFolder: (SOGoGCSFolder *) newFolder +{ + NSArray *elements; + NSString *newUID; + unsigned int count, max; + iCalCalendar *calendar; + SOGoCalendarComponent *newComponent; + + newUID = [self globallyUniqueObjectId]; + calendar = [self calendar: NO secure: NO]; + + elements = [calendar allObjects]; + max = [elements count]; + for (count = 0; count < max; count++) + [[elements objectAtIndex: count] setUid: newUID]; + + newComponent = [[self class] objectWithName: + [NSString stringWithFormat: @"%@.ics", newUID] + inContainer: newFolder]; + + return [newComponent saveContentString: [calendar versitString]]; +} + - (NSString *) _roleOfOwner: (iCalRepeatableEntityObject *) component { NSString *role; diff --git a/SoObjects/Contacts/SOGoContactGCSEntry.m b/SoObjects/Contacts/SOGoContactGCSEntry.m index b56e5a928..73bca8969 100644 --- a/SoObjects/Contacts/SOGoContactGCSEntry.m +++ b/SoObjects/Contacts/SOGoContactGCSEntry.m @@ -60,6 +60,27 @@ return card; } +/* actions */ + +- (NSException *) copyToFolder: (SOGoGCSFolder *) newFolder +{ + NGVCard *newCard; + NSString *newUID; + SOGoContactGCSEntry *newContact; + + // Change the contact UID + newUID = [self globallyUniqueObjectId]; + newCard = [self vCard]; + + [newCard setUid: newUID]; + + newContact = [[self class] objectWithName: + [NSString stringWithFormat: @"%@.vcf", newUID] + inContainer: newFolder]; + + return [newContact saveContentString: [newCard versitString]]; +} + /* DAV */ - (NSString *) davContentType diff --git a/SoObjects/Contacts/SOGoContactLDIFEntry.m b/SoObjects/Contacts/SOGoContactLDIFEntry.m index 6caa7ca17..3b7104c07 100644 --- a/SoObjects/Contacts/SOGoContactLDIFEntry.m +++ b/SoObjects/Contacts/SOGoContactLDIFEntry.m @@ -27,6 +27,7 @@ #import #import +#import "SOGoContactGCSEntry.h" #import "SOGoContactLDIFEntry.h" @implementation SOGoContactLDIFEntry @@ -200,6 +201,26 @@ { } +/* DAV */ +- (NSException *) copyToFolder: (SOGoGCSFolder *) newFolder +{ + NGVCard *newCard; + NSString *newUID; + SOGoContactGCSEntry *newContact; + + // Change the contact UID + newUID = [self globallyUniqueObjectId]; + newCard = [self vCard]; + + [newCard setUid: newUID]; + + newContact = [SOGoContactGCSEntry objectWithName: + [NSString stringWithFormat: @"%@.vcf", newUID] + inContainer: newFolder]; + + return [newContact saveContentString: [newCard versitString]]; +} + /* message type */ - (NSString *) outlookMessageClass diff --git a/SoObjects/SOGo/SOGoContentObject.h b/SoObjects/SOGo/SOGoContentObject.h index 958ce9ed7..9d4860118 100644 --- a/SoObjects/SOGo/SOGoContentObject.h +++ b/SoObjects/SOGo/SOGoContentObject.h @@ -59,9 +59,13 @@ - (NSException *) saveContentString: (NSString *) _str baseVersion: (unsigned int) _baseVersion; - (NSException *) saveContentString: (NSString *)_str; + +/* actions */ +- (NSException *) copyToFolder: (SOGoGCSFolder *) newFolder; +- (NSException *) moveToFolder: (SOGoGCSFolder *) newFolder; - (NSException *) delete; -/* etag support */ +/* DAV support */ - (id) davEntityTag; - (NSString *) davCreationDate; diff --git a/SoObjects/SOGo/SOGoContentObject.m b/SoObjects/SOGo/SOGoContentObject.m index df0c47a65..c014c41a5 100644 --- a/SoObjects/SOGo/SOGoContentObject.m +++ b/SoObjects/SOGo/SOGoContentObject.m @@ -198,6 +198,30 @@ return [self saveContentString: newContent baseVersion: 0]; } +/* actions */ + +- (NSException *) copyToFolder: (SOGoGCSFolder *) newFolder +{ + [self subclassResponsibility: _cmd]; + + return nil; +} + +- (NSException *) moveToFolder: (SOGoGCSFolder *) newFolder +{ + SOGoContentObject *newObject; + NSException *ex; + + newObject = [[self class] objectWithName: nameInContainer + inContainer: newFolder]; + [newObject setIsNew: YES]; + ex = [newObject saveContentString: content]; + if (!ex) + ex = [self delete]; + + return ex; +} + - (NSException *) delete { /* Note: "iCal multifolder saves" are implemented in the apt subclass! */ diff --git a/UI/Common/UIxFolderActions.m b/UI/Common/UIxFolderActions.m index fae4f02b4..b0fc2175c 100644 --- a/UI/Common/UIxFolderActions.m +++ b/UI/Common/UIxFolderActions.m @@ -21,6 +21,7 @@ */ #import +#import #import #import #import @@ -32,10 +33,13 @@ #import #import #import +#import #import #import +#import #import +#import #import #import @@ -219,4 +223,135 @@ return response; } +- (NSException*) _moveContacts: (NSArray*) contactsId + toFolder: (NSString*) destinationFolderId + andKeepCopy: (BOOL) keepCopy +{ + NSEnumerator *uids; + NSException *ex; + NSString *uid; + SOGoContentObject *currentChild; + SOGoGCSFolder *sourceFolder, *destinationFolder; + SOGoParentFolder *folders; + SoSecurityManager *sm; + WORequest *request; + unsigned int errorCount; + + sm = [SoSecurityManager sharedSecurityManager]; + request = [context request]; + ex = nil; + errorCount = 0; + + // Search the specified destination folder + sourceFolder = [self clientObject]; + folders = [sourceFolder container]; + destinationFolder = [folders lookupName: destinationFolderId + inContext: nil + acquire: NO]; + if (destinationFolder) + { + // Verify write access to the folder + ex = [sm validatePermission: SoPerm_AddDocumentsImagesAndFiles + onObject: destinationFolder + inContext: context]; + if (!ex) + { + uids = [contactsId objectEnumerator]; + while ((uid = [uids nextObject])) + { + // Search the currentChild ID + currentChild = [sourceFolder lookupName: uid + inContext: [self context] + acquire: NO]; + if ([currentChild isKindOfClass: [NSException class]]) + errorCount++; + else + { + if (keepCopy) + ex = [currentChild copyToFolder: destinationFolder]; + else + ex = [currentChild moveToFolder: destinationFolder]; + if (ex) + errorCount++; + } + } + } + } + else + ex = [NSException exceptionWithName: @"UnknownDestinationFolder" + reason: @"Unknown Destination Folder" + userInfo: nil]; + + if (errorCount > 0) + // At least one currentChild 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; +} + +- (id ) copyAction +{ + WORequest *request; + id 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 ) moveAction +{ + WORequest *request; + id 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 diff --git a/UI/Common/product.plist b/UI/Common/product.plist index f807824de..cf718f117 100644 --- a/UI/Common/product.plist +++ b/UI/Common/product.plist @@ -116,6 +116,16 @@ actionClass = "UIxFolderActions"; actionName = "batchDelete"; }; + copy = { + protectedBy = "Access Contents Information"; + actionClass = "UIxFolderActions"; + actionName = "copy"; + }; + move = { + protectedBy = "Delete Objects"; + actionClass = "UIxFolderActions"; + actionName = "move"; + }; }; }; }; diff --git a/UI/Contacts/GNUmakefile b/UI/Contacts/GNUmakefile index fee01c1cc..cb22e9f3b 100644 --- a/UI/Contacts/GNUmakefile +++ b/UI/Contacts/GNUmakefile @@ -21,8 +21,7 @@ ContactsUI_OBJC_FILES = \ UIxListEditor.m \ UIxContactsListView.m \ UIxContactsListViewContainer.m \ - UIxContactFoldersView.m \ - UIxContactFolderActions.m \ + UIxContactFoldersView.m ContactsUI_RESOURCE_FILES += \ Version \ diff --git a/UI/Contacts/UIxContactFolderActions.h b/UI/Contacts/UIxContactFolderActions.h deleted file mode 100644 index 0a915e0f6..000000000 --- a/UI/Contacts/UIxContactFolderActions.h +++ /dev/null @@ -1,39 +0,0 @@ -/* UIxContactFolderActions.h - this file is part of SOGo - * - * Copyright (C) 2008 Inverse groupe conseil - * - * Author: Francis Lachapelle - * - * This file is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * This file is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#ifndef UIXCONTACTFOLDERACTIONS_H -#define UIXCONTACTFOLDERACTIONS_H - -#import - -@class WOResponse; - -@interface UIxContactFolderActions : WODirectAction -{ -} - -- (id ) copyAction; -- (id ) moveAction; - -@end - -#endif /* UIXCONTACTFOLDERACTIONS_H */ diff --git a/UI/Contacts/UIxContactFolderActions.m b/UI/Contacts/UIxContactFolderActions.m deleted file mode 100644 index 91444c0f5..000000000 --- a/UI/Contacts/UIxContactFolderActions.m +++ /dev/null @@ -1,205 +0,0 @@ -/* UIxContactFolderActions.m - this file is part of SOGo - * - * Copyright (C) 2008 Inverse groupe conseil - * - * Author: Francis Lachapelle - * - * This file is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * This file is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#import -#import -#import - -#import -#import -#import -#import -#import -#import - -#import - -#import -#import - -#import -#import -#import -#import -#import -#import - -#import "../Common/WODirectAction+SOGo.h" - -#import "UIxContactFolderActions.h" - -@implementation UIxContactFolderActions - -- (NSException*) _moveContacts: (NSArray*) contactsId - toFolder: (NSString*) destinationFolderId - andKeepCopy: (BOOL) keepCopy -{ - NGVCard *card; - NSEnumerator *uids; - NSException *ex; - NSString *uid, *newUid; - id 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; - - // 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) - { - // Verify write access to the folder - ex = [sm validatePermission: SoPerm_AddDocumentsImagesAndFiles - onObject: destinationFolder - inContext: context]; - if (ex == nil) - { - uids = [contactsId objectEnumerator]; - uid = [uids nextObject]; - - while (uid) - { - // Search the contact ID - contact = [sourceFolder lookupName: uid - inContext: [self context] - acquire: NO]; - if ([(NSObject*)contact isKindOfClass: [NSException class]]) - errorCount++; - else - { - card = [contact vCard]; - - 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]; - - 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; -} - -- (id ) copyAction -{ - WORequest *request; - id 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 ) moveAction -{ - WORequest *request; - id 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 diff --git a/UI/Contacts/product.plist b/UI/Contacts/product.plist index 7cd004d77..2870c1ee8 100644 --- a/UI/Contacts/product.plist +++ b/UI/Contacts/product.plist @@ -96,16 +96,6 @@ pageName = "UIxContactsUserRightsEditor"; actionName = "saveUserRights"; }; - copy = { - protectedBy = "Access Contents Information"; - actionClass = "UIxContactFolderActions"; - actionName = "copy"; - }; - move = { - protectedBy = "Delete Objects"; - actionClass = "UIxContactFolderActions"; - actionName = "move"; - }; }; }; @@ -137,9 +127,9 @@ actionName = "canAccessContent"; }; copy = { - protectedBy = "Access Contents Information"; - actionClass = "UIxContactFolderActions"; - actionName = "copy"; + protectedBy = "Access Contents Information"; + actionClass = "UIxFolderActions"; + actionName = "copy"; }; }; }; diff --git a/UI/Scheduler/UIxAppointmentEditor.m b/UI/Scheduler/UIxAppointmentEditor.m index 205df2a56..4a747391a 100644 --- a/UI/Scheduler/UIxAppointmentEditor.m +++ b/UI/Scheduler/UIxAppointmentEditor.m @@ -23,6 +23,8 @@ #include #import +#import +#import #import #import #import @@ -36,6 +38,7 @@ #import #import #import +#import #import #import @@ -230,7 +233,36 @@ - (id ) saveAction { - [[self clientObject] saveComponent: event]; + NSString *newCalendar; + SOGoAppointmentFolder *thisFolder, *newFolder; + SOGoAppointmentFolders *parentFolder; + SOGoAppointmentObject *co; + SoSecurityManager *sm; + NSException *ex; + + co = [self clientObject]; + [co saveComponent: event]; + + newCalendar = [self queryParameterForKey: @"moveToCalendar"]; + if ([newCalendar length]) + { + sm = [SoSecurityManager sharedSecurityManager]; + + thisFolder = [co container]; + if (![sm validatePermission: SoPerm_DeleteObjects + onObject: thisFolder + inContext: context]) + { + parentFolder = [[self container] container]; + newFolder = [[thisFolder container] lookupName: newCalendar + inContext: context + acquire: NO]; + if (![sm validatePermission: SoPerm_AddDocumentsImagesAndFiles + onObject: newFolder + inContext: context]) + ex = [co moveToFolder: newFolder]; + } + } return [self jsCloseWithRefreshMethod: @"refreshEventsAndDisplay()"]; } diff --git a/UI/Scheduler/UIxTaskEditor.m b/UI/Scheduler/UIxTaskEditor.m index 693223d30..71632e451 100644 --- a/UI/Scheduler/UIxTaskEditor.m +++ b/UI/Scheduler/UIxTaskEditor.m @@ -21,6 +21,8 @@ */ #import +#import +#import #import #import #import @@ -314,9 +316,39 @@ return result; } +#warning this method could be replaced with a method common with UIxAppointmentEditor... - (id ) saveAction { - [[self clientObject] saveComponent: todo]; + NSString *newCalendar; + SOGoAppointmentFolder *thisFolder, *newFolder; + SOGoAppointmentFolders *parentFolder; + SOGoTaskObject *co; + SoSecurityManager *sm; + NSException *ex; + + co = [self clientObject]; + [co saveComponent: todo]; + + newCalendar = [self queryParameterForKey: @"moveToCalendar"]; + if ([newCalendar length]) + { + sm = [SoSecurityManager sharedSecurityManager]; + + thisFolder = [co container]; + if (![sm validatePermission: SoPerm_DeleteObjects + onObject: thisFolder + inContext: context]) + { + parentFolder = [[self container] container]; + newFolder = [[thisFolder container] lookupName: newCalendar + inContext: context + acquire: NO]; + if (![sm validatePermission: SoPerm_AddDocumentsImagesAndFiles + onObject: newFolder + inContext: context]) + ex = [co moveToFolder: newFolder]; + } + } return [self jsCloseWithRefreshMethod: @"refreshTasks()"]; } diff --git a/UI/WebServerResources/UIxComponentEditor.js b/UI/WebServerResources/UIxComponentEditor.js index c50562371..89a72760b 100644 --- a/UI/WebServerResources/UIxComponentEditor.js +++ b/UI/WebServerResources/UIxComponentEditor.js @@ -70,10 +70,18 @@ function onMenuSetClassification(event) { function onChangeCalendar(event) { var calendars = $("calendarFoldersList").value.split(","); var form = document.forms["editform"]; - var urlElems = form.getAttribute("action").split("/"); + var urlElems = form.getAttribute("action").split("?"); var choice = calendars[this.value]; - urlElems[urlElems.length-3] = choice; - form.setAttribute("action", urlElems.join("/")); + var urlParam = "moveToCalendar=" + choice; + if (urlElems.length == 1) + urlElems.push(urlParam); + else + urlElems[2] = urlParam; + + while (urlElems.length > 2) + urlElems.pop(); + + form.setAttribute("action", urlElems.join("?")); } function initializeDocumentHref() { @@ -111,9 +119,7 @@ function onComponentEditorLoad(event) { initializeDocumentHref(); initializePrivacyMenu(); var list = $("calendarList"); - list.observe("mousedown", - onChangeCalendar.bindAsEventListener(list), - false); + list.observe("change", onChangeCalendar, false); list.fire("mousedown"); var menuItems = $("itemPrivacyList").childNodesWithTag("li");