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

Added input field for changing node name during copy of single nodes and added...

Added input field for changing node name during copy of single nodes and added comments on moveOrCopyNodes method
parent 5b9e1610
Loading
Loading
Loading
Loading
Loading
+11 −2
Original line number Diff line number Diff line
@@ -210,6 +210,7 @@ public class NodesController extends BaseController {
    @PostMapping(value = "/moveOrCopy")
    public ResponseEntity<List<Job>> moveOrCopyNodes(@RequestBody MoveOrCopyRequest request) throws Exception {

        // Creates a transfer request for each copy or move operation
        CompletableFuture<JobSummary>[] futureJobs = request.getTargets().stream().map(t -> {
            String target = urlEncodePath(t);
            String direction = urlEncodePath(request.getDirection());
@@ -220,12 +221,21 @@ public class NodesController extends BaseController {
            return CompletableFuture.supplyAsync(() -> client.startTransferJob(transfer), Runnable::run);
        }).collect(Collectors.toList()).toArray(CompletableFuture[]::new);

        // starts all HTTP requests in parallel
        CompletableFuture.allOf(futureJobs).join();

        // parses all the responses and populates a list of jobs to check the
        // completion status using polling mechanism
        List<JobSummary> jobs = Stream.of(futureJobs).map(j -> j.join()).collect(Collectors.toList());

        // Job statuses are checked for some time and if completion takes too much
        // time sends the execution phase to the UI and let it handles the polling.
        // Since CompletableFutures can't really be aborted (see cancel method
        // documentation) an AtomicReference containing a boolean flag it is
        // passed to handle polling abortion.
        AtomicReference<Boolean> cancelled = new AtomicReference<>(false);

        // CompletableFuture that triggers the timeout
        CompletableFuture timeout = CompletableFuture.runAsync(() -> {
            try {
                Thread.sleep(pollingTimeout * 1000);
@@ -235,6 +245,7 @@ public class NodesController extends BaseController {
        });

        try {
            // performs polling of job statuses or timeout
            CompletableFuture.anyOf(jobsPolling(jobs, cancelled), timeout).join();
        } catch (CompletionException ex) {
            if (ex.getCause() != null && ex.getCause() instanceof VOSpaceException) {
@@ -243,8 +254,6 @@ public class NodesController extends BaseController {
            throw ex;
        }

        // Try to perform polling until completion. If it takes too much time
        // sends the execution phase to the UI and let it handles the polling.
        Job.JobType type = request.isKeepBytes() ? Job.JobType.COPY : Job.JobType.MOVE;

        return ResponseEntity.ok(jobs.stream().map(j -> new Job(j, type))
+43 −2
Original line number Diff line number Diff line
@@ -5,6 +5,12 @@
-->
<template>
<b-modal id="move-or-copy-modal" :title="title" :okTitle="okTitle" @show="afterShow" @ok.prevent="moveOrCopyNodes" :ok-disabled="!writable" size="lg">
  <b-form inline v-if="this.nodesToMoveOrCopy.length === 1 && this.moveOrCopy === 'copy'" class="mb-3">
    <label class="w-25" for="new-name-input">New name</label>
    <b-form-input v-model.trim="newName" id="new-name-input" class="w-75" aria-describedby="new-name-input-feedback" :state="newNameState" v-on:input="resetNewNameError" @keydown.native.enter="moveOrCopyNodes">
    </b-form-input>
    <b-form-invalid-feedback id="new-folder-name-input-feedback" class="text-right">{{newNameError}}</b-form-invalid-feedback>
  </b-form>
  <ol class="breadcrumb">
    <li class="breadcrumb-item" v-for="(item, i) in breadcrumbs" :key="i" :class="{ 'active' : item.active }">
      <a href="#" @click.stop.prevent="breadcrumbClick(i)" v-if="!item.active">{{item.text}}</a>
@@ -66,13 +72,31 @@ export default {
        }
      }
      return items;
    },
    newNameState() {
      if (this.newNameError) {
        return false;
      }
      return null;
    }
  },
  data() {
    return {
      newName: null,
      newNameError: null
    }
  },
  methods: {
    afterShow() {
      // starts from parent path
      let firstSelectedNode = this.nodesToMoveOrCopy[0];
      this.$store.dispatch('openNodeInMoveOrCopyModal', firstSelectedNode.substring(0, firstSelectedNode.lastIndexOf('/')));
      let lastSlashPos = firstSelectedNode.lastIndexOf('/');
      this.$store.dispatch('openNodeInMoveOrCopyModal', firstSelectedNode.substring(0, lastSlashPos));
      if (this.nodesToMoveOrCopy.length === 1 && this.moveOrCopy === 'copy') {
        this.newName = firstSelectedNode.substring(lastSlashPos + 1);
      } else {
        this.newName = null;
      }
    },
    breadcrumbClick(i) {
      let pathSplit = this.destinationPath.split('/');
@@ -84,14 +108,31 @@ export default {
      window.openNodeInMoveOrCopyModal(event, path);
    },
    moveOrCopyNodes() {
      let direction = this.destinationPath;

      if (this.nodesToMoveOrCopy.length === 1 && this.moveOrCopy === 'copy') {
        if (!this.newName) {
          this.newNameError = "Name is required";
          return;
        } else if (/[<>?":\\/`|'*]/.test(this.newName)) {
          this.newNameError = "Name contains an illegal character. Following characters are not allowed: < > ? \" : \\ / | ' * `";
          return;
        }

        direction += '/' + this.newName;
      }

      this.$store.dispatch('moveOrCopyNodes', {
          targets: this.nodesToMoveOrCopy,
          direction: this.destinationPath,
          direction,
          keepBytes: this.moveOrCopy === 'copy'
        })
        .then(() => {
          this.$bvModal.hide('move-or-copy-modal');
        })
    },
    resetNewNameError() {
      this.newNameError = null;
    }
  }
}