Commit f2417395 authored by Sonia Zorba's avatar Sonia Zorba
Browse files

Added modal for moving nodes

parent 336569fc
Loading
Loading
Loading
Loading
Loading
+20 −3
Original line number Diff line number Diff line
@@ -8,7 +8,8 @@ package it.inaf.ia2.vospace.ui.controller;
import it.inaf.ia2.aa.data.User;
import it.inaf.ia2.vospace.ui.client.VOSpaceClient;
import it.inaf.ia2.vospace.ui.data.ListNodeData;
import it.inaf.ia2.vospace.ui.service.NodesHtmlGenerator;
import it.inaf.ia2.vospace.ui.service.MainNodesHtmlGenerator;
import it.inaf.ia2.vospace.ui.service.MoveNodeModalHtmlGenerator;
import it.inaf.oats.vospace.datamodel.NodeUtils;
import java.util.List;
import java.util.Map;
@@ -33,6 +34,7 @@ import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
@@ -61,8 +63,23 @@ public class NodesController extends BaseController {

        listNodeData.setWritable(NodeUtils.checkIfWritable(node, principal.getName(), principal.getGroups()));

        NodesHtmlGenerator htmlGenerator = new NodesHtmlGenerator(node, principal, authority);
        listNodeData.setHtmlTable(htmlGenerator.generateNodes());
        MainNodesHtmlGenerator htmlGenerator = new MainNodesHtmlGenerator(node, principal, authority);
        listNodeData.setHtml(htmlGenerator.generateNodes());

        return ResponseEntity.ok(listNodeData);
    }

    @GetMapping(value = "/nodesForMove", produces = MediaType.APPLICATION_JSON_VALUE)
    public ResponseEntity<ListNodeData> listNodesForMoveModal(@RequestParam("path") String path, @RequestParam("nodeToMove") String nodeToMove, User principal) throws Exception {

        ListNodeData listNodeData = new ListNodeData();

        Node node = client.getNode(path);

        listNodeData.setWritable(NodeUtils.checkIfWritable(node, principal.getName(), principal.getGroups()));

        MoveNodeModalHtmlGenerator htmlGenerator = new MoveNodeModalHtmlGenerator(node, nodeToMove, principal, authority);
        listNodeData.setHtml(htmlGenerator.generateNodes());

        return ResponseEntity.ok(listNodeData);
    }
+5 −5
Original line number Diff line number Diff line
@@ -7,15 +7,15 @@ package it.inaf.ia2.vospace.ui.data;

public class ListNodeData {

    private String htmlTable;
    private String html;
    private boolean writable;

