@Override
    public BulkActionResult copy(final List<ExplorerNode> explorerNodes,
                                 final ExplorerNode destinationFolder,
                                 final boolean allowRename,
                                 final String docName,
                                 final PermissionInheritance permissionInheritance) {
        final StringBuilder resultMessage = new StringBuilder();

        // Create a map to store source to destination document references.
        final Map<ExplorerNode, ExplorerNode> remappings = new HashMap<>();

        // Discover all affected folder children.
        final Map<ExplorerNode, List<ExplorerNode>> childMap = new HashMap<>();
        createChildMap(explorerNodes, childMap);

        // Perform a copy on the selected items.
        explorerNodes.forEach(sourceNode ->
                copy(sourceNode,
                        destinationFolder,
                        allowRename,
                        docName,
                        permissionInheritance,
                        resultMessage,
                        remappings));

        // Recursively copy any selected folders.
        explorerNodes.forEach(sourceNode ->
                recurseCopy(sourceNode,
                        permissionInheritance,
                        resultMessage,
                        remappings,
                        childMap));

        // Remap all dependencies for the copied items.
        remappings.values().forEach(newExplorerNode -> {
            final ExplorerActionHandler handler = explorerActionHandlers.getHandler(newExplorerNode.getType());
            if (handler != null) {
                final HashMap<DocRef, DocRef> docRefRemappings = new HashMap<>();
                for (final var remapping : remappings.entrySet()) {
                    docRefRemappings.put(remapping.getKey().getDocRef(), remapping.getValue().getDocRef());
                }
                handler.remapDependencies(newExplorerNode.getDocRef(), docRefRemappings);
            }
        });

        // Make sure the tree model is rebuilt.
        remappings.values().forEach(newNode -> {
            // Although the copy above will have fired entity events, they were before the deps
            // get re-mapped. Thus, we need to let the exp tree know that deps may have changed
            EntityEvent.fire(entityEventBus, newNode.getDocRef(), EntityAction.CREATE_EXPLORER_NODE);

            // Tell listeners that the nodes are in the explorer tree
            EntityEvent.fire(entityEventBus, newNode.getDocRef(), EntityAction.POST_CREATE);
        });

        return new BulkActionResult(new ArrayList<>(remappings.values()), resultMessage.toString());
    }
Returns the DocRef of a destination folder

@param destinationFolder If null, looks up the default root node
stroom-explorer/stroom-explorer-impl/src/main/java/stroom/explorer/impl/ExplorerServiceImpl.java