Merge pull request #1068 from dbeaver/feat/CB-2302/dnd-folders

CB-2302 DnD folders
This commit is contained in:
Alexey
2022-08-29 16:04:30 +03:00
committed by GitHub
7 changed files with 73 additions and 17 deletions
@@ -21,6 +21,10 @@ import io.cloudbeaver.model.WebConnectionFolderInfo;
import io.cloudbeaver.model.session.WebSession;
import org.jkiss.code.Nullable;
import org.jkiss.dbeaver.model.DBPDataSourceFolder;
import org.jkiss.dbeaver.model.navigator.DBNLocalFolder;
import org.jkiss.dbeaver.model.navigator.DBNNode;
import org.jkiss.dbeaver.model.navigator.DBNProject;
import org.jkiss.dbeaver.model.navigator.DBNRoot;
import org.jkiss.utils.CommonUtils;
public class WebConnectionFolderUtils {
@@ -44,4 +48,14 @@ public class WebConnectionFolderUtils {
throw new DBWebException("Folder name '" + folderName + "' contains illegal characters: /");
}
}
public static DBPDataSourceFolder getParentFolder(DBNNode folderNode) throws DBWebException {
if (folderNode instanceof DBNRoot || folderNode instanceof DBNProject) {
return null;
} else if (folderNode instanceof DBNLocalFolder) {
return ((DBNLocalFolder) folderNode).getFolder();
} else {
throw new DBWebException("Navigator node '" + folderNode.getNodeItemPath() + "' is not a folder node");
}
}
}
@@ -466,17 +466,22 @@ public class WebServiceNavigator implements DBWServiceNavigator {
}
checkProjectEditAccess(node, session);
if (node instanceof DBNDataSource) {
DBPDataSourceFolder folder;
if (folderNode instanceof DBNRoot || folderNode instanceof DBNProject) {
folder = null;
} else if (folderNode instanceof DBNLocalFolder) {
folder = ((DBNLocalFolder) folderNode).getFolder();
} else {
throw new DBWebException("Navigator node '" + folderNodePath + "' is not a folder node");
}
DBPDataSourceFolder folder = WebConnectionFolderUtils.getParentFolder(folderNode);
((DBNDataSource) node).moveToFolder(folderNode.getOwnerProject(), folder);
node.getOwnerProject().getDataSourceRegistry().updateDataSource(
((DBNDataSource) node).getDataSourceContainer());
} else if (node instanceof DBNLocalFolder) {
DBPDataSourceFolder folder = WebConnectionFolderUtils.getParentFolder(folderNode);
if (folder != null) {
List<String> siblings = Arrays.stream(folder.getChildren())
.map(DBPDataSourceFolder::getName)
.collect(Collectors.toList());
if (siblings.contains(node.getName())) {
throw new DBWebException("Node " + folderNodePath + " contains folder with name '" + node.getName() + "'");
}
}
((DBNLocalFolder) node).getFolder().setParent(folder);
WebServiceUtils.updateConfigAndRefreshDatabases(session, node.getOwnerProject().getId());
} else if (node instanceof DBNResourceManagerResource) {
boolean rmNewNode = folderNode instanceof DBNAbstractResourceManagerNode;
DBNResourceManagerResource rmOldNode = (DBNResourceManagerResource) node;
@@ -155,22 +155,30 @@ export class ConnectionFoldersBootstrap extends Bootstrap {
}: INodeMoveData,
contexts: IExecutionContextProvider<INodeMoveData>
) {
if (![NAV_NODE_TYPE_PROJECT, NAV_NODE_TYPE_FOLDER].includes(targetNode.nodeType!)) {
return;
}
await this.projectsResource.load(CachedMapAllKey);
const move = contexts.getContext(navNodeMoveContext);
const nodes = getNodesFromContext(moveContexts);
const nodeIdList = nodes.map(node => node.id);
const children = this.navTreeResource.get(targetNode.id);
const children = this.navTreeResource.get(targetNode.id) ?? [];
const targetProject = this.projectsNavNodeService.getProject(targetNode.id);
const supported = (
[NAV_NODE_TYPE_PROJECT, NAV_NODE_TYPE_FOLDER].includes(targetNode.nodeType!)
&& nodes.every(node => (
node.nodeType === NAV_NODE_TYPE_CONNECTION
&& targetProject === this.projectsNavNodeService.getProject(node.id)
&& !children?.includes(node.id)
))
);
const supported = nodes.every(node => {
if (
![NAV_NODE_TYPE_CONNECTION, NAV_NODE_TYPE_FOLDER, NAV_NODE_TYPE_PROJECT].includes(node.nodeType!)
|| targetProject !== this.projectsNavNodeService.getProject(node.id)
|| children.includes(node.id)
|| targetNode.id === node.id
) {
return false;
}
return true;
});
if (!supported) {
return;
@@ -181,6 +189,31 @@ export class ConnectionFoldersBootstrap extends Bootstrap {
move.setCanMove(true);
}
} else {
const childrenNode = this.navNodeInfoResource.get(resourceKeyList(children));
const folderDuplicates = nodes.filter(node => (
node.nodeType === NAV_NODE_TYPE_FOLDER
&& (
childrenNode.some(child => child?.nodeType === NAV_NODE_TYPE_FOLDER && child.name === node.name)
|| nodes.some(child => (
child.nodeType === NAV_NODE_TYPE_FOLDER
&& child.name === node.name
&& child.id !== node.id
))
)
));
if (folderDuplicates.length > 0) {
this.notificationService.logError({
title: 'connections_public_connection_folder_move_failed',
message: this.localizationService.translate(
'connections_public_connection_folder_move_duplication',
undefined,
{ name: folderDuplicates.map(node => `"${node.name}"`).join(', ') }
),
});
return;
}
try {
await this.navTreeResource.moveTo(resourceKeyList(nodeIdList), targetNode.id);
const connections = nodeIdList
@@ -6,5 +6,6 @@ export default [
['connections_public_connection_edit_reconnect_message', 'Connection has been updated. Do you want to reconnect?'],
['connections_public_connection_edit_reconnect_failed', 'Failed to reconnect'],
['connections_public_connection_folder_move_failed', 'Failed to move to folder'],
['connections_public_connection_folder_move_duplication', 'Target folder or selected folders contains folder with the same name ({args.name})'],
['connections_public_connection_folder_delete_confirmation', 'You\'re going to delete "{args.name}". Connections won\'t be deleted. Are you sure?'],
];
@@ -6,5 +6,6 @@ export default [
['connections_public_connection_edit_reconnect_message', 'Connection has been updated. Do you want to reconnect?'],
['connections_public_connection_edit_reconnect_failed', 'Failed to reconnect'],
['connections_public_connection_folder_move_failed', 'Failed to move to folder'],
['connections_public_connection_folder_move_duplication', 'Target folder or selected folders contains folder with the same name ({args.name})'],
['connections_public_connection_folder_delete_confirmation', 'You\'re going to delete "{args.name}". Connections won\'t be deleted. Are you sure?'],
];
@@ -6,5 +6,6 @@ export default [
['connections_public_connection_edit_reconnect_message', 'Подключение было обновлено. Вы хотите переподключиться?'],
['connections_public_connection_edit_reconnect_failed', 'Не удалось переподключиться'],
['connections_public_connection_folder_move_failed', 'Ошибка перемещения в папку'],
['connections_public_connection_folder_move_duplication', 'Выбранные папки или папка назначения содержит папки с таким же названием ({args.name})'],
['connections_public_connection_folder_delete_confirmation', 'Вы удаляете "{args.name}". Подключения не будут удалены. Вы уверены?'],
];
@@ -6,5 +6,6 @@ export default [
['connections_public_connection_edit_reconnect_message', '连接已更新。您想重新连接吗?'],
['connections_public_connection_edit_reconnect_failed', '重新连接失败'],
['connections_public_connection_folder_move_failed', 'Failed to move to folder'],
['connections_public_connection_folder_move_duplication', 'Target folder or selected folders contains folder with the same name ({args.name})'],
['connections_public_connection_folder_delete_confirmation', 'You\'re going to delete "{args.name}". Connections won\'t be deleted. Are you sure?'],
];