You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jena.apache.org by ki...@apache.org on 2022/03/29 11:12:05 UTC

[jena] branch main updated (95cedff -> 9b7d442)

This is an automated email from the ASF dual-hosted git repository.

kinow pushed a change to branch main
in repository https://gitbox.apache.org/repos/asf/jena.git.


    from 95cedff  Update ansi-regex to 4.1.1 (#1242)
     new d7d2b38  [JENA-2312] Use the service endpoint in the URL used by the UI to query the backend
     new 9b7d442  [JENA-2312] Prevent Edit view from being used when no write service enabled

The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .../{list-datasets.js => current-dataset.js}       |  93 ++++---
 .../jena-fuseki-ui/src/services/fuseki.service.js  |  12 +-
 .../jena-fuseki-ui/src/{events => utils}/index.js  |  13 +-
 .../jena-fuseki-ui/src/views/dataset/Edit.vue      |  30 +--
 .../jena-fuseki-ui/src/views/dataset/Info.vue      |  72 ++----
 .../jena-fuseki-ui/src/views/dataset/Query.vue     |  34 ++-
 .../jena-fuseki-ui/src/views/dataset/Upload.vue    | 286 +++++++++++----------
 7 files changed, 259 insertions(+), 281 deletions(-)
 copy jena-fuseki2/jena-fuseki-ui/src/mixins/{list-datasets.js => current-dataset.js} (50%)
 copy jena-fuseki2/jena-fuseki-ui/src/{events => utils}/index.js (82%)

[jena] 01/02: [JENA-2312] Use the service endpoint in the URL used by the UI to query the backend

Posted by ki...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

kinow pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/jena.git

commit d7d2b38365f4e2535372773cc0d7ed9ab6a3dea3
Author: Bruno P. Kinoshita <ki...@apache.org>
AuthorDate: Mon Mar 14 21:48:01 2022 +1300

    [JENA-2312] Use the service endpoint in the URL used by the UI to query the backend
---
 .../jena-fuseki-ui/src/mixins/current-dataset.js   | 89 ++++++++++++++++++++++
 .../jena-fuseki-ui/src/services/fuseki.service.js  | 12 +--
 jena-fuseki2/jena-fuseki-ui/src/utils/index.js     | 25 ++++++
 .../jena-fuseki-ui/src/views/dataset/Edit.vue      | 20 ++---
 .../jena-fuseki-ui/src/views/dataset/Info.vue      | 72 +++++------------
 .../jena-fuseki-ui/src/views/dataset/Query.vue     | 34 ++++++---
 .../jena-fuseki-ui/src/views/dataset/Upload.vue    | 24 +++---
 7 files changed, 183 insertions(+), 93 deletions(-)

diff --git a/jena-fuseki2/jena-fuseki-ui/src/mixins/current-dataset.js b/jena-fuseki2/jena-fuseki-ui/src/mixins/current-dataset.js
new file mode 100644
index 0000000..2859839
--- /dev/null
+++ b/jena-fuseki2/jena-fuseki-ui/src/mixins/current-dataset.js
@@ -0,0 +1,89 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { BUS } from '@/events'
+
+/**
+ * A mixin for views and components that need to have the current dataset loaded. The
+ * dataset is loaded by its name, which is a prop for the view or component.
+ */
+
+export default {
+  props: {
+    datasetName: {
+      type: String,
+      required: true
+    }
+  },
+  data () {
+    return {
+      isDatasetStatsLoading: true,
+      serverData: {
+        datasets: []
+      }
+    }
+  },
+  computed: {
+    currentDataset () {
+      return this.serverData.datasets.find(dataset => dataset['ds.name'] === `/${this.datasetName}`)
+    },
+    services () {
+      if (!this.currentDataset || !this.currentDataset['ds.services']) {
+        return []
+      }
+      return this.currentDataset['ds.services']
+        .slice()
+        .sort((left, right) => {
+          return left['srv.type'].localeCompare(right['srv.type'])
+        })
+        .reduce((acc, cur) => {
+          acc[cur['srv.type']] = cur
+          return acc
+        }, {})
+    }
+  },
+  methods: {
+    loadCurrentDataset () {
+      this.isDatasetStatsLoading = true
+      this.$fusekiService
+        .getServerData()
+        .then(serverData => {
+          this.serverData = serverData
+        })
+      this.$fusekiService
+        .getDatasetStats(this.datasetName)
+        .then(datasetStats => {
+          this.datasetStats = datasetStats
+        })
+      this.isDatasetStatsLoading = false
+    }
+  },
+  beforeRouteEnter (from, to, next) {
+    next(async vm => {
+      BUS.$on('connection:reset', vm.loadCurrentDataset)
+      vm.loadCurrentDataset()
+    })
+  },
+  async beforeRouteUpdate (from, to, next) {
+    this.loadCurrentDataset()
+    next()
+  },
+  beforeRouteLeave (from, to, next) {
+    BUS.$off('connection:reset')
+    next()
+  }
+}
diff --git a/jena-fuseki2/jena-fuseki-ui/src/services/fuseki.service.js b/jena-fuseki2/jena-fuseki-ui/src/services/fuseki.service.js
index 60a332d..eaf7414 100644
--- a/jena-fuseki2/jena-fuseki-ui/src/services/fuseki.service.js
+++ b/jena-fuseki2/jena-fuseki-ui/src/services/fuseki.service.js
@@ -87,15 +87,15 @@ class FusekiService {
     return response.data
   }
 
-  async getDatasetSize (datasetName) {
+  async getDatasetSize (datasetName, endpoint) {
     const promisesResult = await Promise.all([
       axios
-        .get(this.getFusekiUrl(`/${datasetName}/sparql`), {
+        .get(this.getFusekiUrl(`/${datasetName}/${endpoint}`), {
           params: {
             query: DATASET_SIZE_QUERY_1
           }
         }),
-      axios.get(this.getFusekiUrl(`/${datasetName}/sparql`), {
+      axios.get(this.getFusekiUrl(`/${datasetName}/${endpoint}`), {
         params: {
           query: DATASET_SIZE_QUERY_2
         }
@@ -160,15 +160,15 @@ class FusekiService {
     return axios.get(this.getFusekiUrl('/$/tasks'))
   }
 
-  async countGraphsTriples (datasetName) {
+  async countGraphsTriples (datasetName, endpoint) {
     const promisesResult = await Promise.all([
       axios
-        .get(this.getFusekiUrl(`/${datasetName}/sparql`), {
+        .get(this.getFusekiUrl(`/${datasetName}/${endpoint}`), {
           params: {
             query: DATASET_COUNT_GRAPH_QUERY_1
           }
         }),
-      axios.get(this.getFusekiUrl(`/${datasetName}/sparql`), {
+      axios.get(this.getFusekiUrl(`/${datasetName}/${endpoint}`), {
         params: {
           query: DATASET_COUNT_GRAPH_QUERY_2
         }
diff --git a/jena-fuseki2/jena-fuseki-ui/src/utils/index.js b/jena-fuseki2/jena-fuseki-ui/src/utils/index.js
new file mode 100644
index 0000000..6e58a59
--- /dev/null
+++ b/jena-fuseki2/jena-fuseki-ui/src/utils/index.js
@@ -0,0 +1,25 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+export function displayError (vm, error) {
+  console.error(error)
+  vm.$bvToast.toast(`${error}`, {
+    title: 'Error',
+    noAutoHide: true,
+    appendToast: false
+  })
+}
diff --git a/jena-fuseki2/jena-fuseki-ui/src/views/dataset/Edit.vue b/jena-fuseki2/jena-fuseki-ui/src/views/dataset/Edit.vue
index c1e83d7..6ada112 100644
--- a/jena-fuseki2/jena-fuseki-ui/src/views/dataset/Edit.vue
+++ b/jena-fuseki2/jena-fuseki-ui/src/views/dataset/Edit.vue
@@ -118,6 +118,8 @@ import {
 } from '@fortawesome/free-solid-svg-icons'
 import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome'
 import { library } from '@fortawesome/fontawesome-svg-core'
+import currentDatasetMixin from '@/mixins/current-dataset'
+import { displayError } from '@/utils'
 
 library.add(faTimes, faCheck)
 
@@ -132,12 +134,9 @@ export default {
     FontAwesomeIcon
   },
 
-  props: {
-    datasetName: {
-      type: String,
-      required: true
-    }
-  },
+  mixins: [
+    currentDatasetMixin
+  ],
 
   data () {
     return {
@@ -218,13 +217,9 @@ export default {
       this.code = ''
       this.selectedGraph = ''
       try {
-        this.graphs = await this.$fusekiService.countGraphsTriples(this.datasetName)
+        this.graphs = await this.$fusekiService.countGraphsTriples(this.datasetName, this.services.query['srv.endpoints'][0])
       } catch (error) {
-        this.$bvToast.toast(`${error}`, {
-          title: 'Error',
-          noAutoHide: true,
-          appendToast: false
-        })
+        displayError(this, error)
       } finally {
         this.loadingGraphs = false
         this.loadingGraph = false
@@ -243,6 +238,7 @@ export default {
         const result = await this.$fusekiService.fetchGraph(this.datasetName, graphName)
         this.code = result.data
       } catch (error) {
+        console.error(error)
         this.$bvToast.toast(`${error}`, {
           title: 'Error',
           noAutoHide: true,
diff --git a/jena-fuseki2/jena-fuseki-ui/src/views/dataset/Info.vue b/jena-fuseki2/jena-fuseki-ui/src/views/dataset/Info.vue
index 2daed94..66c4720 100644
--- a/jena-fuseki2/jena-fuseki-ui/src/views/dataset/Info.vue
+++ b/jena-fuseki2/jena-fuseki-ui/src/views/dataset/Info.vue
@@ -149,7 +149,8 @@
 
 <script>
 import Menu from '@/components/dataset/Menu'
-import { BUS } from '@/events'
+import { displayError } from '@/utils'
+import currentDatasetMixin from '@/mixins/current-dataset'
 
 export default {
   name: 'DatasetInfo',
@@ -158,24 +159,15 @@ export default {
     Menu
   },
 
-  props: {
-    datasetName: {
-      type: String,
-      required: true
-    }
-  },
+  mixins: [
+    currentDatasetMixin
+  ],
 
   data () {
     return {
-      serverData: {
-        datasets: []
-      },
-      datasetStats: {
-
-      },
+      datasetStats: {},
       datasetSize: null,
       isDatasetSizeLoading: false,
-      isDatasetStatsLoading: true,
       popoverShow: false,
       statsFields: [
         {
@@ -219,17 +211,6 @@ export default {
   },
 
   computed: {
-    currentDataset () {
-      return this.serverData.datasets.find(dataset => dataset['ds.name'] === `/${this.datasetName}`)
-    },
-    services () {
-      if (!this.currentDataset || !this.currentDataset['ds.services']) {
-        return []
-      }
-      return this.currentDataset['ds.services'].slice().sort((left, right) => {
-        return left['srv.type'].localeCompare(right['srv.type'])
-      })
-    },
     statsItems () {
       if (!this.datasetStats || !this.datasetStats.datasets) {
         return []
@@ -284,46 +265,29 @@ export default {
 
   beforeRouteEnter (from, to, next) {
     next(async vm => {
-      BUS.$on('connection:reset', vm.initializeData)
-      vm.initializeData()
+      vm.datasetSize = null
     })
   },
 
   async beforeRouteUpdate (from, to, next) {
-    this.initializeData()
-    next()
-  },
-
-  beforeRouteLeave (from, to, next) {
-    BUS.$off('connection:reset')
+    this.datasetSize = null
     next()
   },
 
   methods: {
     async countTriplesInGraphs () {
       this.popoverShow = false
-      this.isDatasetStatsLoading = true
       this.isDatasetSizeLoading = true
-      this.datasetSize = await this.$fusekiService.getDatasetSize(this.datasetName)
-      this.isDatasetSizeLoading = false
-      this.$refs['count-triples-button'].disabled = this.isDatasetSizeLoading
-      this.datasetStats = await this.$fusekiService.getDatasetStats(this.datasetName)
-      this.isDatasetStatsLoading = false
-    },
-    initializeData () {
-      this.isDatasetStatsLoading = true
-      this.$fusekiService
-        .getServerData()
-        .then(serverData => {
-          this.serverData = serverData
-        })
-      this.$fusekiService
-        .getDatasetStats(this.datasetName)
-        .then(datasetStats => {
-          this.datasetStats = datasetStats
-        })
-      this.isDatasetStatsLoading = false
-      this.datasetSize = null
+      try {
+        this.datasetSize = await this.$fusekiService.getDatasetSize(this.currentDataset['ds.name'], this.services.query['srv.endpoints'][0])
+        this.$refs['count-triples-button'].disabled = this.isDatasetSizeLoading
+        this.datasetStats = await this.$fusekiService.getDatasetStats(this.datasetName)
+      } catch (error) {
+        displayError(this, error)
+      } finally {
+        this.isDatasetSizeLoading = false
+        this.$refs['count-triples-button'].disabled = this.isDatasetSizeLoading
+      }
     }
   }
 }
diff --git a/jena-fuseki2/jena-fuseki-ui/src/views/dataset/Query.vue b/jena-fuseki2/jena-fuseki-ui/src/views/dataset/Query.vue
index fa9b11e..3d3f153 100644
--- a/jena-fuseki2/jena-fuseki-ui/src/views/dataset/Query.vue
+++ b/jena-fuseki2/jena-fuseki-ui/src/views/dataset/Query.vue
@@ -120,6 +120,7 @@ import Yasqe from '@triply/yasqe'
 import Yasr from '@triply/yasr'
 import queryString from 'query-string'
 import Vue from 'vue'
+import currentDatasetMixin from '@/mixins/current-dataset'
 
 const SELECT_TRIPLES_QUERY = `SELECT ?subject ?predicate ?object
 WHERE {
@@ -145,19 +146,15 @@ export default {
     Menu
   },
 
-  props: {
-    datasetName: {
-      type: String,
-      required: true
-    }
-  },
+  mixins: [
+    currentDatasetMixin
+  ],
 
   data () {
     return {
       loading: true,
       yasqe: null,
       yasr: null,
-      datasetUrl: `/${this.datasetName}/sparql`,
       contentTypeSelect: 'application/sparql-results+json',
       contentTypeSelectOptions: [
         { value: 'application/sparql-results+json', text: 'JSON' },
@@ -192,6 +189,15 @@ export default {
     }
   },
 
+  computed: {
+    datasetUrl () {
+      if (!this.datasetName || !this.services.query || !this.services.query['srv.endpoints'] || this.services.query['srv.endpoints'].length === 0) {
+        return ''
+      }
+      return `/${this.datasetName}/${this.services.query['srv.endpoints'][0]}`
+    }
+  },
+
   created () {
     this.$nextTick(() => {
       setTimeout(() => {
@@ -213,7 +219,7 @@ export default {
             showQueryButton: true,
             resizeable: false,
             requestConfig: {
-              endpoint: this.$fusekiService.getFusekiUrl(`/${vm.datasetName}/sparql`)
+              endpoint: this.$fusekiService.getFusekiUrl(this.datasetUrl)
             },
             /**
              * Based on YASGUI code, but modified to avoid parsing the Vue Route query
@@ -264,13 +270,19 @@ export default {
 
   watch: {
     datasetUrl: function (val, oldVal) {
-      this.yasqe.options.requestConfig.endpoint = this.$fusekiService.getFusekiUrl(this.datasetUrl)
+      if (this.yasqe) {
+        this.yasqe.options.requestConfig.endpoint = this.$fusekiService.getFusekiUrl(this.datasetUrl)
+      }
     },
     contentTypeSelect: function (val, oldVal) {
-      this.yasqe.options.requestConfig.acceptHeaderSelect = this.contentTypeSelect
+      if (this.yasqe) {
+        this.yasqe.options.requestConfig.acceptHeaderSelect = this.contentTypeSelect
+      }
     },
     contentTypeGraph: function (val, oldVal) {
-      this.yasqe.options.requestConfig.acceptHeaderGraph = this.contentTypeGraph
+      if (this.yasqe) {
+        this.yasqe.options.requestConfig.acceptHeaderGraph = this.contentTypeGraph
+      }
     }
   },
 
diff --git a/jena-fuseki2/jena-fuseki-ui/src/views/dataset/Upload.vue b/jena-fuseki2/jena-fuseki-ui/src/views/dataset/Upload.vue
index 827b1dc..55cf352 100644
--- a/jena-fuseki2/jena-fuseki-ui/src/views/dataset/Upload.vue
+++ b/jena-fuseki2/jena-fuseki-ui/src/views/dataset/Upload.vue
@@ -25,7 +25,10 @@
             <Menu :dataset-name="datasetName" />
           </b-card-header>
           <b-card-body>
-            <div>
+            <div v-if="!this.services['gsp-rw'] || this.services['gsp-rw'].length === 0">
+              <b-alert show variant="warning">No service for Graph Store Protocol configured</b-alert>
+            </div>
+            <div v-else>
               <div v-show="$refs.upload && $refs.upload.dropActive" class="drop-active">
                 <h3>Drop files to upload</h3>
               </div>
@@ -172,6 +175,7 @@ import FileUpload from 'vue-upload-component'
 import { library } from '@fortawesome/fontawesome-svg-core'
 import { faPlus, faUpload, faTimesCircle, faMinusCircle } from '@fortawesome/free-solid-svg-icons'
 import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome'
+import currentDatasetMixin from '@/mixins/current-dataset'
 
 library.add(faPlus, faUpload, faTimesCircle, faMinusCircle)
 
@@ -184,12 +188,9 @@ export default {
     FileUpload
   },
 
-  props: {
-    datasetName: {
-      type: String,
-      required: true
-    }
-  },
+  mixins: [
+    currentDatasetMixin
+  ],
 
   data () {
     return {
@@ -211,8 +212,7 @@ export default {
         name: 'file',
         headers: { // e.g. CSRF headers
         },
-        data: {
-        },
+        data: {},
         autoCompress: 1024 * 1024,
         uploadAuto: false,
         isOption: false
@@ -269,8 +269,12 @@ export default {
         })
     },
     postActionUrl () {
+      if (!this.services['gsp-rw'] || this.services['gsp-rw'].length === 0) {
+        return ''
+      }
       const params = (this.form.datasetGraphName && this.form.datasetGraphName !== '') ? `?graph=${this.form.datasetGraphName}` : ''
-      return this.$fusekiService.getFusekiUrl(`/${this.datasetName}/data${params}`)
+      const dataEndpoint = this.services['gsp-rw']['srv.endpoints'].find(endpoint => endpoint !== '') || ''
+      return this.$fusekiService.getFusekiUrl(`/${this.datasetName}/${dataEndpoint}${params}`)
     }
   },
 

[jena] 02/02: [JENA-2312] Prevent Edit view from being used when no write service enabled

Posted by ki...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

kinow pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/jena.git

commit 9b7d442f3ee0493d0bc7f33be94744f7acb44e1f
Author: Bruno P. Kinoshita <ki...@apache.org>
AuthorDate: Tue Mar 29 14:37:36 2022 +1300

    [JENA-2312] Prevent Edit view from being used when no write service enabled
---
 .../jena-fuseki-ui/src/views/dataset/Edit.vue      |  10 +-
 .../jena-fuseki-ui/src/views/dataset/Upload.vue    | 268 ++++++++++-----------
 2 files changed, 140 insertions(+), 138 deletions(-)

diff --git a/jena-fuseki2/jena-fuseki-ui/src/views/dataset/Edit.vue b/jena-fuseki2/jena-fuseki-ui/src/views/dataset/Edit.vue
index 6ada112..687ff28 100644
--- a/jena-fuseki2/jena-fuseki-ui/src/views/dataset/Edit.vue
+++ b/jena-fuseki2/jena-fuseki-ui/src/views/dataset/Edit.vue
@@ -24,16 +24,20 @@
           <b-card-header header-tag="nav">
             <Menu :dataset-name="datasetName" />
           </b-card-header>
-          <b-card-body>
+          <b-card-body v-if="!this.services['gsp-rw'] || this.services['gsp-rw'].length === 0">
+            <b-alert show variant="warning">No service for adding data available. The Graph Store Protocol service should be configured to allow adding data.</b-alert>
+          </b-card-body>
+          <b-card-body v-else>
             <b-card-title>Available Graphs</b-card-title>
             <div>
               <b-row class="mb-2">
                 <b-col sm="12" md="4">
                   <div class="mb-2">
-                    <b-btn
+                    <b-button
                       @click="listCurrentGraphs()"
                       :disabled="!!loadingGraphs"
-                    >list current graphs</b-btn>
+                      variant="primary"
+                    >list current graphs</b-button>
                   </div>
                   <b-list-group>
                     <b-list-group-item
diff --git a/jena-fuseki2/jena-fuseki-ui/src/views/dataset/Upload.vue b/jena-fuseki2/jena-fuseki-ui/src/views/dataset/Upload.vue
index 55cf352..8d11aec 100644
--- a/jena-fuseki2/jena-fuseki-ui/src/views/dataset/Upload.vue
+++ b/jena-fuseki2/jena-fuseki-ui/src/views/dataset/Upload.vue
@@ -24,144 +24,142 @@
           <b-card-header header-tag="nav">
             <Menu :dataset-name="datasetName" />
           </b-card-header>
-          <b-card-body>
-            <div v-if="!this.services['gsp-rw'] || this.services['gsp-rw'].length === 0">
-              <b-alert show variant="warning">No service for Graph Store Protocol configured</b-alert>
+          <b-card-body v-if="!this.services['gsp-rw'] || this.services['gsp-rw'].length === 0">
+            <b-alert show variant="warning">No service for adding data available. The Graph Store Protocol service should be configured to allow adding data.</b-alert>
+          </b-card-body>
+          <b-card-body v-else>
+            <div v-show="$refs.upload && $refs.upload.dropActive" class="drop-active">
+              <h3>Drop files to upload</h3>
             </div>
-            <div v-else>
-              <div v-show="$refs.upload && $refs.upload.dropActive" class="drop-active">
-                <h3>Drop files to upload</h3>
-              </div>
-              <b-row>
-                <b-col sm="12">
-                  <h3>Upload files</h3>
-                  <p>Load data into the default graph of the currently selected dataset, or the given named graph.
-                    You may upload any RDF format, such as Turtle, RDF/XML or TRiG.</p>
-                  <b-form>
-                    <b-form-group
-                      id="dataset-graph-name-group"
-                      label="Dataset graph name"
-                      label-for="dataset-graph-name"
-                      label-cols="12"
-                      label-cols-sm="4"
-                      label-cols-md="4"
-                      label-cols-lg="2"
-                      label-size="sm"
+            <b-row>
+              <b-col sm="12">
+                <h3>Upload files</h3>
+                <p>Load data into the default graph of the currently selected dataset, or the given named graph.
+                  You may upload any RDF format, such as Turtle, RDF/XML or TRiG.</p>
+                <b-form>
+                  <b-form-group
+                    id="dataset-graph-name-group"
+                    label="Dataset graph name"
+                    label-for="dataset-graph-name"
+                    label-cols="12"
+                    label-cols-sm="4"
+                    label-cols-md="4"
+                    label-cols-lg="2"
+                    label-size="sm"
+                  >
+                    <b-form-input
+                      pattern="[^\s]+"
+                      oninvalid="this.setCustomValidity('Enter a valid dataset graph name')"
+                      oninput="this.setCustomValidity('')"
+                      id="dataset-graph-name"
+                      v-model="form.datasetGraphName"
+                      type="text"
+                      placeholder="Leave blank for default graph"
+                      trim
+                    ></b-form-input>
+                  </b-form-group>
+                  <b-form-group
+                    id="dataset-files"
+                    label="Files to upload"
+                    label-for="add-files-action-dropdown"
+                    label-cols="12"
+                    label-cols-sm="4"
+                    label-cols-md="4"
+                    label-cols-lg="2"
+                    label-size="sm"
+                  >
+                    <file-upload
+                      ref="upload"
+                      v-model="upload.files"
+                      :post-action="postActionUrl"
+                      :extensions="upload.extensions"
+                      :accept="upload.accept"
+                      :multiple="upload.multiple"
+                      :directory="upload.directory"
+                      :size="upload.size || 0"
+                      :thread="upload.thread < 1 ? 1 : (upload.thread > 5 ? 5 : upload.thread)"
+                      :headers="upload.headers"
+                      :data="upload.data"
+                      :drop="upload.drop"
+                      :drop-directory="upload.dropDirectory"
+                      :add-index="upload.addIndex"
+                      class="btn btn-success"
                     >
-                      <b-form-input
-                        pattern="[^\s]+"
-                        oninvalid="this.setCustomValidity('Enter a valid dataset graph name')"
-                        oninput="this.setCustomValidity('')"
-                        id="dataset-graph-name"
-                        v-model="form.datasetGraphName"
-                        type="text"
-                        placeholder="Leave blank for default graph"
-                        trim
-                      ></b-form-input>
-                    </b-form-group>
-                    <b-form-group
-                      id="dataset-files"
-                      label="Files to upload"
-                      label-for="add-files-action-dropdown"
-                      label-cols="12"
-                      label-cols-sm="4"
-                      label-cols-md="4"
-                      label-cols-lg="2"
-                      label-size="sm"
+                      <FontAwesomeIcon icon="plus" />
+                      <span class="ml-2">select files</span>
+                    </file-upload>
+                    <b-button
+                      v-if="!$refs.upload || !$refs.upload.active"
+                      @click.prevent="$refs.upload.active = true"
+                      variant="primary"
+                      class="ml-2 d-inline">
+                      <FontAwesomeIcon icon="upload" />
+                      <span class="ml-2">upload all</span>
+                    </b-button>
+                    <b-button
+                      v-else
+                      @click.prevent="$refs.upload.active = false"
+                      variant="primary"
+                      class="ml-2 d-inline">
+                      <FontAwesomeIcon icon="times-circle" />
+                      <span class="ml-2">stop upload</span>
+                    </b-button>
+                  </b-form-group>
+                </b-form>
+              </b-col>
+            </b-row>
+            <b-row>
+              <b-col>
+                <b-table
+                  :fields="datasetTableFields"
+                  :items="datasetTableItems"
+                  stacked="lg"
+                  empty-text="No files selected"
+                  bordered
+                  fixed
+                  hover
+                  show-empty
+                >
+                  <template v-slot:cell(size)="data">
+                    {{ readableFileSize(data.item.size) }}
+                  </template>
+                  <template v-slot:cell(speed)="data">
+                    {{ readableFileSize(data.item.speed) }}/s
+                  </template>
+                  <template v-slot:cell(status)="data">
+                    <b-progress
+                      :variant="getFileStatus(data.item)"
+                      :value="data.item.progress"
+                      :max="100"
+                      :precision="2"
+                      show-progress></b-progress>
+                    <span class="small">Triples uploaded:&nbsp;</span>
+                    <span v-if="data.item.response.tripleCount" class="small">
+                      {{ data.item.response.tripleCount }}
+                    </span>
+                    <span v-else class="small">0</span>
+                  </template>
+                  <template v-slot:cell(actions)="data">
+                    <b-button
+                      @click.prevent="data.item.success || data.item.error === 'compressing' ? false : $refs.upload.update(data.item, {active: true})"
+                      variant="outline-primary"
+                      class="mr-0 mb-2 d-block"
                     >
-                      <file-upload
-                        ref="upload"
-                        v-model="upload.files"
-                        :post-action="postActionUrl"
-                        :extensions="upload.extensions"
-                        :accept="upload.accept"
-                        :multiple="upload.multiple"
-                        :directory="upload.directory"
-                        :size="upload.size || 0"
-                        :thread="upload.thread < 1 ? 1 : (upload.thread > 5 ? 5 : upload.thread)"
-                        :headers="upload.headers"
-                        :data="upload.data"
-                        :drop="upload.drop"
-                        :drop-directory="upload.dropDirectory"
-                        :add-index="upload.addIndex"
-                        class="btn btn-success"
-                      >
-                        <FontAwesomeIcon icon="plus" />
-                        <span class="ml-2">select files</span>
-                      </file-upload>
-                      <b-button
-                        v-if="!$refs.upload || !$refs.upload.active"
-                        @click.prevent="$refs.upload.active = true"
-                        variant="primary"
-                        class="ml-2 d-inline">
-                        <FontAwesomeIcon icon="upload" />
-                        <span class="ml-2">upload all</span>
-                      </b-button>
-                      <b-button
-                        v-else
-                        @click.prevent="$refs.upload.active = false"
-                        variant="primary"
-                        class="ml-2 d-inline">
-                        <FontAwesomeIcon icon="times-circle" />
-                        <span class="ml-2">stop upload</span>
-                      </b-button>
-                    </b-form-group>
-                  </b-form>
-                </b-col>
-              </b-row>
-              <b-row>
-                <b-col>
-                  <b-table
-                    :fields="datasetTableFields"
-                    :items="datasetTableItems"
-                    stacked="lg"
-                    empty-text="No files selected"
-                    bordered
-                    fixed
-                    hover
-                    show-empty
-                  >
-                    <template v-slot:cell(size)="data">
-                      {{ readableFileSize(data.item.size) }}
-                    </template>
-                    <template v-slot:cell(speed)="data">
-                      {{ readableFileSize(data.item.speed) }}/s
-                    </template>
-                    <template v-slot:cell(status)="data">
-                      <b-progress
-                        :variant="getFileStatus(data.item)"
-                        :value="data.item.progress"
-                        :max="100"
-                        :precision="2"
-                        show-progress></b-progress>
-                      <span class="small">Triples uploaded:&nbsp;</span>
-                      <span v-if="data.item.response.tripleCount" class="small">
-                        {{ data.item.response.tripleCount }}
-                      </span>
-                      <span v-else class="small">0</span>
-                    </template>
-                    <template v-slot:cell(actions)="data">
-                      <b-button
-                        @click.prevent="data.item.success || data.item.error === 'compressing' ? false : $refs.upload.update(data.item, {active: true})"
-                        variant="outline-primary"
-                        class="mr-0 mb-2 d-block"
-                      >
-                        <FontAwesomeIcon icon="upload" />
-                        <span class="ml-2">upload now</span>
-                      </b-button>
-                      <b-button
-                        @click.prevent="remove(data.item)"
-                        variant="outline-primary"
-                        class="mr-0 mb-md-0 d-block d-md-inline-block"
-                      >
-                        <FontAwesomeIcon icon="minus-circle" />
-                        <span class="ml-2">remove</span>
-                      </b-button>
-                    </template>
-                  </b-table>
-                </b-col>
-              </b-row>
-            </div>
+                      <FontAwesomeIcon icon="upload" />
+                      <span class="ml-2">upload now</span>
+                    </b-button>
+                    <b-button
+                      @click.prevent="remove(data.item)"
+                      variant="outline-primary"
+                      class="mr-0 mb-md-0 d-block d-md-inline-block"
+                    >
+                      <FontAwesomeIcon icon="minus-circle" />
+                      <span class="ml-2">remove</span>
+                    </b-button>
+                  </template>
+                </b-table>
+              </b-col>
+            </b-row>
           </b-card-body>
         </b-card>
       </b-col>