    public String getHtmlTable() {
        return htmlTable;
    public String getHtml() {
        return html;
    }

    public void setHtmlTable(String htmlTable) {
        this.htmlTable = htmlTable;
    public void setHtml(String html) {
        this.html = html;
    }

    public boolean isWritable() {
+195 −0
Original line number Diff line number Diff line
/*
 * This file is part of vospace-ui
 * Copyright (C) 2021 Istituto Nazionale di Astrofisica
 * SPDX-License-Identifier: GPL-3.0-or-later
 */
package it.inaf.ia2.vospace.ui.service;

import it.inaf.ia2.aa.data.User;
import static it.inaf.oats.vospace.datamodel.NodeUtils.urlEncodePath;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import net.ivoa.xml.vospace.v2.Node;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;

public class MainNodesHtmlGenerator extends NodesHtmlGenerator {

    public MainNodesHtmlGenerator(Node node, User user, String authority) {
        super(node, user, authority);
    }

    @Override
    protected Element createContainerElement(Document html) {
        Element container = html.body().appendElement("tbody");
        container.attr("id", "nodes");
        return container;
    }

    @Override
    protected void addChild(Node child, Element containerElement) {
        NodeInfo nodeInfo = new NodeInfo(child, user, authority);

        if (nodeInfo.isListOfFiles()) {
            // hidden file
            return;
        }

        Element row = containerElement.appendElement("tr");

        addSelectionCell(nodeInfo, row);
        addLinkCell(nodeInfo, row);
        addSizeCell(nodeInfo, row);
        addGroupReadCell(nodeInfo, row);
        addGroupWriteCell(nodeInfo, row);
        addActionsCell(nodeInfo, row);
    }

    private void addSelectionCell(NodeInfo nodeInfo, Element row) {

        Element cell = row.appendElement("td");

        Element input = cell.appendElement("input");
        input.attr("type", "checkbox");
        input.attr("data-node", nodeInfo.getPath());

        if (nodeInfo.isAsyncTrans()) {
            input.addClass("async");
        } else if (nodeInfo.isDeletable()) {
            input.addClass("deletable");
        }
    }

    private void addLinkCell(NodeInfo nodeInfo, Element row) {

        Element cell = row.appendElement("td");
        addNodeIcon(nodeInfo, cell);
        addLink(nodeInfo, cell);
    }

    private void addSizeCell(NodeInfo nodeInfo, Element row) {
        Element cell = row.appendElement("td");
        cell.text(nodeInfo.getSize());
    }

    private void addGroupReadCell(NodeInfo nodeInfo, Element row) {
        Element cell = row.appendElement("td");
        fillGroupCell(cell, nodeInfo.getGroupRead());
    }

    private void addGroupWriteCell(NodeInfo nodeInfo, Element row) {
        Element cell = row.appendElement("td");
        fillGroupCell(cell, nodeInfo.getGroupWrite());
    }

    private void fillGroupCell(Element cell, String groups) {
        String[] values = groups.split(" ");
        List<String> personGroups = new ArrayList<>();
        List<String> peopleGroups = new ArrayList<>();
        for (String value : values) {
            if (!value.isBlank()) {
                if (value.startsWith("people.")) {
                    personGroups.add(value.substring("people.".length()).replace("\\.", "."));
                } else {
                    peopleGroups.add(value);
                }
            }
        }
        if (!personGroups.isEmpty()) {
            Element personIcon = cell.appendElement("span");
            personIcon.attr("class", "icon person-icon");
            cell.appendText(String.join(" ", personGroups));
            cell.append("&nbsp;");
        }
        if (!peopleGroups.isEmpty()) {
            Element personIcon = cell.appendElement("span");
            personIcon.attr("class", "icon people-icon");
            cell.appendText(String.join(" ", peopleGroups));
        }
    }

    private void addActionsCell(NodeInfo nodeInfo, Element row) {
        Element cell = row.appendElement("td");

        Element dotsMenu = cell.appendElement("span");
        dotsMenu.attr("class", "dots-menu icon dots-menu-icon pointer");

        Element dropdown = dotsMenu.appendElement("span");
        dropdown.attr("class", "dots-menu-content dropdown-menu");

        String nodePathJs = makeJsArg(nodeInfo.getPath());

        if (nodeInfo.isWritable()) {
            Element shareBtn = dropdown.appendElement("button");
            shareBtn.text("Share");
            shareBtn.attr("type", "button");
            shareBtn.attr("class", "dropdown-item");
            shareBtn.attr("onclick", "shareNode(" + nodePathJs
                    + "," + makeJsArg(nodeInfo.getGroupRead())
                    + "," + makeJsArg(nodeInfo.getGroupWrite()) + ")");
        }
        if (nodeInfo.isDeletable()) {
            Element renameBtn = dropdown.appendElement("button");
            renameBtn.text("Rename");
            renameBtn.attr("type", "button");
            renameBtn.attr("class", "dropdown-item");
            renameBtn.attr("onclick", "renameNode(" + nodePathJs + ")");

            Element moveBtn = dropdown.appendElement("button");
            moveBtn.text("Move");
            moveBtn.attr("type", "button");
            moveBtn.attr("class", "dropdown-item");
            moveBtn.attr("onclick", "moveNode(" + nodePathJs + ")");

            Element deleteBtn = dropdown.appendElement("button");
            deleteBtn.text("Delete");
            deleteBtn.attr("type", "button");
            deleteBtn.attr("class", "dropdown-item");
            deleteBtn.attr("onclick", "deleteNode(" + nodePathJs + ")");
        }
    }

    private void addLink(NodeInfo nodeInfo, Element cell) {
        if (isDownloadable(nodeInfo, user)) {
            Element link = cell.appendElement("a");
            String href;
            if (nodeInfo.isFolder()) {
                href = "#/nodes" + urlEncodePath(nodeInfo.getPath());
            } else {
                href = "download" + urlEncodePath(nodeInfo.getPath());
                link.attr("target", "blank_");
            }
            link.attr("href", href);
            link.text(nodeInfo.getName());
        } else {
            cell.appendText(nodeInfo.getName());
        }
    }

    private boolean isDownloadable(NodeInfo nodeInfo, User user) {
        if (nodeInfo.isFile()) {
            if (nodeInfo.isAsyncTrans() || nodeInfo.isBusy()) {
                return false;
            }
        }
        if (nodeInfo.isPublic()) {
            return true;
        }

        if (nodeInfo.getCreator().equals(user.getName())) {
            return true;
        }

        if (user.getGroups() != null && !user.getGroups().isEmpty() && !nodeInfo.getGroupRead().isEmpty()) {
            List<String> groupRead = Arrays.asList(nodeInfo.getGroupRead().split(" "));
            for (String group : groupRead) {
                if (user.getGroups().contains(group)) {
                    return true;
                }
            }
        }

        return false;
    }
}
+51 −0
Original line number Diff line number Diff line
/*
 * This file is part of vospace-ui
 * Copyright (C) 2021 Istituto Nazionale di Astrofisica
 * SPDX-License-Identifier: GPL-3.0-or-later
 */
package it.inaf.ia2.vospace.ui.service;

import it.inaf.ia2.aa.data.User;
import net.ivoa.xml.vospace.v2.Node;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;

public class MoveNodeModalHtmlGenerator extends NodesHtmlGenerator {

    private final String nodeNodeMovePath;

    public MoveNodeModalHtmlGenerator(Node node, String nodeNodeMovePath, User user, String authority) {
        super(node, user, authority);
        this.nodeNodeMovePath = nodeNodeMovePath;
    }

    @Override
    protected Element createContainerElement(Document html) {
        Element container = html.body().appendElement("div");
        container.attr("id", "move-nodes");
        container.attr("class", "list-group");
        return container;
    }

    @Override
    protected void addChild(Node child, Element containerElement) {
        NodeInfo nodeInfo = new NodeInfo(child, user, authority);

        if (!nodeInfo.isFolder() || nodeInfo.getPath().equals(nodeNodeMovePath)) {
            return;
        }

        Element row = containerElement.appendElement("div");
        row.addClass("list-group-item");

        addNodeIcon(nodeInfo, row);
        addLink(nodeInfo, row);
    }

    private void addLink(NodeInfo nodeInfo, Element cell) {
        Element link = cell.appendElement("a");
        link.attr("href", "#");
        link.attr("onclick", "openNodeInMoveModal(event, " + makeJsArg(nodeInfo.getPath()) + ")");
        link.text(nodeInfo.getName());
    }
}
+18 −177
Original line number Diff line number Diff line
@@ -6,25 +6,22 @@
package it.inaf.ia2.vospace.ui.service;

import it.inaf.ia2.aa.data.User;
import static it.inaf.oats.vospace.datamodel.NodeUtils.urlEncodePath;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import net.ivoa.xml.vospace.v2.ContainerNode;
import net.ivoa.xml.vospace.v2.Node;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;

public class NodesHtmlGenerator {
public abstract class NodesHtmlGenerator {

    private final Node parentNode;
    private final User user;
    private final String authority;

    private Element tbody;
    protected final User user;
    protected final String authority;

    public NodesHtmlGenerator(Node node, User user, String authority) {
    private Element containerElement;

    protected NodesHtmlGenerator(Node node, User user, String authority) {
        this.parentNode = node;
        this.user = user;
        this.authority = authority;
@@ -34,143 +31,30 @@ public class NodesHtmlGenerator {

        Document html = Jsoup.parse("<html></html>");

        tbody = html.body().appendElement("tbody");
        tbody.attr("id", "nodes");
        containerElement = createContainerElement(html);
        
        if (parentNode instanceof ContainerNode) {
            ContainerNode folder = (ContainerNode) parentNode;
            for (Node child : folder.getNodes()) {
                addChild(child);
            }
        }

        return tbody.toString();
    }

    private void addChild(Node child) {
        NodeInfo nodeInfo = new NodeInfo(child, user, authority);

        if (nodeInfo.isListOfFiles()) {
            // hidden file
            return;
        }

        Element row = tbody.appendElement("tr");

        addSelectionCell(nodeInfo, row);
        addLinkCell(nodeInfo, row);
        addSizeCell(nodeInfo, row);
        addGroupReadCell(nodeInfo, row);
        addGroupWriteCell(nodeInfo, row);
        addActionsCell(nodeInfo, row);
    }

    private void addSelectionCell(NodeInfo nodeInfo, Element row) {

        Element cell = row.appendElement("td");

        Element input = cell.appendElement("input");
        input.attr("type", "checkbox");
        input.attr("data-node", nodeInfo.getPath());

        if (nodeInfo.isAsyncTrans()) {
            input.addClass("async");
        } else if (nodeInfo.isDeletable()) {
            input.addClass("deletable");
        }
    }

    private void addLinkCell(NodeInfo nodeInfo, Element row) {

        Element cell = row.appendElement("td");
        addNodeIcon(nodeInfo, cell);
        addLink(nodeInfo, cell);
    }

    private void addSizeCell(NodeInfo nodeInfo, Element row) {
        Element cell = row.appendElement("td");
        cell.text(nodeInfo.getSize());
    }

    private void addGroupReadCell(NodeInfo nodeInfo, Element row) {
        Element cell = row.appendElement("td");
        fillGroupCell(cell, nodeInfo.getGroupRead());
                addChild(child, containerElement);
            }

    private void addGroupWriteCell(NodeInfo nodeInfo, Element row) {
        Element cell = row.appendElement("td");
        fillGroupCell(cell, nodeInfo.getGroupWrite());
        }

    private void fillGroupCell(Element cell, String groups) {
        String[] values = groups.split(" ");
        List<String> personGroups = new ArrayList<>();
        List<String> peopleGroups = new ArrayList<>();
        for (String value : values) {
            if (!value.isBlank()) {
                if (value.startsWith("people.")) {
                    personGroups.add(value.substring("people.".length()).replace("\\.", "."));
                } else {
                    peopleGroups.add(value);
                }
            }
        return containerElement.toString();
    }
        if (!personGroups.isEmpty()) {
            Element personIcon = cell.appendElement("span");
            personIcon.attr("class", "icon person-icon");
            cell.appendText(String.join(" ", personGroups));
            cell.append("&nbsp;");
        }
        if (!peopleGroups.isEmpty()) {
            Element personIcon = cell.appendElement("span");
            personIcon.attr("class", "icon people-icon");
            cell.appendText(String.join(" ", peopleGroups));
        }
    }

    private void addActionsCell(NodeInfo nodeInfo, Element row) {
        Element cell = row.appendElement("td");

        Element dotsMenu = cell.appendElement("span");
        dotsMenu.attr("class", "dots-menu icon dots-menu-icon pointer");
    protected abstract Element createContainerElement(Document doc);
    
        Element dropdown = dotsMenu.appendElement("span");
        dropdown.attr("class", "dots-menu-content dropdown-menu");

        String nodePathJs = makeJsArg(nodeInfo.getPath());

        if (nodeInfo.isWritable()) {
            Element shareBtn = dropdown.appendElement("button");
            shareBtn.text("Share");
            shareBtn.attr("type", "button");
            shareBtn.attr("class", "dropdown-item");
            shareBtn.attr("onclick", "shareNode(" + nodePathJs
                    + "," + makeJsArg(nodeInfo.getGroupRead())
                    + "," + makeJsArg(nodeInfo.getGroupWrite()) + ")");
        }
        if (nodeInfo.isDeletable()) {
            Element renameBtn = dropdown.appendElement("button");
            renameBtn.text("Rename");
            renameBtn.attr("type", "button");
            renameBtn.attr("class", "dropdown-item");
            renameBtn.attr("onclick", "renameNode(" + nodePathJs + ")");

            Element deleteBtn = dropdown.appendElement("button");
            deleteBtn.text("Delete");
            deleteBtn.attr("type", "button");
            deleteBtn.attr("class", "dropdown-item");
            deleteBtn.attr("onclick", "deleteNode(" + nodePathJs + ")");
        }
    }
    protected abstract void addChild(Node child, Element containerElement);

    private String makeJsArg(String arg) {
    protected String makeJsArg(String arg) {
        return "'" + arg.replace("\\", "\\\\").replace("'", "\\'") + "'";
    }

    private void addNodeIcon(NodeInfo nodeInfo, Element cell) {
    protected void addNodeIcon(NodeInfo nodeInfo, Element parentElement) {

        if (nodeInfo.isBusy()) {
            Element loadingWrapper = cell.appendElement("span");
            Element loadingWrapper = parentElement.appendElement("span");
            loadingWrapper.addClass("node-busy");
            Element spinner = loadingWrapper.appendElement("span");
            spinner.attr("role", "status");
@@ -180,7 +64,7 @@ public class NodesHtmlGenerator {
            srEl.text("Loading...");
        }

        Element icon = cell.appendElement("span");
        Element icon = parentElement.appendElement("span");
        icon.addClass("icon");

        if (nodeInfo.isFolder()) {
@@ -197,49 +81,6 @@ public class NodesHtmlGenerator {
            }
        }

        cell.append("&nbsp;");
    }

    private void addLink(NodeInfo nodeInfo, Element cell) {
        if (isDownloadable(nodeInfo, user)) {
            Element link = cell.appendElement("a");
            String href;
            if (nodeInfo.isFolder()) {
                href = "#/nodes" + urlEncodePath(nodeInfo.getPath());
            } else {
                href = "download" + urlEncodePath(nodeInfo.getPath());
                link.attr("target", "blank_");
            }
            link.attr("href", href);
            link.text(nodeInfo.getName());
        } else {
            cell.appendText(nodeInfo.getName());
        }
    }

    private boolean isDownloadable(NodeInfo nodeInfo, User user) {
        if (nodeInfo.isFile()) {
            if (nodeInfo.isAsyncTrans() || nodeInfo.isBusy()) {
                return false;
            }
        }
        if (nodeInfo.isPublic()) {
            return true;
        }

        if (nodeInfo.getCreator().equals(user.getName())) {
            return true;
        }

        if (user.getGroups() != null && !user.getGroups().isEmpty() && !nodeInfo.getGroupRead().isEmpty()) {
            List<String> groupRead = Arrays.asList(nodeInfo.getGroupRead().split(" "));
            for (String group : groupRead) {
                if (user.getGroups().contains(group)) {
                    return true;
                }
            }
        }

        return false;
        parentElement.append("&nbsp;");
    }
}
Loading