Skip to content

Commit

Permalink
(web) New menu option to move mailboxes
Browse files Browse the repository at this point in the history
Fixes #644
Fixes #3511
Fixes #4479
  • Loading branch information
cgx committed Jul 27, 2018
1 parent 7e75c8b commit 894769e
Show file tree
Hide file tree
Showing 5 changed files with 177 additions and 2 deletions.
3 changes: 3 additions & 0 deletions NEWS
@@ -1,6 +1,9 @@
4.x.x (2018-xx-xx)
------------------

New features
- [web] move mailboxes (#644, #3511, #4479)

Bug fixes
- [core] handle multi-valued mozillasecondemail attribute mapping

Expand Down
106 changes: 105 additions & 1 deletion UI/MailerUI/UIxMailFolderActions.m
@@ -1,6 +1,6 @@
/* UIxMailFolderActions.m - this file is part of SOGo
*
* Copyright (C) 2007-2017 Inverse inc.
* Copyright (C) 2007-2018 Inverse inc.
*
* 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
Expand Down Expand Up @@ -32,6 +32,7 @@
#import <Mailer/SOGoMailAccount.h>
#import <Mailer/SOGoTrashFolder.h>

#import <SOGo/NSArray+Utilities.h>
#import <SOGo/NSObject+Utilities.h>
#import <SOGo/NSString+Utilities.h>
#import <SOGo/SOGoDomainDefaults.h>
Expand Down Expand Up @@ -183,6 +184,109 @@ - (WOResponse *) renameFolderAction
return response;
}

/**
* @api {post} /so/:username/Mail/:accountId/:mailboxPath/renameFolder Rename mailbox
* @apiVersion 1.0.0
* @apiName PostRenameFolder
* @apiGroup Mail
*
* @apiParam {String} parent Name of the new parent mailbox
*
* @apiSuccess (Success 200) {String} path New mailbox path relative to account
* @apiSuccess (Success 200) {String} sievePath New mailbox path relative to account for Sieve script usage
* @apiError (Error 500) {Object} error The error message
*/
- (WOResponse *) moveFolderAction
{
SOGoMailFolder *co;
SOGoUserSettings *us;
WORequest *request;
WOResponse *response;
NSException *error;
NSString *newParentPath, *newFolderPath, *sievePath, *currentMailbox, *currentAccount,
*keyForMsgUIDs, *newKeyForMsgUIDs;
NSMutableDictionary *params, *moduleSettings, *threadsCollapsed, *message;
NSArray *currentComponents, *newComponents, *values;
int count;

co = [self clientObject];

// Prepare the variables need to verify if the current folder have any collapsed threads saved in userSettings
us = [[context activeUser] userSettings];
moduleSettings = [us objectForKey: @"Mail"];
threadsCollapsed = [moduleSettings objectForKey:@"threadsCollapsed"];

// Retrieve new folder name from JSON payload
request = [context request];
params = [[request contentAsString] objectFromJSONString];
newParentPath = [params objectForKey: @"parent"]; // encoded parent path (ex: "Travail/Employ&AOk-s")

if (!newParentPath || [newParentPath length] == 0)
{
message = [NSDictionary dictionaryWithObject: [self labelForKey: @"Missing parent parameter" inContext: context]
forKey: @"message"];
response = [self responseWithStatus: 500 andJSONRepresentation: message];
}
else
{
newFolderPath = [NSString stringWithFormat:@"/%@/%@",
newParentPath,
[[co imap4URL] lastPathComponent]];
error = [co renameTo: newFolderPath];
if (error)
{
message = [NSDictionary dictionaryWithObject: [self labelForKey: @"Unable to move folder." inContext: context]
forKey: @"message"];
response = [self responseWithStatus: 500 andJSONRepresentation: message];
}
else
{
// Build lookup key for current mailbox path
currentComponents = [[co imap4URL] pathComponents];
count = [currentComponents count];
currentComponents = [[currentComponents subarrayWithRange: NSMakeRange(1,count-1)]
resultsOfSelector: @selector (asCSSIdentifier)];
currentComponents = [currentComponents stringsWithFormat: @"folder%@"];
currentAccount = [[co mailAccountFolder] nameInContainer]; // integer (ex: 0)
keyForMsgUIDs = [NSString stringWithFormat:@"/%@/%@", currentAccount,
[currentComponents componentsJoinedByString: @"/"]];

// Build lookup key for new mailbox path
newComponents = [newParentPath pathComponents];
newComponents = [newComponents resultsOfSelector: @selector (asCSSIdentifier)];
newComponents = [newComponents stringsWithFormat: @"folder%@"];
currentMailbox = [NSString stringWithFormat: @"folder%@", [[[co imap4URL] lastPathComponent] asCSSIdentifier]];
newKeyForMsgUIDs = [NSString stringWithFormat:@"/%@/%@/%@", currentAccount,
[newComponents componentsJoinedByString: @"/"], currentMailbox];

// Verify if the current folder have any collapsed threads save under it old name and adjust the folderName
if (threadsCollapsed)
{
if ([threadsCollapsed objectForKey: keyForMsgUIDs])
{
values = [NSArray arrayWithArray:[threadsCollapsed objectForKey:keyForMsgUIDs]];
[threadsCollapsed setObject:values forKey:newKeyForMsgUIDs];
[threadsCollapsed removeObjectForKey:keyForMsgUIDs];
[us synchronize];
}
}
newFolderPath = [newFolderPath substringFromIndex: 1]; // remove slash at beginning of path

NSString *sieveFolderEncoding = [[SOGoSystemDefaults sharedSystemDefaults] sieveFolderEncoding];
if ([sieveFolderEncoding isEqualToString: @"UTF-8"])
sievePath = [newFolderPath stringByDecodingImap4FolderName];
else
sievePath = newFolderPath;

message = [NSDictionary dictionaryWithObjectsAndKeys:
newFolderPath, @"path", sievePath, @"sievePath", nil];
response = [self responseWithStatus: 200 andJSONRepresentation: message];
}
}

return response;
}

- (NSURL *) _trashedURLOfFolder: (NSURL *) srcURL
withObject: (SOGoMailFolder *) co
{
Expand Down
22 changes: 22 additions & 0 deletions UI/Templates/MailerUI/UIxMailMainFrame.wox
Expand Up @@ -201,6 +201,28 @@
<var:string label:value="Rename"/>
</md-button>
</md-menu-item>
<md-menu-item ng-show="::$menuCtrl.folder.$isEditable">
<md-menu md-position-mode="cascade target">
<md-button label:aria-label="Move To" ng-click="$mdMenu.open($event)">
<var:string label:value="Move To"/>
</md-button>
<md-menu-content class="md-dense" width="3">
<div ng-repeat="folder in
$menuCtrl.folder.$account.$flattenMailboxes({ all: true })
track by folder.path">
<md-menu-item>
<md-button class="sg-no-wrap"
aria-label="{{folder.$displayName}}"
ng-disabled="$menuCtrl.isParentOf(folder.path)"
ng-click="$menuCtrl.moveFolder(folder.path)">
<span ng-class="::('sg-child-level-' + folder.level)"
ng-bind="folder.$displayName"><!-- mailbox name --></span>
</md-button>
</md-menu-item>
</div>
</md-menu-content>
</md-menu>
</md-menu-item>
<md-menu-item>
<md-button type="button" ng-click="$menuCtrl.compactFolder()">
<var:string label:value="Compact"/>
Expand Down
17 changes: 16 additions & 1 deletion UI/WebServerResources/js/Mailer/Mailbox.service.js
Expand Up @@ -485,7 +485,7 @@
/**
* @function $rename
* @memberof AddressBook.prototype
* @desc Rename the addressbook and keep the list sorted
* @desc Rename the mailbox and keep the list sorted
* @param {string} name - the new name
* @returns a promise of the HTTP operation
*/
Expand Down Expand Up @@ -827,6 +827,21 @@
this.$shadowData = this.$omit();
};

/**
* @function $move
* @memberof Mailbox.prototype
* @desc Move the mailbox to a different parent. Will reload the mailboxes list.
* @returns a promise of the HTTP operation
*/
Mailbox.prototype.$move = function(parentPath) {
var _this = this;

return Mailbox.$$resource.post(this.id, 'move', {parent: parentPath}).finally(function() {
_this.$account.$getMailboxes({reload: true});
return true;
});
};

/**
* @function $save
* @memberof Mailbox.prototype
Expand Down
31 changes: 31 additions & 0 deletions UI/WebServerResources/js/Mailer/sgMailboxListItem.directive.js
Expand Up @@ -296,6 +296,37 @@
});
};

this.isParentOf = function(path) {
var findChildren;

// Local recursive function
findChildren = function(parent) {
if (parent.children && parent.children.length > 0) {
for (var i = 0, found = false; !found && i < parent.children.length; i++) {
var o = parent.children[i];
if (o.children && o.children.length > 0) {
if (findChildren(o)) {
return true;
}
}
else if (o.path == path) {
return true;
}
}
}
else {
return (parent.path == path);
}
};

return findChildren(this.folder);
};

this.moveFolder = function(path) {
this.folder.$move(path);
mdPanelRef.close();
};

} // MenuController


Expand Down

0 comments on commit 894769e

Please sign in to comment.