You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jena.apache.org by an...@apache.org on 2018/09/21 10:15:17 UTC
[06/70] [abbrv] [partial] jena git commit: JENA-1597: separate
jena-fuseki-webapp module
http://git-wip-us.apache.org/repos/asf/jena/blob/e8abcbb6/jena-fuseki2/jena-fuseki-webapp/src/main/webapp/js/app/controllers/upload-controller.js
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-webapp/src/main/webapp/js/app/controllers/upload-controller.js b/jena-fuseki2/jena-fuseki-webapp/src/main/webapp/js/app/controllers/upload-controller.js
new file mode 100644
index 0000000..ace2547
--- /dev/null
+++ b/jena-fuseki2/jena-fuseki-webapp/src/main/webapp/js/app/controllers/upload-controller.js
@@ -0,0 +1,42 @@
+/** Controller for the file uploader component */
+
+define(
+ function( require ) {
+ var Marionette = require( "marionette" ),
+ Backbone = require( "backbone" ),
+ _ = require( "underscore" ),
+ sprintf = require("sprintf"),
+ pageUtils = require( "app/util/page-utils" ),
+ fui = require( "app/fui" ),
+ FileUploadView = require( "app/views/file-upload" );
+
+ var UploadController = function() {
+ this.initialize();
+ };
+
+ _.extend( UploadController.prototype, {
+
+ /** Initialize the controler */
+ initialize: function() {
+ if (fui.models.fusekiServer && fui.models.fusekiServer.get( "ready" )) {
+ this.onServerModelReady();
+ }
+ else {
+ _.bindAll( this, "onServerModelReady" );
+ fui.vent.on( "models.fuseki-server.ready", this.onServerModelReady );
+ }
+
+ },
+
+ /** When the fuseki server is ready, we can set up the initial view */
+ onServerModelReady: function( event ) {
+ var fusekiServer = fui.models.fusekiServer;
+
+ fui.views.fileUploadView = new FileUploadView();
+ fui.views.fileUploadView.render();
+ },
+ } );
+
+ return UploadController;
+ }
+);
http://git-wip-us.apache.org/repos/asf/jena/blob/e8abcbb6/jena-fuseki2/jena-fuseki-webapp/src/main/webapp/js/app/controllers/validation-controller.js
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-webapp/src/main/webapp/js/app/controllers/validation-controller.js b/jena-fuseki2/jena-fuseki-webapp/src/main/webapp/js/app/controllers/validation-controller.js
new file mode 100644
index 0000000..43ed8ce
--- /dev/null
+++ b/jena-fuseki2/jena-fuseki-webapp/src/main/webapp/js/app/controllers/validation-controller.js
@@ -0,0 +1,38 @@
+/** Controller for the main index.html page */
+define(
+ function( require ) {
+ var Marionette = require( "marionette" ),
+ Backbone = require( "backbone" ),
+ _ = require( "underscore" ),
+ fui = require( "app/fui" ),
+ ValidationOptions = require( "app/views/validation-options" ),
+ ValidationService = require( "app/services/validation-service" );
+
+ var ValidationController = function() {
+ this.initServices();
+ this.initEvents();
+ };
+
+ // add the behaviours defined on the controller
+ _.extend( ValidationController.prototype, {
+ initEvents: function() {
+ fui.vent.on( "models.validation-options.ready", this.onValidationOptionsModelReady );
+ $(".validation").on( "click", "a.perform-validation", function( event ) {
+ fui.services.validation.performValidation( fui.views.validationOptions.model );
+ } );
+ },
+
+ onValidationOptionsModelReady: function( e ) {
+ fui.views.validationOptions = new ValidationOptions( {model: fui.models.validationOptions} );
+ },
+
+ initServices: function() {
+ fui.services.validation = new ValidationService( "#query-edit-cm", "#validation-output-cm" );
+ fui.services.validation.init();
+ }
+
+ } );
+
+ return ValidationController;
+ }
+);
http://git-wip-us.apache.org/repos/asf/jena/blob/e8abcbb6/jena-fuseki2/jena-fuseki-webapp/src/main/webapp/js/app/fui.js
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-webapp/src/main/webapp/js/app/fui.js b/jena-fuseki2/jena-fuseki-webapp/src/main/webapp/js/app/fui.js
new file mode 100644
index 0000000..e8bc41d
--- /dev/null
+++ b/jena-fuseki2/jena-fuseki-webapp/src/main/webapp/js/app/fui.js
@@ -0,0 +1,33 @@
+/**
+ * Top-level application code module for Fuseki UI
+ */
+
+define( ['require', 'backbone', 'marionette'],
+ function( require, Backbone, Marionette ) {
+ // define the application object, and add it to the global namespace
+ var fui = new Marionette.Application();
+
+ // define some Marionette modules, because they have a lifecycle component
+ // see https://github.com/marionettejs/backbone.marionette/wiki/AMD-Modules-vs-Marionette%27s-Modules
+ fui.module( "models" );
+ fui.module( "views" );
+ fui.module( "layouts" );
+ fui.module( "controllers" );
+ fui.module( "services" );
+
+ // define top-level regions where our layouts will go
+ fui.addRegions({
+ });
+
+ fui.on('initialize:before', function( options ) {
+ });
+
+ fui.on('initialize:after', function( options ) {
+ // Backbone.history.start();
+ this.initialized = true;
+ });
+
+
+ return fui;
+ }
+);
http://git-wip-us.apache.org/repos/asf/jena/blob/e8abcbb6/jena-fuseki2/jena-fuseki-webapp/src/main/webapp/js/app/main.dataset.js
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-webapp/src/main/webapp/js/app/main.dataset.js b/jena-fuseki2/jena-fuseki-webapp/src/main/webapp/js/app/main.dataset.js
new file mode 100644
index 0000000..50ba27b
--- /dev/null
+++ b/jena-fuseki2/jena-fuseki-webapp/src/main/webapp/js/app/main.dataset.js
@@ -0,0 +1,31 @@
+/** RequireJS dependency configuration for dataset.html page */
+
+define( ['require', '../common-config'],
+ function( require ) {
+ require(
+ ['underscore', 'jquery', 'backbone', 'marionette', 'app/fui', 'app/controllers/dataset-controller',
+ 'sprintf',
+ 'bootstrap-select.min',
+ 'app/controllers/query-controller',
+ 'app/controllers/upload-controller',
+ 'app/models/fuseki-server',
+ 'app/models/dataset',
+ 'app/views/dataset-selector',
+ 'app/views/tabbed-view-manager',
+ 'app/services/ping-service',
+ 'jquery.xdomainrequest',
+ 'jquery.form',
+ 'jquery.fileupload'
+ ],
+ function( _, $, Backbone, Marionette, fui, DatasetController ) {
+ var options = { };
+
+ // initialise the backbone application
+ fui.controllers.datasetController = new DatasetController();
+ fui.start( options );
+
+ // additional services
+ require( 'app/services/ping-service' ).start();
+ });
+ }
+);
http://git-wip-us.apache.org/repos/asf/jena/blob/e8abcbb6/jena-fuseki2/jena-fuseki-webapp/src/main/webapp/js/app/main.index.js
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-webapp/src/main/webapp/js/app/main.index.js b/jena-fuseki2/jena-fuseki-webapp/src/main/webapp/js/app/main.index.js
new file mode 100644
index 0000000..56c8903
--- /dev/null
+++ b/jena-fuseki2/jena-fuseki-webapp/src/main/webapp/js/app/main.index.js
@@ -0,0 +1,24 @@
+
+define( ['require', '../common-config'],
+ function( require ) {
+ require(
+ ['underscore', 'jquery', 'backbone', 'marionette',
+ 'app/fui', 'app/controllers/index-controller',
+ 'sprintf', 'bootstrap',
+ 'app/models/fuseki-server',
+ 'app/models/dataset',
+ 'app/views/dataset-selection-list',
+ 'app/services/ping-service'
+ ],
+ function( _, $, Backbone, Marionette, fui, IndexController ) {
+ var options = { };
+
+ // initialise the backbone application
+ fui.controllers.indexController = new IndexController();
+ fui.start( options );
+
+ // additional services
+ require( 'app/services/ping-service' ).start();
+ });
+ }
+);
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/jena/blob/e8abcbb6/jena-fuseki2/jena-fuseki-webapp/src/main/webapp/js/app/main.manage.js
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-webapp/src/main/webapp/js/app/main.manage.js b/jena-fuseki2/jena-fuseki-webapp/src/main/webapp/js/app/main.manage.js
new file mode 100644
index 0000000..312972c
--- /dev/null
+++ b/jena-fuseki2/jena-fuseki-webapp/src/main/webapp/js/app/main.manage.js
@@ -0,0 +1,27 @@
+
+define( ['require', '../common-config'],
+ function( require ) {
+ require(
+ ['underscore', 'jquery', 'backbone', 'marionette',
+ 'app/fui', 'app/controllers/manage-controller',
+ 'sprintf', 'bootstrap',
+ 'app/models/fuseki-server',
+ 'app/models/dataset',
+ 'app/models/task',
+ 'app/views/dataset-management',
+ 'app/services/ping-service',
+ 'jquery.xdomainrequest'
+ ],
+ function( _, $, Backbone, Marionette, fui, ManageController ) {
+
+ var options = { } ;
+
+ // initialise the backbone application
+ fui.controllers.manageController = new ManageController();
+ fui.start( options );
+
+ // additional services
+ require( 'app/services/ping-service' ).start();
+ });
+ }
+);
http://git-wip-us.apache.org/repos/asf/jena/blob/e8abcbb6/jena-fuseki2/jena-fuseki-webapp/src/main/webapp/js/app/main.validation.js
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-webapp/src/main/webapp/js/app/main.validation.js b/jena-fuseki2/jena-fuseki-webapp/src/main/webapp/js/app/main.validation.js
new file mode 100644
index 0000000..0e30fad
--- /dev/null
+++ b/jena-fuseki2/jena-fuseki-webapp/src/main/webapp/js/app/main.validation.js
@@ -0,0 +1,24 @@
+
+define( ['require', '../common-config'],
+ function( require ) {
+ require(
+ ['underscore', 'jquery', 'backbone', 'marionette',
+ 'app/fui', 'app/controllers/validation-controller',
+ 'sprintf', 'bootstrap',
+ 'app/models/validation-options',
+ 'app/services/ping-service',
+ 'app/services/validation-service',
+ 'jquery.xdomainrequest'
+ ],
+ function( _, $, Backbone, Marionette, fui, ValidationController ) {
+ var options = { } ;
+
+ // initialise the backbone application
+ fui.controllers.validationController = new ValidationController();
+ fui.start( options );
+
+ // additional services
+// require( 'services/ping-service' ).start(); TODO restore
+ });
+ }
+);
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/jena/blob/e8abcbb6/jena-fuseki2/jena-fuseki-webapp/src/main/webapp/js/app/models/dataset-stats.js
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-webapp/src/main/webapp/js/app/models/dataset-stats.js b/jena-fuseki2/jena-fuseki-webapp/src/main/webapp/js/app/models/dataset-stats.js
new file mode 100644
index 0000000..35527c7
--- /dev/null
+++ b/jena-fuseki2/jena-fuseki-webapp/src/main/webapp/js/app/models/dataset-stats.js
@@ -0,0 +1,102 @@
+/**
+ * Backbone model denoting statistics on a dataset
+ */
+define(
+ function( require ) {
+ "use strict";
+
+ var Marionette = require( "marionette" ),
+ Backbone = require( "backbone" ),
+ _ = require( "underscore" ),
+ fui = require( "app/fui" ),
+ sprintf = require( "sprintf" );
+
+ /**
+ * This model represents the statistics available on a one or more datasets
+ */
+ var DatasetStats = Backbone.Model.extend( {
+ initialize: function( dataset, stats ) {
+ this.set( {dataset: dataset, stats: stats} );
+ },
+
+ /** Return the number of datasets we have statistics for */
+ size: function() {
+ return _.keys( datasets() ).length;
+ },
+
+ toJSON: function() {
+ return this.asTable();
+ },
+
+ /** Return a table of the statistics we have, one row per dataset */
+ asTable: function() {
+ var ds = this.datasets();
+ var endpoints = this.collectEndpoints( ds );
+ var rows = [];
+
+ _.each( ds, function( d, dsName ) {
+ var row = [dsName, d.Requests, d.RequestsGood, d.RequestsBad];
+ var es = d.endpoints;
+
+ _.each( endpoints, function( e ) {
+ if (es[e.key]) {
+ var servStats = es[e.key];
+
+ if (servStats.Requests === 0) {
+ row.push( "0" );
+ }
+ else {
+ row.push( sprintf( "%d (%d bad)", servStats.Requests, servStats.RequestsBad ))
+ }
+ }
+ else {
+ row.push( "–" );
+ }
+ } );
+
+ rows.push( row );
+ } );
+
+ return {headings: this.columnHeadings( endpoints ), rows: rows};
+ },
+
+ stats: function() {
+ return this.get( "stats" );
+ },
+
+ datasets: function() {
+ return this.stats().datasets;
+ },
+
+ /** Reload the numbers from the server */
+ refresh: function() {
+ var self = this;
+
+ this.get( "dataset" )
+ .statistics()
+ .done( function( data ) {
+ self.set( "stats", data );
+ } );
+ },
+
+ // internal methods
+
+ collectEndpoints: function( ds ) {
+ var endpoints = [];
+ _.each( ds, function( d ) {
+ var ep = _.each( d.endpoints, function( v, k ) {
+ endpoints.push( {key: k, label: v.description} );
+ } );
+ } );
+
+ return _.uniq( endpoints ).sort();
+ },
+
+ columnHeadings: function( services ) {
+ return ["Name", "Overall", "Overall good", "Overall bad"].concat( _.pluck( services, 'label' ) );
+ }
+ } );
+
+ return DatasetStats;
+ }
+);
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/jena/blob/e8abcbb6/jena-fuseki2/jena-fuseki-webapp/src/main/webapp/js/app/models/dataset.js
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-webapp/src/main/webapp/js/app/models/dataset.js b/jena-fuseki2/jena-fuseki-webapp/src/main/webapp/js/app/models/dataset.js
new file mode 100644
index 0000000..4fc192f
--- /dev/null
+++ b/jena-fuseki2/jena-fuseki-webapp/src/main/webapp/js/app/models/dataset.js
@@ -0,0 +1,262 @@
+/**
+ * Backbone model denoting the remote Fuseki server.
+ */
+define(
+ function( require ) {
+ "use strict";
+
+ var Marionette = require( "marionette" ),
+ Backbone = require( "backbone" ),
+ _ = require( "underscore" ),
+ fui = require( "app/fui" ),
+ sprintf = require( "sprintf" ),
+ Task = require( "app/models/task" );
+
+ /**
+ * This model represents the core representation of the remote Fuseki
+ * server. Individual datasets have their own model.
+ */
+ var Dataset = Backbone.Model.extend( {
+ initialize: function( datasetDescription, baseURL, mgmtURL ) {
+ this.set( datasetDescription );
+ this.set( {
+ baseURL: baseURL,
+ mgmtURL: mgmtURL,
+ counts: {},
+ countPerformed: false,
+ counting: false,
+ statistics: false
+ } );
+ },
+
+ /* x is the empty object if baseURL is ""
+ * Ensure it is always a string.
+ */
+ getStr: function(key) {
+ var x = this.get( key );
+ return jQuery.isEmptyObject(x) ? "" : x ;
+ },
+
+ baseURL: function() {
+ return this.getStr( "baseURL" );
+ },
+
+ mgmtURL: function() {
+ return this.getStr( "mgmtURL" );
+ },
+
+ mgmtActionURL: function() {
+ return this.get( "mgmtURL" ) + this.name();
+ },
+
+ statisticsURL: function() {
+ return sprintf( "%s/$/stats%s", this.baseURL(), this.name() );
+ },
+
+ name: function() {
+ return this.get( "ds.name" );
+ },
+
+ services: function() {
+ return this.get( "ds.services" );
+ },
+
+ countPerformed: function() {
+ return this.get( "countPerformed" );
+ },
+
+ counts: function() {
+ return this.get( "counts" );
+ },
+
+ serviceTypes: function() {
+ return _.map( this.services(), function( s ) {return s["srv.type"];} );
+ },
+
+ /** Return a descriptive data-structure listing all this datasets services */
+ servicesDescription: function() {
+ var description = [];
+ var self = this;
+
+ _.each( this.services(), function( s ) {
+ _.each( s["srv.endpoints"], function( e ) {
+ description.push( {label: s["srv.description"],
+ url: self.datasetEndpointURL( e )
+ } );
+ } );
+ } );
+
+ description.sort( function( d0, d1 ) {
+ return (d0.label < d1.label) ? -1 : (d0.label > d1.label ? 1 : 0);
+ } );
+
+ return description;
+ },
+
+ /** Return the first service that has the given type */
+ serviceOfType: function( serviceType ) {
+ return _.find( this.services(), function( s ) {
+ return s["srv.type"] === serviceType;
+ } );
+ },
+
+ /** Return the first endpoint of the first service that has the given type */
+ endpointOfType: function( serviceType ) {
+ var service = this.serviceOfType( serviceType );
+ return service && _.first( service["srv.endpoints"] );
+ },
+
+ /* Return URL for a service of a given type or null, if no such service */
+ endpointURL: function( serviceType ) {
+ var endpoint = this.endpointOfType( serviceType );
+ return endpoint ? this.datasetEndpointURL( endpoint ) : null;
+ },
+
+ /** Return the URL for the given endpoint */
+ datasetEndpointURL: function( endpoint ) {
+ return sprintf( "%s%s/%s", this.baseURL(), this.name(), endpoint );
+ },
+
+ /** Return the sparql query URL for this dataset, if it has one, or null */
+ queryURL: function() {
+ return this.endpointURL( "Query" ) ;
+ },
+
+ /** Return the sparql query URL for this dataset, if it has one, or null */
+ quadsURL: function() {
+ return this.endpointURL( "Quads" ) ;
+ },
+
+ /** Return the sparql update URL for this dataset, if it has one, or null */
+ updateURL: function() {
+ return this.endpointURL( "Update" ) ;
+ },
+
+ /** Return the GSP write URL for this dataset, if it has one, or null */
+ graphStoreProtocolURL: function() {
+ if ( this.endpointURL( "GSP" ) )
+ // Old name
+ return this.endpointURL( "GSP" ) ;
+ return this.endpointURL( "GSP_RW" ) ;
+ },
+
+ /** Return the GSP read URL for this dataset, if it has one, or null */
+ graphStoreProtocolReadURL: function() {
+ return this.endpointURL( "GSP_R" ) ;
+ },
+
+ /** Return the upload URL for this dataset, if it has one, or null */
+ uploadURL: function( graphName ) {
+ if (this.graphStoreProtocolURL() !== null) {
+ return sprintf( "%s%s", this.graphStoreProtocolURL(), (graphName === "default" ? "" : ("?graph=" + graphName) ));
+ }
+ else {
+ return null;
+ }
+ },
+
+ /** Perform the action to delete the dataset. Returns the Ajax deferred object */
+ deleteDataset: function() {
+ return $.ajax( {
+ url: this.mgmtActionURL(),
+ type: 'DELETE'
+ } );
+ },
+
+ /** Perform the action of taking a backup of this dataset */
+ backupDataset: function() {
+ var backupURL = sprintf( "%s/$/backup%s", this.baseURL(), this.name() );
+ var ds = this;
+
+ return $.ajax( {
+ url: backupURL,
+ type: 'POST'
+ } )
+ .done( function( taskDescription ) {
+ new Task( ds, "backup", taskDescription );
+ } );
+ },
+
+ /**
+ * Request the statistics for this dataset, and return the promise object for the callback.
+ * @param keep If truthy, and we have existing statistics, re-use the existing stats
+ * */
+ statistics: function( keep ) {
+ var self = this;
+ var currentStats = this.get( "statistics" );
+
+ if (currentStats && keep) {
+ return $.Deferred().resolveWith( null, currentStats );
+ }
+ else {
+ return $.getJSON( this.statisticsURL() )
+ .then( function( data ) {
+ self.set( "statistics", data );
+ return data;
+ } );
+ }
+ },
+
+ /** Perform a count query to determine the size of the dataset. Changes the size property when done,
+ * but also returns the JQuery promise object used to monitor the query response */
+ count: function() {
+ var self = this;
+ var query1 = sprintf( "select (count(*) as ?count) {?s ?p ?o}" );
+ var query2 = sprintf( "select ?g (count(*) as ?count) {graph ?g {?s ?p ?o}} group by ?g" );
+
+ self.set( "counting", true );
+
+ var updateCount = function( model, result, graph ) {
+ var n = parseInt( result.count.value );
+ var counts = _.extend( {}, model.get( "counts" ) );
+ counts[graph] = n;
+ model.set( "counts", counts );
+ };
+
+ $.getJSON( self.queryURL(), { query: query1 } )
+ .done( function( data ) {
+ updateCount( self, data.results.bindings[0], "default graph" );
+
+ $.getJSON( self.queryURL(), { query: query2 } )
+ .done( function( data ) {
+ _.each( data.results.bindings, function( binding ) {
+ if (binding.g) {
+ updateCount( self, binding, binding.g.value );
+ }
+ } );
+ } );
+
+ self.set( {countPerformed: true, counting: false} );
+ } );
+ },
+
+ /**
+ * Fetch the content of the given graph as Turtle. Return the jQuery promise
+ * object for the Ajax call.
+ */
+ fetchGraph: function( graphName ) {
+ return $.ajax( this.graphStoreProtocolReadURL(),
+ {method: "get",
+ headers: {Accept : "text/turtle; charset=utf-8"},
+ dataType: "html",
+ data: {graph: graphName}
+ } );
+ },
+
+ /**
+ * Put the given Turtle content back to the server using the given graph name
+ */
+ putGraph: function( turtle, graphName ) {
+ return $.ajax( sprintf( "%s?graph=%s", this.graphStoreProtocolURL(), graphName ),
+ {method: "put",
+ dataType: "json",
+ data: turtle,
+ contentType: "text/turtle; charset=uft-8"
+ } );
+ }
+
+ } );
+
+ return Dataset;
+ }
+);
http://git-wip-us.apache.org/repos/asf/jena/blob/e8abcbb6/jena-fuseki2/jena-fuseki-webapp/src/main/webapp/js/app/models/fuseki-server.js
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-webapp/src/main/webapp/js/app/models/fuseki-server.js b/jena-fuseki2/jena-fuseki-webapp/src/main/webapp/js/app/models/fuseki-server.js
new file mode 100644
index 0000000..336d0c7
--- /dev/null
+++ b/jena-fuseki2/jena-fuseki-webapp/src/main/webapp/js/app/models/fuseki-server.js
@@ -0,0 +1,180 @@
+/**
+ * Backbone model denoting the remote Fuseki server.
+ */
+
+define(
+ function( require ) {
+ "use strict";
+
+ var Marionette = require( "marionette" ),
+ Backbone = require( "backbone" ),
+ _ = require( "underscore" ),
+ fui = require( "app/fui" ),
+ sprintf = require( "sprintf" ),
+ Dataset = require( "app/models/dataset" ),
+ PageUtils = require( "app/util/page-utils" );
+
+ var DATASETS_MANAGEMENT_PATH = "/$/datasets";
+
+ /**
+ * This model represents the core representation of the remote Fuseki
+ * server. Individual datasets have their own model.
+ */
+ var FusekiServer = Backbone.Model.extend( {
+ /** This initializer occurs when the module starts, not when the constructor is invoked */
+ init: function( options ) {
+ this._baseURL = this.currentRootPath();
+ this._managementURL = null;
+ this.set( "selectedDatasetName", PageUtils.queryParam( "ds" ) )
+ },
+
+ baseURL: function() {
+ return this._baseURL;
+ },
+
+ /** Return the URL from which we extract the details of the current server */
+ serverDetailsURL: function() {
+ return sprintf( "%s/$/server", this.baseURL() );
+ },
+
+ /** Return the URL for issuing commands to the management API, or null if no API defined */
+ managementURL: function() {
+ return this._managementURL;
+ },
+
+ /** Return the URL for getting the stats for all datasets */
+ statsURL: function() {
+ return sprintf( "%s/$/stats", this.managementURL() );
+ },
+
+ /** Return the list of datasets that this server knows about. Each dataset will be a Dataset model object */
+ datasets: function() {
+ return this.get( "datasets" );
+ },
+
+ /** Return the dataset with the given name */
+ dataset: function( dsName ) {
+ return _.find( this.datasets(), function( ds ) {return dsName === ds.name();} )
+ },
+
+ /** Return the name of the currently selected dataset, if known */
+ selectedDatasetName: function() {
+ return this.get( "selectedDatasetName" );
+ },
+
+ /** Return the dataset that is currently selected, or null */
+ selectedDataset: function() {
+ var dsName = this.selectedDatasetName();
+ return dsName && this.dataset( dsName );
+ },
+
+ /** Load and cache the remote server description. Trigger change event when done */
+ loadServerDescription: function() {
+ var self = this;
+ return this.getJSON( this.serverDetailsURL() )
+ .done( function( data ) {
+ self.saveServerDescription( data );
+ } )
+ .then( function() {
+ fui.vent.trigger( "models.fuseki-server.ready" );
+ });
+ },
+
+ /** Store the server description in this model */
+ saveServerDescription: function( serverDesc ) {
+ // wrap each dataset JSON description as a dataset model
+ var bURL = this.baseURL();
+ var mgmtURL = bURL;
+
+ if (serverDesc.admin) {
+ // This is too simple. window.location.port may be empty and matches protocol.
+ //mgmtURL = bURL.replace( ":" + window.location.port, ":" + serverDesc.admin.port );
+ //console.log("managementURL -- s/"+window.location.port+"/"+serverDesc.admin.port+"/") ;
+ var path = window.location.pathname.replace( /\/[^/]*$/, "" ) ;
+ mgmtURL = sprintf( "%s//%s:%s%s", window.location.protocol, window.location.hostname, serverDesc.admin, path );
+ }
+ this._managementURL = mgmtURL ;
+
+ var datasets = _.map( serverDesc.datasets, function( d ) {
+ return new Dataset( d, bURL, mgmtURL + DATASETS_MANAGEMENT_PATH );
+ } );
+
+ datasets.sort( function( ds0, ds1 ) {
+ if (ds0.name() > ds1.name()) {
+ return 1;
+ }
+ else if (ds0.name() < ds1.name()) {
+ return -1;
+ }
+ else
+ return 0;
+ } );
+
+ this.set( {
+ serverDescription: serverDesc,
+ datasets: datasets,
+ ready: true
+ } );
+ },
+
+ /**
+ * Get the given relative path from the server, and return a promise object which will
+ * complete with the JSON object denoted by the path.
+ */
+ getJSON: function( path, data ) {
+ return $.getJSON( path, data );
+ },
+
+ /** Update or create a dataset by posting to its endpoint */
+ updateOrCreateDataset: function( datasetId, data ) {
+ var url = sprintf( "%s/$/datasets%s", this.managementURL(),
+ datasetId ? ("/" + datasetId) : ""
+ );
+
+ return $.ajax( url,
+ { data: data,
+ method: "post"
+ }
+ );
+ },
+
+ /** Extract the server root path from the current window href
+ * This is the path, from /, without protocol, host or port.
+ * Then the browser adds protocol, host or port.
+ * Sometimes, the app does not know URL the browser used.
+ * For example, in docker, the port may have been remapped,
+ * or with a reverse proxy, https may have been terminated.
+ */
+ currentRootPath: function() {
+ var path = window.location.pathname.replace( /\/[^/]*$/, "" );
+ /*
+ console.log("window.location="+window.location) ;
+ console.log("window.location.href="+window.location.href) ;
+
+ console.log("window.location.protocol="+window.location.protocol) ;
+ console.log("window.location.host="+window.location.host) ;
+ console.log("window.location.hostname="+window.location.hostname) ;
+ console.log("window.location.port="+window.location.port) ;
+ console.log("window.location.pathname="+window.location.pathname) ;
+
+ console.log("window.location.origin="+window.location.origin) ;
+ console.log("window.location.hash="+window.location.hash) ;
+ console.log("window.location.search="+window.location.search) ;
+ console.log("path='"+path+"'") ;
+ */
+ return path ;
+ }
+ } );
+
+ // when the models module starts, automatically load the server description
+ fui.models.addInitializer( function( options ) {
+ var fusekiServer = new FusekiServer();
+ fui.models.fusekiServer = fusekiServer;
+
+ fusekiServer.init( options );
+ fusekiServer.loadServerDescription();
+ } );
+
+ return FusekiServer;
+ }
+);
http://git-wip-us.apache.org/repos/asf/jena/blob/e8abcbb6/jena-fuseki2/jena-fuseki-webapp/src/main/webapp/js/app/models/task.js
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-webapp/src/main/webapp/js/app/models/task.js b/jena-fuseki2/jena-fuseki-webapp/src/main/webapp/js/app/models/task.js
new file mode 100644
index 0000000..10c0563
--- /dev/null
+++ b/jena-fuseki2/jena-fuseki-webapp/src/main/webapp/js/app/models/task.js
@@ -0,0 +1,105 @@
+/**
+ * A long-running task, which periodically pings the server for its task status
+ */
+
+define(
+ function( require ) {
+ "use strict";
+
+ var Marionette = require( "marionette" ),
+ Backbone = require( "backbone" ),
+ _ = require( "underscore" ),
+ fui = require( "app/fui" ),
+ sprintf = require( "sprintf" );
+
+ /* Constants */
+
+ var MS = 1000;
+ var MAX_DELAY = 10 * MS;
+
+ /**
+ * This model represents a long running task process
+ */
+ var Task = function( ds, operationType, taskDescription ) {
+ this.taskDescription = taskDescription;
+ this.ds = ds;
+ this.operationType = operationType;
+ this.delay = 500;
+
+ _.bindAll( this, "checkTaskStatus", "onCurrentTaskStatusFail", "onCurrentTaskStatus" );
+
+ this.checkTaskStatus();
+ };
+
+ _.extend( Task.prototype, {
+ /** Return the unique ID (on this server) of the task */
+ taskId: function() {
+ return this.taskDescription.taskId;
+ },
+
+ /** Return the URL for the task's API */
+ taskURL: function() {
+ return sprintf( "%s/$/tasks/%s", this.ds.baseURL(), this.taskId() );
+ },
+
+ /** Test the current status of the task */
+ checkTaskStatus: function() {
+ $.getJSON( this.taskURL() )
+ .done( this.onCurrentTaskStatus )
+ .fail( this.onCurrentTaskStatusFail )
+ },
+
+ /** Successful result from checking the task */
+ onCurrentTaskStatus: function( taskDescription ) {
+ this.taskDescription = taskDescription;
+
+ var status = {
+ task: this,
+ dsId: this.ds.name(),
+ finished: this.taskFinished()
+ };
+
+ fui.vent.trigger( "task.status", status );
+
+ this.queueTaskStatusCheck();
+ },
+
+ /** Failed to check the task */
+ onCurrentTaskStatusFail: function( jqxhr, msg, err ) {
+ var status = {
+ task: this,
+ dsId: this.ds.name(),
+ errorMessage: err || msg
+ };
+
+ fui.vent.trigger( "task.failed", status );
+ },
+
+ /** Re-queue the status check if the task is not yet complete */
+ queueTaskStatusCheck: function() {
+ if (!this.taskFinished()) {
+ _.delay( this.checkTaskStatus, this.statusDelay() );
+ }
+ },
+
+ /** Return the completion time if the task has been fid, otherwise null */
+ taskFinished: function() {
+ return this.taskDescription.finished;
+ },
+
+ /** Return the delay in ms until the next status check is due. */
+ statusDelay: function() {
+ var t = this.delay;
+
+ if (t < MAX_DELAY) {
+ this.delay = t * 2;
+ }
+
+ return t;
+ }
+ } );
+
+ return Task;
+ }
+);
+
http://git-wip-us.apache.org/repos/asf/jena/blob/e8abcbb6/jena-fuseki2/jena-fuseki-webapp/src/main/webapp/js/app/models/validation-options.js
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-webapp/src/main/webapp/js/app/models/validation-options.js b/jena-fuseki2/jena-fuseki-webapp/src/main/webapp/js/app/models/validation-options.js
new file mode 100644
index 0000000..b114cf9
--- /dev/null
+++ b/jena-fuseki2/jena-fuseki-webapp/src/main/webapp/js/app/models/validation-options.js
@@ -0,0 +1,85 @@
+/**
+ * Backbone model denoting the remote Fuseki server.
+ */
+define(
+ function( require ) {
+ "use strict";
+
+ var Marionette = require( "marionette" ),
+ Backbone = require( "backbone" ),
+ _ = require( "underscore" ),
+ fui = require( "app/fui" ),
+ sprintf = require( "sprintf" );
+
+ /**
+ * This model represents the users current choice of options to the
+ * validation service.
+ */
+ var ValidationOptions = Backbone.Model.extend( {
+ initialize: function() {
+ this.set( {validateAs: "sparql"} );
+ this.set( {outputFormat: "algebra"} );
+ },
+
+ validateAs: function() {
+ return this.get( "validateAs" );
+ },
+
+ validateAsQuery: function() {
+ return this.validateAs() === "sparql" || this.validateAs() === "arq";
+ },
+
+ setValidateAs: function( va ) {
+ this.set( "validateAs", va );
+ console.log( JSON.stringify( this.toJSON() ));
+ console.log( "----" );
+ },
+
+ outputFormat: function() {
+ return this.get( "outputFormat" );
+ },
+
+ setOutputFormat: function( of ) {
+ this.set( "outputFormat", of );
+ },
+
+ validationURL: function() {
+ switch (this.get( "validateAs" )) {
+ case "sparql": return "/validate/query";
+ case "arq": return "/validate/query";
+ case "Turtle": return "/validate/data";
+ case "TriG": return "/validate/data";
+ case "N-Triples": return "/validate/data";
+ case "N-Quads": return "/validate/data";
+ }
+ },
+
+ payloadParam: function() {
+ return this.validateAsQuery() ? "query" : "data";
+ },
+
+ toJSON: function() {
+ var json = {
+ languageSyntax: this.validateAs(),
+ lineNumbers: true
+ };
+
+ if (this.validateAsQuery()) {
+ json.outputFormat = this.outputFormat();
+ }
+
+ return json;
+ }
+
+ } );
+
+ // when the models module starts, create the model
+ fui.models.addInitializer( function( options ) {
+ fui.models.validationOptions = new ValidationOptions();
+ fui.vent.trigger( "models.validation-options.ready" );
+ } );
+
+
+ return ValidationOptions;
+ }
+);
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/jena/blob/e8abcbb6/jena-fuseki2/jena-fuseki-webapp/src/main/webapp/js/app/qonsole-config.js
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-webapp/src/main/webapp/js/app/qonsole-config.js b/jena-fuseki2/jena-fuseki-webapp/src/main/webapp/js/app/qonsole-config.js
new file mode 100644
index 0000000..0c48273
--- /dev/null
+++ b/jena-fuseki2/jena-fuseki-webapp/src/main/webapp/js/app/qonsole-config.js
@@ -0,0 +1,27 @@
+/** Standalone configuration for qonsole on index page */
+
+define( [], function() {
+ return {
+ prefixes: {
+ "rdf": "http://www.w3.org/1999/02/22-rdf-syntax-ns#",
+ "rdfs": "http://www.w3.org/2000/01/rdf-schema#",
+ "owl": "http://www.w3.org/2002/07/owl#",
+ "xsd": "http://www.w3.org/2001/XMLSchema#"
+ },
+ queries: [
+ { "name": "Selection of triples",
+ "query": "SELECT ?subject ?predicate ?object\nWHERE {\n" +
+ " ?subject ?predicate ?object\n}\n" +
+ "LIMIT 25"
+ },
+ { "name": "Selection of classes",
+ "query": "SELECT DISTINCT ?class ?label ?description\nWHERE {\n" +
+ " ?class a owl:Class.\n" +
+ " OPTIONAL { ?class rdfs:label ?label}\n" +
+ " OPTIONAL { ?class rdfs:comment ?description}\n}\n" +
+ "LIMIT 25",
+ "prefixes": ["owl", "rdfs"]
+ }
+ ]
+ };
+} );
http://git-wip-us.apache.org/repos/asf/jena/blob/e8abcbb6/jena-fuseki2/jena-fuseki-webapp/src/main/webapp/js/app/services/ping-service.js
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-webapp/src/main/webapp/js/app/services/ping-service.js b/jena-fuseki2/jena-fuseki-webapp/src/main/webapp/js/app/services/ping-service.js
new file mode 100644
index 0000000..75ef6a4
--- /dev/null
+++ b/jena-fuseki2/jena-fuseki-webapp/src/main/webapp/js/app/services/ping-service.js
@@ -0,0 +1,54 @@
+/**
+ * The ping service checks the status of the attached server and sets the light in the
+ * control bar accordingly.
+ */
+define( ['jquery', 'underscore', 'sprintf'],
+ function( $, _, sprintf ) {
+
+ var PING_URL = "$/ping"
+ var DEFAULT_PING_TIME = 500000; // TODO slowed down during debugging phase
+ var _startTime = 0;
+
+ var onBeforeSend = function() {
+ _startTime = new Date().getTime();
+ };
+
+ var duration = function() {
+ return new Date().getTime() - _startTime;
+ };
+
+ var onPingSuccess = function( ) {
+ setPingStatus( "server-up", sprintf( "Last ping returned OK in %dms", duration() ) );
+ };
+
+ var onPingFail = function( jqXHR, msg, errorThrown ) {
+ setPingStatus( "server-down", sprintf( "Last ping returned '%s' in %dms", errorThrown || msg, duration() ) );
+ };
+
+ var setPingStatus = function( lampClass, statusText ) {
+ $( "a#server-status-light span").removeClass()
+ .addClass( lampClass )
+ .attr( "title", statusText );
+ };
+
+ /** Return a cache-defeating ping URL */
+ var ping_url = function() {
+ return PING_URL + "?_=" + Math.random();
+ };
+
+ var start = function( period ) {
+ ping( period || DEFAULT_PING_TIME );
+ };
+
+ var ping = function( period ) {
+ onBeforeSend();
+ $.get( ping_url() ).done( onPingSuccess )
+ .fail( onPingFail );
+ setTimeout( function() {ping( period );}, period );
+ };
+
+ return {
+ start: start
+ }
+ }
+);
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/jena/blob/e8abcbb6/jena-fuseki2/jena-fuseki-webapp/src/main/webapp/js/app/services/validation-service.js
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-webapp/src/main/webapp/js/app/services/validation-service.js b/jena-fuseki2/jena-fuseki-webapp/src/main/webapp/js/app/services/validation-service.js
new file mode 100644
index 0000000..1a5d919
--- /dev/null
+++ b/jena-fuseki2/jena-fuseki-webapp/src/main/webapp/js/app/services/validation-service.js
@@ -0,0 +1,98 @@
+define( ['underscore', 'jquery', 'fui',
+ 'lib/codemirror', 'mode/javascript/javascript', 'mode/sparql/sparql'],
+ function( _, $, fui, CodeMirror ) {
+
+ var ValidationService = function( editor_el, output_el ) {
+ this.editor_el = editor_el;
+ this.output_el = output_el;
+ };
+
+ _.extend( ValidationService.prototype, {
+ init: function() {
+ _.bindAll( this, "handleValidationOutput", "handleJsonValidationOutput" );
+ this.editorElement();
+ },
+
+ /** Return the DOM node representing the query editor */
+ editorElement: function() {
+ if (!this._editor) {
+ this._editor = new CodeMirror( $(this.editor_el).get(0), {
+ lineNumbers: true,
+ mode: "text"
+ } );
+ }
+ return this._editor;
+ },
+
+ /** Return the DOM node representing the output editor */
+ outputElement: function( mode, lineNumbers, data ) {
+ $(this.output_el).empty();
+
+ var cm = new CodeMirror( $(this.output_el).get(0), {
+ lineNumbers: lineNumbers,
+ mode: mode || "text",
+ readOnly: true,
+ value: data
+ } );
+
+ return cm;
+ },
+
+ /** Return the current code editor contents */
+ editorContent: function() {
+ return this.editorElement().getValue();
+ },
+
+ /** Perform the given action to validate the current content */
+ performValidation: function( optionsModel ) {
+ var context = {optionsModel: optionsModel};
+ var self = this;
+
+ var content = {};
+ content[optionsModel.payloadParam()] = this.editorContent();
+
+ var options = {
+ data: _.extend( optionsModel.toJSON(), content ),
+ type: "POST"
+ };
+
+ $.ajax( optionsModel.validationURL(), options )
+ .done( function( data, status, xhr ) {
+ self.handleValidationOutput( data, status, xhr, context );
+ } );
+ },
+
+ /** Respond to validation output from the server */
+ handleValidationOutput: function( data, status, xhr, context ) {
+ var ct = xhr.getResponseHeader("content-type") || "";
+ if (ct.match( /json/ )) {
+ this.handleJsonValidationOutput( data, context );
+ }
+ else {
+ // in HTML output, we look for a .error div
+ var errors = $(data).find( "div.error" ).text();
+ this.outputElement( "text", true, errors || "No warnings or errors reported." );
+ }
+ },
+
+ handleJsonValidationOutput: function( json, context ) {
+ var outputFormat = context.optionsModel.outputFormat();
+ console.log( "output format = " + outputFormat );
+ var jsonString = null;
+
+ if (outputFormat && json[outputFormat]) {
+ jsonString = json[outputFormat];
+ }
+ else {
+ jsonString = JSON.stringify( json, null, ' ' );
+ }
+
+ this.outputElement( "application/json", false, jsonString );
+ }
+
+ } );
+
+
+ return ValidationService;
+ }
+);
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/jena/blob/e8abcbb6/jena-fuseki2/jena-fuseki-webapp/src/main/webapp/js/app/templates/dataset-edit.tpl
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-webapp/src/main/webapp/js/app/templates/dataset-edit.tpl b/jena-fuseki2/jena-fuseki-webapp/src/main/webapp/js/app/templates/dataset-edit.tpl
new file mode 100644
index 0000000..8202831
--- /dev/null
+++ b/jena-fuseki2/jena-fuseki-webapp/src/main/webapp/js/app/templates/dataset-edit.tpl
@@ -0,0 +1,58 @@
+<div class="row">
+ <div class="col-md-4">
+ <div class="bordered-box">
+ <span class="pull-right">
+ <button class="btn btn-sm btn-info action list-graphs">list current graphs</button>
+ </span>
+ <h3>Available graphs</h3>
+
+ <% if (countPerformed()) { %>
+ <ul class="nav nav-pills nav-stacked graphs">
+ <% _.each( counts(), function( n, g ) { %>
+ <li class="">
+ <a href="#" class="select-dataset" data-graph-name="<%= g %>" data-graph-size="<%= n %>">
+ <%= g %> (<%= n %> triples)
+ </a>
+ </li>
+ <% } ); %>
+ </ul>
+ <% } else { %>
+ <p class="text-muted text-sm">Click to list current graphs</p>
+ <% } %>
+ </div> <!-- /.bordered-box -->
+ </div> <!-- /.col-md-4 -->
+
+ <div class="col-md-8">
+ <div class="row">
+ <div class="col-md-12">
+ <div class="form-group">
+ <div class="input-group">
+ <div class="input-group-addon">graph:</div>
+ <input class="form-control graph-name" type="text" placeholder="">
+ </div>
+ </div>
+ </div>
+ </div> <!-- /.row -->
+
+ <div class="row">
+ <div class="col-md-12">
+ <div id="graph-editor" class="bordered-box"></div>
+ </div>
+ </div> <!-- /.row -->
+
+ <div class="row">
+ <div class="col-md-12">
+ <p class="feedback"></p>
+ </div>
+ </div> <!-- /.row -->
+
+ <div class="row">
+ <div class="col-md-12">
+ <span class="pull-right">
+ <button class="btn btn-default action cancel-edit"><i class="fa fa-times"></i> discard changes</button>
+ <button class="btn btn-info action save-edit"><i class="fa fa-check"></i> save</button>
+ </span>
+ </div>
+ </div> <!-- /.row -->
+ </div> <!-- /.col-md-8 -->
+</div>
http://git-wip-us.apache.org/repos/asf/jena/blob/e8abcbb6/jena-fuseki2/jena-fuseki-webapp/src/main/webapp/js/app/templates/dataset-info.tpl
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-webapp/src/main/webapp/js/app/templates/dataset-info.tpl b/jena-fuseki2/jena-fuseki-webapp/src/main/webapp/js/app/templates/dataset-info.tpl
new file mode 100644
index 0000000..c2c6891
--- /dev/null
+++ b/jena-fuseki2/jena-fuseki-webapp/src/main/webapp/js/app/templates/dataset-info.tpl
@@ -0,0 +1,40 @@
+<h2>Available services</h2>
+
+<dl class="dl-horizontal">
+ <% _.each( servicesDescription(), function( serviceDescription ) { %>
+ <dt>
+ <%= serviceDescription.label %>:
+ </dt>
+ <dd>
+ <a href="<%= serviceDescription.url %>"><%= serviceDescription.url %></a>
+ </dd>
+ <% } ); %>
+</dl>
+
+<h2>Statistics</h2>
+<div id="statistics"></div>
+
+<h2>Dataset size</h2>
+<p>
+<strong>Note</strong> this may be slow and impose a significant load on large datasets:
+<button href="#" class="action count-graphs btn btn-primary">count triples in all graphs</button>
+</p>
+<% if (countPerformed()) { %>
+<dl class="dl-horizontal">
+ <dt><span class="heading">graph name:</span></dt><dd><span class="heading">triples:</span></dd>
+ <% _.each( counts(), function( n, g ) { %>
+ <dt class="font-weight-normal">
+ <%= g %>
+ </dt>
+ <dd>
+ <div class="numeric"><%= n %></div>
+ </dd>
+ <% } ); %>
+</dl>
+
+<% } %>
+
+<h2>Ongoing operations</h2>
+
+<p><em>TBD. Will list any long-lasting operations that are ongoing or recently completed,
+e.g. backups.</em></p>
http://git-wip-us.apache.org/repos/asf/jena/blob/e8abcbb6/jena-fuseki2/jena-fuseki-webapp/src/main/webapp/js/app/templates/dataset-management.tpl
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-webapp/src/main/webapp/js/app/templates/dataset-management.tpl b/jena-fuseki2/jena-fuseki-webapp/src/main/webapp/js/app/templates/dataset-management.tpl
new file mode 100644
index 0000000..9a01812
--- /dev/null
+++ b/jena-fuseki2/jena-fuseki-webapp/src/main/webapp/js/app/templates/dataset-management.tpl
@@ -0,0 +1,62 @@
+<% if (datasets.length === 0) { %>
+ <p>No datasets have been created yet.
+ <a class="btn btn-sm btn-primary" href="?tab=new-dataset">add one</a>
+ </p>
+<% } else { %>
+ <div class="row">
+ <div class="col-md-12">
+ <table class='table'>
+ <tr class="headings">
+ <th>Name</th>
+ <!-- JENA-867 <th>Active?</th> -->
+ <th></th>
+ </tr>
+ <% _.each( datasets, function( ds ) { %>
+ <tr>
+ <td>
+ <%= ds.name() %>
+ </td>
+ <!-- JENA-867 temporarily disable non-functional checkbox
+ <td>
+ <input type='checkbox' class='checkbox' checked />
+ </td>
+ -->
+ <td>
+ <div>
+ <!-- JENA-869 Disable download button until it works again -->
+ <a class="btn btn-sm action remove btn-primary" data-ds-id='<%= ds.name() %>'><i class='fa fa-times-circle'></i> remove</a>
+ <a class="btn btn-sm action backup btn-primary" data-ds-id='<%= ds.name() %>'><i class='fa fa-download'></i> backup</a>
+ <a class="btn btn-sm action add-data btn-primary" href="dataset.html?tab=upload&ds=<%= ds.name() %>"><i class='fa fa-upload'></i> upload data</a>
+ </div>
+ <div class="action feedback"></a>
+ </td>
+ </tr>
+ <% }) %>
+
+ </table>
+ </div>
+ </div>
+<% } %>
+
+<!-- Modal dialogs -->
+
+<div class="modal fade" id="actionConfirmModal" tabindex="-1" role="dialog" aria-labelledby="actionConfirmModalLabel" aria-hidden="true">
+ <div class="modal-dialog">
+ <div class="modal-content">
+ <div class="modal-header">
+ <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
+ <h4 class="modal-title" id="actionConfirmModalLabel">Confirm action</h4>
+ </div>
+ <div class="modal-body">
+ <p></p>
+ </div>
+ <div class="modal-footer">
+ <button type="button" class="btn btn-default" data-dismiss="modal"><i class="fa fa-icon-remove"></i> Cancel</button>
+ <button type="button" class="btn btn-primary action confirm">
+ <i class="fa fa-icon-confirm"></i>
+ Confirm <span class="action-label">action</span>
+ </button>
+ </div>
+ </div><!-- /.modal-content -->
+ </div><!-- /.modal-dialog -->
+</div><!-- /.modal -->
http://git-wip-us.apache.org/repos/asf/jena/blob/e8abcbb6/jena-fuseki2/jena-fuseki-webapp/src/main/webapp/js/app/templates/dataset-selection-list.tpl
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-webapp/src/main/webapp/js/app/templates/dataset-selection-list.tpl b/jena-fuseki2/jena-fuseki-webapp/src/main/webapp/js/app/templates/dataset-selection-list.tpl
new file mode 100644
index 0000000..7eda02d
--- /dev/null
+++ b/jena-fuseki2/jena-fuseki-webapp/src/main/webapp/js/app/templates/dataset-selection-list.tpl
@@ -0,0 +1,22 @@
+<div class="col-md-span-12">
+ <% if (datasets.length > 0) { %>
+ <table class='table ijd'>
+ <tr class="headings"><th>dataset name</th><th>actions</th></tr>
+ <% _.each( datasets, function( ds ) { %>
+ <tr>
+ <td>
+ <%= ds.name() %>
+ </td>
+ <td>
+ <a class="btn btn-sm action remove btn-primary" href="dataset.html?tab=query&ds=<%= ds.name() %>"><i class='fa fa-question-circle'></i> query</a>
+ <a class="btn btn-sm action remove btn-primary" href="dataset.html?tab=upload&ds=<%= ds.name() %>"><i class='fa fa-upload'></i> add data</a>
+ <a class="btn btn-sm action configure btn-primary" href="dataset.html?tab=info&ds=<%= ds.name() %>"><i class='fa fa-dashboard'></i> info</a>
+ </td>
+ </tr>
+ <% }) %>
+
+ </table>
+ <% } else { %>
+ <p>There are no datasets on this server yet. <a href="manage.html?tab=new-dataset">Add one.</a></p>
+ <% } %>
+</div>
http://git-wip-us.apache.org/repos/asf/jena/blob/e8abcbb6/jena-fuseki2/jena-fuseki-webapp/src/main/webapp/js/app/templates/dataset-selector.tpl
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-webapp/src/main/webapp/js/app/templates/dataset-selector.tpl b/jena-fuseki2/jena-fuseki-webapp/src/main/webapp/js/app/templates/dataset-selector.tpl
new file mode 100644
index 0000000..d684995
--- /dev/null
+++ b/jena-fuseki2/jena-fuseki-webapp/src/main/webapp/js/app/templates/dataset-selector.tpl
@@ -0,0 +1,15 @@
+<div class='dataset-selector'>
+ <% if (datasets.length == 0) { %>
+ <% } else { %>
+ <label class="">
+ <div class="select-picker-label">Dataset:</div>
+ <select class='selectpicker show-tick'>
+ <% _.each( datasets, function( ds ) { %>
+ <option <%= (ds.name() === selectedDatasetName) ? "selected" : "" %>>
+ <%= ds.name() %>
+ </option>
+ <% } ); %>
+ </select>
+ </label>
+ <% } %>
+</div>
http://git-wip-us.apache.org/repos/asf/jena/blob/e8abcbb6/jena-fuseki2/jena-fuseki-webapp/src/main/webapp/js/app/templates/dataset-simple-create.tpl
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-webapp/src/main/webapp/js/app/templates/dataset-simple-create.tpl b/jena-fuseki2/jena-fuseki-webapp/src/main/webapp/js/app/templates/dataset-simple-create.tpl
new file mode 100644
index 0000000..3819bdc
--- /dev/null
+++ b/jena-fuseki2/jena-fuseki-webapp/src/main/webapp/js/app/templates/dataset-simple-create.tpl
@@ -0,0 +1,85 @@
+<div class="row">
+ <div class="col-md-12">
+ <div class="" id="simple-edit">
+ <form class="form-horizontal" role="form">
+ <div class="form-group">
+ <label for="datasetName" class="col-sm-2 control-label">Dataset name</label>
+ <div class="col-sm-10">
+ <div class="validation-warning dbNameValidation">A name for the dataset is required</div>
+ <input type="text" class="form-control" name="dbName" placeholder="dataset name" />
+ </div>
+ </div>
+ <div class="form-group">
+ <label class="col-sm-2 control-label">Dataset type</label>
+ <div class="col-sm-10">
+ <div class="radio">
+ <label>
+ <input type="radio" name="dbType" value="mem" checked>
+ In-memory – dataset will be recreated when Fuseki restarts, but contents will be lost
+ </label>
+ </div>
+ <div class="radio">
+ <label>
+ <input type="radio" name="dbType" value="tdb">
+ Persistent – dataset will persist across Fuseki restarts
+ </label>
+ </div>
+ <div class="radio">
+ <label>
+ <input type="radio" name="dbType" value="tdb2">
+ Persistent (TDB2) – dataset will persist across Fuseki restarts
+ </label>
+ </div>
+ </div>
+ </div>
+
+ <div class="row">
+ <div class="col-md-12">
+ <div class="errorOutput bg-warning"></div>
+ </div>
+ </div>
+
+ <div class="row controls">
+ <div class="col-md-3 col-md-offset-9">
+ <a href="#" class="btn btn-sm btn-primary action commit simple"><i class="fa fa-check"></i> create dataset</a>
+ </div>
+ </div>
+ </form>
+ </div>
+
+ <!--
+ <div class="tab-pane" id="upload">
+ <p> </p>
+ <div class="row">
+ <p class="col-sm-12">If you have a Fuseki config file (i.e. a Jena assembler description),
+ you can upload it here:</p>
+ </div>
+ <div class="row controls">
+ <form id="uploadForm" method="post" action="$/datasets" class="form-horizontal col-sm-12">
+ <div class="form-group">
+ <label for="assemblerFile" class="col-sm-2 control-label">Configuration file</label>
+ <div class="col-sm-10">
+ <div class="validation-warning assemblerFileValidation">A file name is required</div>
+ <input type="file" class="form-control" name="assemblerFile" />
+ </div>
+ </div>
+ </form>
+ </div>
+
+ <div class="row">
+ <div class="errorOutput col-sm-12"></div>
+ </div>
+
+ <div class="row">
+ <div class="col-sm-12">
+ <a href="admin-data-management.html" class="btn btn-sm btn-default"><i class="fa fa-mail-reply"></i> cancel</a>
+ <a href="#" class="btn btn-sm btn-primary action upload"><i class="fa fa-upload"></i> upload config file</a>
+ </div>
+ </div>
+ </div>
+ -->
+
+ </div><!-- /.col-md-12 -->
+</div><!-- /.row -->
+
+
http://git-wip-us.apache.org/repos/asf/jena/blob/e8abcbb6/jena-fuseki2/jena-fuseki-webapp/src/main/webapp/js/app/templates/dataset-stats.tpl
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-webapp/src/main/webapp/js/app/templates/dataset-stats.tpl b/jena-fuseki2/jena-fuseki-webapp/src/main/webapp/js/app/templates/dataset-stats.tpl
new file mode 100644
index 0000000..fc3d7d9
--- /dev/null
+++ b/jena-fuseki2/jena-fuseki-webapp/src/main/webapp/js/app/templates/dataset-stats.tpl
@@ -0,0 +1,14 @@
+<table class="table">
+ <tr>
+ <% _.each( headings, function( h ) { %>
+ <th class="text-right"><%= h %></th>
+ <% } ); %>
+ </tr>
+ <% _.each( rows, function( row ) { %>
+ <tr>
+ <% _.each( row, function( cell ) { %>
+ <td class="text-right"><%= cell %></td>
+ <% } ); %>
+ </tr>
+ <% } ) %>
+</table>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/jena/blob/e8abcbb6/jena-fuseki2/jena-fuseki-webapp/src/main/webapp/js/app/templates/file-upload.tpl
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-webapp/src/main/webapp/js/app/templates/file-upload.tpl b/jena-fuseki2/jena-fuseki-webapp/src/main/webapp/js/app/templates/file-upload.tpl
new file mode 100644
index 0000000..03d7dcc
--- /dev/null
+++ b/jena-fuseki2/jena-fuseki-webapp/src/main/webapp/js/app/templates/file-upload.tpl
@@ -0,0 +1,46 @@
+<div class="col-md-12 ful">
+ <h2>Upload files</h2>
+ <p class="text-muted">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>
+
+ <div class="row">
+ <div class="form-group2 graph-label">
+ <label for="uploadGraphName" class="col-sm-3 control-label">Destination graph name</label>
+ <span class="col-sm-9">
+ <p>
+ <input type="text" name="graph" id="graphName" placeholder="Leave blank for default graph" class="form-control">
+ </p>
+ </span>
+ </div> <!-- /.form-group -->
+ </div>
+
+ <div class="row">
+ <form id="fileuploadForm" class="form-horizontal" role="form" method="POST" enctype="multipart/form-data">
+ <div class="form-group2">
+ <label class="col-sm-3 control-label">Files to upload</label>
+ <div class="col-sm-9">
+ <span>
+ <span class="btn btn-success fileinput-button">
+ <i class="fa fa-plus"></i>
+ <span>select files...</span>
+ <input id="fileupload" type="file" name="files[]" multiple>
+ </span>
+ <button type="submit" class="btn btn-primary start action-upload-all" disabled>
+ <i class="fa fa-upload"></i>
+ <span>upload all</span>
+ </button>
+ </span>
+ </div>
+ </div> <!-- /.form-group -->
+
+ <div class="form-group2">
+ <div class="col-sm-9 col-sm-offset-3">
+ <ul class="list-unstyled">
+ </ul>
+ </div>
+ </div> <!-- /.form-group -->
+ </form>
+ </div>
+ </div>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/jena/blob/e8abcbb6/jena-fuseki2/jena-fuseki-webapp/src/main/webapp/js/app/templates/uploadable-file.tpl
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-webapp/src/main/webapp/js/app/templates/uploadable-file.tpl b/jena-fuseki2/jena-fuseki-webapp/src/main/webapp/js/app/templates/uploadable-file.tpl
new file mode 100644
index 0000000..83b8449
--- /dev/null
+++ b/jena-fuseki2/jena-fuseki-webapp/src/main/webapp/js/app/templates/uploadable-file.tpl
@@ -0,0 +1,23 @@
+<div class="row file-description">
+ <div class="col-sm-3">
+ <%= file.name %>
+ </div>
+ <div class="col-sm-3">
+ <em>
+ <%= file.readableFileSize %>
+ </em>
+ </div>
+ <div class="col-sm-6">
+ <button class="btn btn-sm btn-default action action-upload-file"><i class="fa fa-upload"></i> upload now</button>
+ <button class="btn btn-sm btn-default action action-remove-upload"><i class="fa fa-minus-circle"></i> remove</button>
+ </div>
+ <div class="col-sm-12">
+ <div class="result"></div>
+ </div>
+ <div class="col-sm-12">
+ <div class="progress">
+ <div class="progress-bar progress-bar-success" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100"></div>
+ </div>
+ </div>
+
+</div>
http://git-wip-us.apache.org/repos/asf/jena/blob/e8abcbb6/jena-fuseki2/jena-fuseki-webapp/src/main/webapp/js/app/util/page-utils.js
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-webapp/src/main/webapp/js/app/util/page-utils.js b/jena-fuseki2/jena-fuseki-webapp/src/main/webapp/js/app/util/page-utils.js
new file mode 100644
index 0000000..45b4ffc
--- /dev/null
+++ b/jena-fuseki2/jena-fuseki-webapp/src/main/webapp/js/app/util/page-utils.js
@@ -0,0 +1,33 @@
+/** Utilities for managing HTML pages */
+
+define(
+ function( require ) {
+ "use strict";
+
+ var _ = require( "underscore" );
+
+ /** Return true if a given query parameter is defined, otherwise null */
+ var hasQueryParam = function( param ) {
+ return !!queryParam( param );
+ };
+
+ /** Return the value of a query parameter, or null */
+ var queryParam = function( param ) {
+ var p = param && queryParams()[param];
+ return p ? p : null;
+ };
+
+ /** Return the current query params as a map */
+ var queryParams = function() {
+ return _.chain( document.location.search.slice(1).split('&') )
+ .invoke('split', '=')
+ .object()
+ .value();
+ };
+
+ return {
+ hasQueryParam: hasQueryParam,
+ queryParam: queryParam
+ };
+ }
+);
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/jena/blob/e8abcbb6/jena-fuseki2/jena-fuseki-webapp/src/main/webapp/js/app/views/dataset-edit.js
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-webapp/src/main/webapp/js/app/views/dataset-edit.js b/jena-fuseki2/jena-fuseki-webapp/src/main/webapp/js/app/views/dataset-edit.js
new file mode 100644
index 0000000..017e097
--- /dev/null
+++ b/jena-fuseki2/jena-fuseki-webapp/src/main/webapp/js/app/views/dataset-edit.js
@@ -0,0 +1,205 @@
+/** Component for showing detailed information about a dataset */
+
+define(
+ function( require ) {
+ var Backbone = require( "backbone" ),
+ _ = require( "underscore" ),
+ fui = require( "app/fui" ),
+ DatasetEditTpl = require( "plugins/text!app/templates/dataset-edit.tpl" ),
+ CodeMirror = require( "lib/codemirror" ),
+ CodeMirrorTurtle = require( "mode/turtle/turtle" );
+
+ var MAX_EDITABLE_SIZE = 10000;
+
+ var DatasetEdit = Backbone.Marionette.ItemView.extend( {
+
+ initialize: function() {
+ _.bindAll( this, "onCountGraphs", "onModelChanged", "onSelectDataset",
+ "onShownTab", "onShownEditTab", "onGraphContent",
+ "onSaveEdit", "onCancelEdit" );
+
+ this.model.on( "change", this.onModelChanged );
+ this._editor = null;
+
+ fui.vent.on( "shown.bs.tab", this.onShownTab );
+ },
+
+ template: _.template( DatasetEditTpl ),
+
+ ui: {
+ listGraphs: ".action.list-graphs",
+ editor: "#graph-editor",
+ graphName: "input.graph-name",
+ saveButton: "button.save-edit",
+ cancelButton: "button.cancel-edit"
+ },
+
+ el: "#edit .with-dataset",
+
+ events: {
+ "click .list-graphs": "onCountGraphs",
+ "click .select-dataset": "onSelectDataset",
+ "click .save-edit": "onSaveEdit",
+ "click .cancel-edit": "onCancelEdit"
+ },
+
+ templateHelpers: {
+ },
+
+ serializeData: function() {
+ return this.model;
+ },
+
+ /** Alias for the model */
+ dataset: function() {
+ return this.model;
+ },
+
+ // event handlers
+
+ onModelChanged: function() {
+ if (!this.model.counting) {
+ this.render();
+ }
+ },
+
+ onCountGraphs: function( e ) {
+ e.preventDefault();
+ this.model.count();
+ },
+
+ /** Event that triggers when any tab is shown */
+ onShownTab: function( tab ) {
+ if (tab.attr("href") === "#edit") {
+ this.onShownEditTab();
+ }
+ },
+
+ /** When the tab is show, ensure the editor element is initialised */
+ onShownEditTab: function() {
+ this.showEditor();
+ },
+
+ /** When rendering, only show the code mirror editor if the tab is visible */
+ onRender: function() {
+ this.showEditor();
+ },
+
+ /** Ensure the code mirror element is visible */
+ showEditor: function() {
+ if (this.editorElementVisible() && this.editorNotYetInitialised()) {
+ this._editor = null;
+ this.editorElement();
+ }
+ },
+
+ /** Return true if the editor container element is visible */
+ editorElementVisible: function() {
+ return this.ui.editor.is( ":visible" );
+ },
+
+ /** Return true if the CodeMirror element has not yet been initialised */
+ editorNotYetInitialised: function() {
+ return this.ui.editor.is( ":not(:has(.CodeMirror))" );
+ },
+
+ /** User has (attempted to) select a dataset */
+ onSelectDataset: function( e ) {
+ e.preventDefault();
+ var self = this;
+ var elem = $(e.currentTarget);
+ var graphName = elem.data( "graph-name" );
+ var graphSize = parseInt( elem.data( "graph-size" ));
+
+ if (graphSize > MAX_EDITABLE_SIZE) {
+ alert( "Sorry, that dataset is too large to load into the editor" );
+ }
+ else {
+ if (this.dirtyCheck()) {
+ $(".nav.graphs").find( ".active" ).removeClass( "active" );
+ elem.parent().addClass( "active" );
+
+ var gn = this.setGraphName( graphName );
+ this.dataset()
+ .fetchGraph( gn )
+ .done( self.onGraphContent );
+ }
+ }
+ },
+
+ /** Return true if the edit buffer is not dirty, or if the user says OK */
+ dirtyCheck: function() {
+ return true; // TODO
+ },
+
+ /** Return the DOM node representing the query editor */
+ editorElement: function() {
+ if (!this._editor) {
+ this._editor = new CodeMirror( this.ui.editor.get(0), {
+ lineNumbers: true,
+ mode: "turtle"
+ } );
+ }
+ return this._editor;
+ },
+
+ /** Set the graph name, return the actual name used */
+ setGraphName: function( name ) {
+ var text = (name === "default" || name === "default graph") ? "default" : name;
+
+ this.ui.graphName.val( text );
+
+ return text;
+ },
+
+ /** Get the graph name */
+ graphName: function() {
+ return this.ui.graphName.val();
+ },
+
+ /** Server has sent the content of the graph encoded as turtle */
+ onGraphContent: function( data ) {
+ this.editorElement().setValue( data );
+ },
+
+ /** User wants to save changes */
+ onSaveEdit: function( e ) {
+ e.preventDefault();
+ var self = this;
+
+ var turtle = this.editorElement().getValue();
+ this.dataset()
+ .putGraph( turtle, this.graphName() )
+ .done( function( data ) {
+ var nq = parseInt( data.quadCount );
+ var nt = parseInt( data.tripleCount );
+ var typ = (nq > nt) ? "quad" : "triple";
+ var s = (nq + nt) === 1 ? "" : "s";
+ var msg = sprintf( "Added %d %s%s", nq + nt, typ, s );
+
+ self.showFeedback( msg, "" );
+ } )
+ .error( function( jqhxr, msg, err ) {
+ self.showFeedback( err || msg, "text-warning" );
+ } );
+ },
+
+ /** User wants to discard changes */
+ onCancelEdit: function( e ) {
+ e.preventDefault();
+ this.ui.graphName.val( "" );
+ this.editorElement().setValue( "" );
+ },
+
+ /** Show feedback from operations */
+ showFeedback: function( msg, cls ) {
+ $(".feedback").html( sprintf( "<span class='%s'>%s</span>", cls, msg ) );
+ }
+
+
+ });
+
+
+ return DatasetEdit;
+ }
+);
http://git-wip-us.apache.org/repos/asf/jena/blob/e8abcbb6/jena-fuseki2/jena-fuseki-webapp/src/main/webapp/js/app/views/dataset-info.js
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-webapp/src/main/webapp/js/app/views/dataset-info.js b/jena-fuseki2/jena-fuseki-webapp/src/main/webapp/js/app/views/dataset-info.js
new file mode 100644
index 0000000..665725f
--- /dev/null
+++ b/jena-fuseki2/jena-fuseki-webapp/src/main/webapp/js/app/views/dataset-info.js
@@ -0,0 +1,76 @@
+/** Component for showing detailed information about a dataset */
+
+define(
+ function( require ) {
+ var Backbone = require( "backbone" ),
+ _ = require( "underscore" ),
+ fui = require( "app/fui" ),
+ DatasetInfoTpl = require( "plugins/text!app/templates/dataset-info.tpl" ),
+ DatasetStatsView = require( "app/views/dataset-stats" ),
+ DatasetStatsModel = require( "app/models/dataset-stats" );
+
+ var DatasetInfo = Backbone.Marionette.ItemView.extend( {
+
+ initialize: function() {
+ _.bindAll( this, "onModelChanged", "onCountGraphs" );
+
+ this.showStatistics( true );
+ this.model.on( "change", this.onModelChanged );
+ },
+
+ template: _.template( DatasetInfoTpl ),
+
+ ui: {
+ stats: "#statistics",
+ count: ".count-graphs"
+ },
+
+ el: "#info .with-dataset",
+
+ events: {
+ "click .count-graphs": "onCountGraphs"
+ },
+
+ templateHelpers: {
+ },
+
+ serializeData: function() {
+ return this.model;
+ },
+
+ /** Alias for the model */
+ dataset: function() {
+ return this.model;
+ },
+
+ // event handlers
+
+ onModelChanged: function() {
+ if (!this.model.counting) {
+ this.render();
+ this.showStatistics( false );
+ }
+ },
+
+ onCountGraphs: function( e ) {
+ e.preventDefault();
+ this.model.count();
+ },
+
+ showStatistics: function( keep ) {
+ var self = this;
+
+ this.model
+ .statistics( keep )
+ .done( function( data ) {
+ var statsModel = new DatasetStatsModel( self.dataset(), data );
+ new DatasetStatsView( {model: statsModel} ).render();
+ } );
+ }
+
+ });
+
+
+ return DatasetInfo;
+ }
+);
http://git-wip-us.apache.org/repos/asf/jena/blob/e8abcbb6/jena-fuseki2/jena-fuseki-webapp/src/main/webapp/js/app/views/dataset-management.js
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-webapp/src/main/webapp/js/app/views/dataset-management.js b/jena-fuseki2/jena-fuseki-webapp/src/main/webapp/js/app/views/dataset-management.js
new file mode 100644
index 0000000..ca3af74
--- /dev/null
+++ b/jena-fuseki2/jena-fuseki-webapp/src/main/webapp/js/app/views/dataset-management.js
@@ -0,0 +1,173 @@
+define(
+ function( require ) {
+ var Backbone = require( "backbone" ),
+ _ = require( "underscore" ),
+ fui = require( "app/fui" ),
+ datasetManagementViewTpl = require( "plugins/text!app/templates/dataset-management.tpl" );
+
+ var DataManagementView = Backbone.Marionette.ItemView.extend( {
+
+ initialize: function(){
+ _.bindAll( this, "onRemoveDataset", "onConfirmAction",
+ "onDatasetRemoveSuccess", "onDatasetRemoveFail",
+ "onTaskStatus", "onTaskFailed", "cleanup" );
+
+ this.listenTo( this.model, "change", this.onModelChange, this );
+
+ fui.vent.on( "action.delete.confirm", this.onConfirmRemoveDataset, this );
+ fui.vent.on( "action.backup.confirm", this.onConfirmBackupDataset, this );
+
+ fui.vent.on( "task.status", this.onTaskStatus, this );
+ fui.vent.on( "task.failed", this.onTaskFailed, this );
+ },
+
+ template: _.template( datasetManagementViewTpl ),
+
+ ui: {
+ actionConfirmModal: "#actionConfirmModal"
+ },
+
+ el: "#dataset-management",
+
+ events: {
+ "click .action.remove": "onRemoveDataset",
+ "click .action.confirm": "onConfirmAction",
+ "click .action.backup": "onBackupDataset"
+ },
+
+ templateHelpers: {
+ },
+
+ cleanup: function() {
+ this.unbind();
+ this.undelegateEvents();
+ this.model.unbind( 'change', this.onModelChange, this );
+ fui.vent.unbind( 'action.delete.confirm', this.onConfirmRemoveDataset, this );
+ },
+
+ /** If the model changes, update the summary */
+ onModelChange: function( event ) {
+ this.cleanup();
+ this.render();
+ },
+
+ /** User has requested a dataset be removed */
+ onRemoveDataset: function( e ) {
+ e.preventDefault();
+ var elem = $(e.currentTarget);
+ var dsId = elem.data( "ds-id" );
+ var msg = sprintf( "Are you sure you want to delete dataset <code>%s</code>? This action cannot be reversed.", dsId );
+
+ this.showConfirmationModal( msg, dsId, "action.delete.confirm" );
+ },
+
+ /** User has requested a dataset be backed up */
+ onBackupDataset: function( e ) {
+ e.preventDefault();
+ var elem = $(e.currentTarget);
+ var dsId = elem.data( "ds-id" );
+ var msg = sprintf( "Are you sure you want to create a backup of dataset <code>%s</code>? This action may take some time to complete", dsId );
+
+ this.showConfirmationModal( msg, dsId, "action.backup.confirm" );
+ },
+
+ /** Show a generic modal confirmation */
+ showConfirmationModal: function( msg, dsId, eventId ) {
+ this.ui.actionConfirmModal
+ .find( ".modal-body p" )
+ .html( msg );
+
+ this.ui.actionConfirmModal
+ .find( ".action.confirm" )
+ .data( "ds-id", dsId )
+ .data( "event-id", eventId );
+
+ this.clearFeedback();
+ this.ui.actionConfirmModal.modal( 'show' );
+ },
+
+ /** Generic response to confirming the current modal dialogue */
+ onConfirmAction: function( e ) {
+ e.preventDefault();
+ var elem = $(e.currentTarget);
+ var dsId = elem.data( "ds-id" );
+ var eventId = elem.data( "event-id" );
+
+ //this.ui.actionConfirmModal.modal( 'hide' );
+ $('.modal.in').modal('hide');
+ $('body').removeClass('modal-open');
+ $('.modal-backdrop').remove();
+ _.delay( function() {
+ fui.vent.trigger( eventId, dsId );
+ }, 100 );
+ },
+
+ /** User has confirmed that the dataset can be deleted */
+ onConfirmRemoveDataset: function( dsId ) {
+ var self = this;
+
+ fui.models
+ .fusekiServer
+ .dataset( dsId )
+ .deleteDataset()
+ .done( function( data ) {self.onDatasetRemoveSuccess( data, dsId );} )
+ .error( function( jqxhr, msg, err ) {self.onDatasetRemoveFail( jqxhr, msg, err, dsId );} );
+ },
+
+ /** Callback after successfully removing a dataset */
+ onDatasetRemoveSuccess: function( data, dsId ) {
+ this.model.loadServerDescription();
+ },
+
+ /** Removing the dataset did not work: notify the user */
+ onDatasetRemoveFail: function( jqxhr, msg, err, dsId ) {
+ this.feedbackArea( dsId )
+ .html( sprintf( "<p class='text-warning'>Sorry, removing dataset %s did not work, because: '%s'</p>", dsId, err || msg ) );
+ },
+
+ /** User has confirmed backing up the dataset */
+ onConfirmBackupDataset: function( dsId ) {
+ var self = this;
+
+ fui.models
+ .fusekiServer
+ .dataset( dsId )
+ .backupDataset();
+ },
+
+ /** Remove any current feedback content */
+ clearFeedback: function() {
+ $(".action.feedback").empty();
+ },
+
+ /** Long running task has updated status */
+ onTaskStatus: function( status ) {
+ var task = status.task;
+ var msg = sprintf( "<p>Task <strong>%s</strong> started at %s%s</p>",
+ task.operationType,
+ task.taskDescription.started,
+ status.finished ? sprintf( ", finished at %s", status.finished ) : " – ongoing" );
+
+ this.feedbackArea( status.dsId )
+ .html( msg );
+ },
+
+ /** Long running task has failed to start */
+ onTaskFailed: function( status ) {
+ this.feedbackArea( status.dsId )
+ .html( sprintf( "<p class='text-danger'>Task %s failed: '%s'</p>", task.operationType, task.errorMessage ));
+ },
+
+ /** Find the feedback area for a particular dataset */
+ feedbackArea: function( dsId ) {
+ return $(sprintf( ".action[data-ds-id='%s']", dsId ) )
+ .parent()
+ .siblings(".action.feedback");
+ }
+
+ });
+
+
+ return DataManagementView;
+ }
+);
http://git-wip-us.apache.org/repos/asf/jena/blob/e8abcbb6/jena-fuseki2/jena-fuseki-webapp/src/main/webapp/js/app/views/dataset-selection-list.js
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-webapp/src/main/webapp/js/app/views/dataset-selection-list.js b/jena-fuseki2/jena-fuseki-webapp/src/main/webapp/js/app/views/dataset-selection-list.js
new file mode 100644
index 0000000..ccad19d
--- /dev/null
+++ b/jena-fuseki2/jena-fuseki-webapp/src/main/webapp/js/app/views/dataset-selection-list.js
@@ -0,0 +1,58 @@
+/**
+ * This view presents a list of the available datasets for the user to interact
+ * with.
+ */
+
+define(
+ function( require ) {
+ var Backbone = require( "backbone" ),
+ _ = require( "underscore" ),
+ fui = require( "app/fui" ),
+ datasetSelectionListTemplate = require( "plugins/text!app/templates/dataset-selection-list.tpl" );
+
+ var DatasetSelectionListView = Backbone.Marionette.ItemView.extend( {
+ initialize: function(){
+// _.bindAll(this, "onFilter", "onModelChange");
+ this.listenTo( this.model, "change", this.onModelChange, this );
+ },
+
+ template: _.template( datasetSelectionListTemplate ),
+
+ el: "#dataset-selection-list",
+
+ ui: {
+ },
+
+ events: {
+// "change #independent-variable-selection": "selectVariable",
+// "click a.action.filter": "onFilter"
+ },
+
+ templateHelpers: {
+ },
+
+// /** Update the model when the user changes the selection */
+// selectVariable: function( event ) {
+// this.model.set( "independentVarSelection", this.ui.variableSelection.val() );
+// },
+//
+// /** User wants to open the filter dialog */
+// onFilter: function( event ) {
+// var varModel = bgViz.models.variablesConfig.independentVar();
+// var rangeType = varModel.component.range().rangeType();
+// var viewName = rangeType.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase();
+//
+// bgViz.layouts.filterDialog.showFilter( viewName, varModel );
+// },
+
+ /** If the model changes, update the summary */
+ onModelChange: function( event ) {
+// this.ui.summary.html( this.model.independentVar().component.range().summarise() );
+ }
+
+ });
+
+
+ return DatasetSelectionListView;
+ }
+);
http://git-wip-us.apache.org/repos/asf/jena/blob/e8abcbb6/jena-fuseki2/jena-fuseki-webapp/src/main/webapp/js/app/views/dataset-selector.js
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-webapp/src/main/webapp/js/app/views/dataset-selector.js b/jena-fuseki2/jena-fuseki-webapp/src/main/webapp/js/app/views/dataset-selector.js
new file mode 100644
index 0000000..f14a747
--- /dev/null
+++ b/jena-fuseki2/jena-fuseki-webapp/src/main/webapp/js/app/views/dataset-selector.js
@@ -0,0 +1,84 @@
+/**
+ * Reusable component that encapsulates selecting a dataset to work on in a given page.
+ * Takes the FusekiServer as a model, and populates a select control to choose one of the
+ * current datasets. If the dataset changes, this view will update the `selectedDatasetName`
+ * on the model, and trigger the event `dataset.changed`.
+ **/
+
+define(
+ function( require ) {
+ "use strict";
+
+ var Marionette = require( "marionette" ),
+ Backbone = require( "backbone" ),
+ _ = require( "underscore" ),
+ fui = require( "app/fui" ),
+ sprintf = require( "sprintf" ),
+ datasetSelectorTemplate = require( "plugins/text!app/templates/dataset-selector.tpl" );
+
+ var DatasetSelectorView = Backbone.Marionette.ItemView.extend( {
+
+ initialize: function(){
+ this.listenTo( this.model, "change", this.render, this );
+ },
+
+ template: _.template( datasetSelectorTemplate ),
+
+ el: ".dataset-selector-container",
+
+ ui: {
+ select: ".dataset-selector select"
+ },
+
+ events: {
+ "change .dataset-selector select": "onChangeDataset"
+ },
+
+ /**
+ * After rendering, set up the dataset picker and notify the rest of the
+ * app if the default dataset name is known.
+ */
+ onRender: function() {
+ var selector = $('.selectpicker');
+ selector.selectpicker('refresh');
+
+ if (selector.val()) {
+ this.unHideDatasetElements();
+ this.onChangeDataset();
+ }
+ },
+
+ /**
+ * Respond to a change in the dataset name selection by updating
+ * the underlying model. TODO: should also update the application
+ * URL.
+ */
+ onChangeDataset: function( e ) {
+ var newDatasetName = this.ui.select.val();
+ this.model.set( "selectedDatasetName", newDatasetName );
+ this.notifyDatasetName( newDatasetName );
+ },
+
+ /**
+ * Ensure that elements that should be visible when a dataset is known
+ * are not hidden, and vice-versa.
+ */
+ unHideDatasetElements: function() {
+ $(".no-dataset").addClass( "hidden" );
+ $(".with-dataset").removeClass( "hidden" );
+ },
+
+ /** Trigger an event to notify other components that the dataset
+ * name has been selected.
+ */
+ notifyDatasetName: function( dsName ) {
+ fui.vent.trigger( "dataset.changed", dsName || this.ui.select.val() );
+ }
+
+
+ });
+
+
+ return DatasetSelectorView;
+ }
+);
http://git-wip-us.apache.org/repos/asf/jena/blob/e8abcbb6/jena-fuseki2/jena-fuseki-webapp/src/main/webapp/js/app/views/dataset-simple-create.js
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-webapp/src/main/webapp/js/app/views/dataset-simple-create.js b/jena-fuseki2/jena-fuseki-webapp/src/main/webapp/js/app/views/dataset-simple-create.js
new file mode 100644
index 0000000..b7a591b
--- /dev/null
+++ b/jena-fuseki2/jena-fuseki-webapp/src/main/webapp/js/app/views/dataset-simple-create.js
@@ -0,0 +1,102 @@
+/** Component for creating a new dataset with a few simple options */
+
+define(
+ function( require ) {
+ var Backbone = require( "backbone" ),
+ _ = require( "underscore" ),
+ fui = require( "app/fui" ),
+ DatasetSimpleCreateTpl = require( "plugins/text!app/templates/dataset-simple-create.tpl" );
+
+ var DatasetSimpleCreate = Backbone.Marionette.ItemView.extend( {
+
+ initialize: function() {
+ _.bindAll( this, "onCommitSimple", "clearWarnings" );
+ },
+
+ template: _.template( DatasetSimpleCreateTpl ),
+
+ ui: {
+ },
+
+ el: "#dataset-simple-create",
+
+ events: {
+ "click a.action.commit.simple": "onCommitSimple",
+ "submit form": "onCommitSimple",
+ "keydown input[name=dbName]": "clearWarnings"
+ },
+
+ templateHelpers: {
+ },
+
+ serializeData: function() {
+ return this.model;
+ },
+
+ // event handlers
+
+ onCommitSimple: function( e ) {
+ e.preventDefault();
+
+ if (this.validateSimpleForm()) {
+ var datasetName = $("input[name=dbName]").val().trim();
+ $("input[name=dbName]").val(datasetName);
+ var options = $("#simple-edit form").serializeArray();
+ fui.models.fusekiServer.updateOrCreateDataset( null, options )
+ .done( this.showDataManagementPage )
+ .fail( this.showFailureMessage );
+ }
+ },
+
+// onCommitUpload: function( e ) {
+// e.preventDefault();
+//
+// if (this.validateUploadForm()) {
+// $("#uploadForm").ajaxSubmit( {
+// success: this.showDataManagementPage,
+// error: this.showFailureMessage
+// });
+// }
+// },
+//
+ showDataManagementPage: function( e ) {
+ location = "?tab=datasets";
+ },
+
+ /** Todo: need to do a better job of responding to errors */
+ showFailureMessage: function( jqXHR, textStatus, errorThrown ) {
+ $(".errorOutput").html( sprintf( "<p class='has-error'>Sorry, that didn't work because:</p><pre>%s</pre>", errorThrown || textStatus ) );
+ },
+
+ /** Clear current warning states */
+ clearWarnings: function() {
+ this.clearValidation();
+ $(".errorOutput").empty();
+ },
+
+ // validation
+
+ validateSimpleForm: function() {
+ this.clearValidation();
+
+ if (! $("input[name=dbName]").val() || 0 === $("input[name=dbName]").val().trim().length) {
+ $(".dbNameValidation").removeClass("hidden")
+ .parents(".form-group" )
+ .addClass( "has-error" );
+ return false;
+ }
+
+ return true;
+ },
+
+ clearValidation: function() {
+ $(".has-error").removeClass( "has-error" );
+ $(".has-warning").removeClass( "has-warning" );
+ }
+
+ });
+
+
+ return DatasetSimpleCreate;
+ }
+);