Don't allow duplicate GCS folder names

pull/229/merge
Francis Lachapelle 2018-08-30 22:09:17 -04:00
parent 18df3bafb5
commit 3bdd90e2be
7 changed files with 75 additions and 33 deletions

View File

@ -614,19 +614,21 @@ static NSArray *childRecordFields = nil;
NSURL *folderLocation; NSURL *folderLocation;
NSString *sql; NSString *sql;
if ([[self container] hasLocalSubFolderNamed: newName])
[NSException raise: NSInvalidArgumentException
format: @"That name already exists"];
cm = [GCSChannelManager defaultChannelManager]; cm = [GCSChannelManager defaultChannelManager];
folderLocation folderLocation = [[GCSFolderManager defaultFolderManager] folderInfoLocation];
= [[GCSFolderManager defaultFolderManager] folderInfoLocation];
fc = [cm acquireOpenChannelForURL: folderLocation]; fc = [cm acquireOpenChannelForURL: folderLocation];
if (fc) if (fc)
{ {
#warning GDLContentStore should provide methods for renaming folders #warning GDLContentStore should provide methods for renaming folders
sql sql = [NSString stringWithFormat: (@"UPDATE %@ SET c_foldername = '%@'"
= [NSString stringWithFormat: (@"UPDATE %@ SET c_foldername = '%@'" @" WHERE c_path = '%@'"),
@" WHERE c_path = '%@'"), [folderLocation gcsTableName],
[folderLocation gcsTableName], [newName stringByReplacingString: @"'" withString: @"''"],
[newName stringByReplacingString: @"'" withString: @"''"], ocsPath];
ocsPath];
[fc evaluateExpressionX: sql]; [fc evaluateExpressionX: sql];
[cm releaseChannel: fc]; [cm releaseChannel: fc];
// sql = [sql stringByAppendingFormat:@" WHERE %@ = '%@'", // sql = [sql stringByAppendingFormat:@" WHERE %@ = '%@'",

View File

@ -289,8 +289,18 @@
if ([folderName length] > 0) if ([folderName length] > 0)
{ {
clientObject = [self clientObject]; clientObject = [self clientObject];
[clientObject renameTo: folderName]; NS_DURING
response = [self responseWith204]; {
[clientObject renameTo: folderName];
response = [self responseWith204];
}
NS_HANDLER
{
message = [NSDictionary dictionaryWithObject: [localException reason] forKey: @"message"];
response = [self responseWithStatus: 409 /* Conflict */
andString: [message jsonRepresentation]];
}
NS_ENDHANDLER;
} }
else else
{ {

View File

@ -1,6 +1,6 @@
/* UIxContactFolderProperties.m - this file is part of SOGo /* UIxContactFolderProperties.m - this file is part of SOGo
* *
* Copyright (C) 2015 Inverse inc. * Copyright (C) 2015-2018 Inverse inc.
* *
* This file is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@ -21,8 +21,10 @@
#import <Foundation/NSDictionary.h> #import <Foundation/NSDictionary.h>
#import <Foundation/NSValue.h> #import <Foundation/NSValue.h>
#import <NGObjWeb/NSException+HTTP.h>
#import <NGObjWeb/WORequest.h> #import <NGObjWeb/WORequest.h>
#import <SOGo/NSDictionary+Utilities.h>
#import <SOGo/NSString+Utilities.h> #import <SOGo/NSString+Utilities.h>
#import <Contacts/SOGoContactGCSFolder.h> #import <Contacts/SOGoContactGCSFolder.h>
@ -59,21 +61,34 @@
- (WOResponse *) savePropertiesAction - (WOResponse *) savePropertiesAction
{ {
WORequest *request; WORequest *request;
NSDictionary *params; WOResponse *response;
NSDictionary *params, *message;
id o; id o;
request = [context request]; request = [context request];
params = [[request contentAsString] objectFromJSONString]; params = [[request contentAsString] objectFromJSONString];
response = [self responseWith204];
o = [params objectForKey: @"name"]; NS_DURING
if ([o isKindOfClass: [NSString class]]) {
[addressbook renameTo: o]; o = [params objectForKey: @"name"];
if ([o isKindOfClass: [NSString class]])
[addressbook renameTo: o];
o = [params objectForKey: @"synchronize"]; o = [params objectForKey: @"synchronize"];
if ([o isKindOfClass: [NSNumber class]]) if ([o isKindOfClass: [NSNumber class]])
[addressbook setSynchronize: [o boolValue]]; [addressbook setSynchronize: [o boolValue]];
}
NS_HANDLER
{
message = [NSDictionary dictionaryWithObject: [localException reason] forKey: @"message"];
response = [self responseWithStatus: 400 /* Bad Request */
andString: [message jsonRepresentation]];
return [self responseWith204]; }
NS_ENDHANDLER;
return response;
} }
@end @end

View File

@ -581,15 +581,16 @@
* @returns a promise of the HTTP operation * @returns a promise of the HTTP operation
*/ */
AddressBook.prototype.$rename = function(name) { AddressBook.prototype.$rename = function(name) {
var i, list; var _this = this, i, list;
list = this.isSubscription? AddressBook.$subscriptions : AddressBook.$addressbooks; list = this.isSubscription? AddressBook.$subscriptions : AddressBook.$addressbooks;
i = _.indexOf(_.map(list, 'id'), this.id); i = _.indexOf(_.map(list, 'id'), this.id);
this.name = name;
list.splice(i, 1);
AddressBook.$add(this);
return this.$save(); return this.$save().then(function() {
list.splice(i, 1);
_this.name = name;
AddressBook.$add(_this);
});
}; };
/** /**

View File

@ -17,6 +17,7 @@
vm.edit = edit; vm.edit = edit;
vm.revertEditing = revertEditing; vm.revertEditing = revertEditing;
vm.save = save; vm.save = save;
vm.saving = false;
vm.confirmDelete = confirmDelete; vm.confirmDelete = confirmDelete;
vm.importCards = importCards; vm.importCards = importCards;
vm.showLinks = showLinks; vm.showLinks = showLinks;
@ -98,13 +99,18 @@
function save(folder) { function save(folder) {
var name = folder.name; var name = folder.name;
if (name && name.length > 0) { if (!vm.saving && name && name.length > 0) {
if (name != vm.originalAddressbook.name) { if (name != vm.originalAddressbook.name) {
vm.saving = true;
folder.$rename(name) folder.$rename(name)
.then(function(data) { .then(function(data) {
vm.editMode = false; vm.editMode = false;
}, function(data, status) { }, function() {
Dialog.alert(l('Warning'), data); revertEditing(folder);
vm.editMode = folder.id;
})
.finally(function() {
vm.saving = false;
}); });
} }
else { else {

View File

@ -498,18 +498,20 @@
* @returns a promise of the HTTP operation * @returns a promise of the HTTP operation
*/ */
Calendar.prototype.$save = function() { Calendar.prototype.$save = function() {
var _this = this; var _this = this,
d = Calendar.$q.defer();
return Calendar.$$resource.save(this.id, this.$omit()).then(function(data) { Calendar.$$resource.save(this.id, this.$omit()).then(function(data) {
// Make a copy of the data for an eventual reset // Make a copy of the data for an eventual reset
_this.$shadowData = _this.$omit(); _this.$shadowData = _this.$omit();
return data; return d.resolve(data);
}, function(data) { }, function(data) {
Calendar.$log.error(JSON.stringify(data, undefined, 2));
// Restore previous version // Restore previous version
_this.$reset(); _this.$reset();
return data; return d.reject(data);
}); });
return d.promise;
}; };
/** /**

View File

@ -87,7 +87,7 @@
this.inputElement.value = this.calendar.name; this.inputElement.value = this.calendar.name;
this.clickableElement.classList.add('ng-hide'); this.clickableElement.classList.add('ng-hide');
this.inputContainer.classList.remove('ng-hide'); this.inputContainer.classList.remove('ng-hide');
if ($event.srcEvent.type == 'touchend') { if ($event.srcEvent && $event.srcEvent.type == 'touchend') {
$timeout(function() { $timeout(function() {
$ctrl.inputElement.focus(); $ctrl.inputElement.focus();
$ctrl.inputElement.select(); $ctrl.inputElement.select();
@ -115,6 +115,12 @@
$ctrl.inputContainer.classList.add('ng-hide'); $ctrl.inputContainer.classList.add('ng-hide');
$ctrl.clickableElement.classList.remove('ng-hide'); $ctrl.clickableElement.classList.remove('ng-hide');
$ctrl.updateCalendarName(); $ctrl.updateCalendarName();
}, function() {
$ctrl.editMode = true;
$timeout(function() {
$ctrl.inputElement.focus();
$ctrl.inputElement.select();
}, 200); // delayed focus for iOS
}) })
.finally(function() { .finally(function() {
$ctrl.inputElement.disabled = false; $ctrl.inputElement.disabled = false;