diff --git a/.gitignore b/.gitignore
index c11bd69955f1624a10ca36bdea9898d7e29e24b4..0235d7090c43b8f7b00253c49ba51aa3a9564fb5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,11 @@
 *.pyc
 .*.swp
 *~
+__pycache__
+ubuntu-bionic-18.04-cloudimg-console.log
+.vagrant
+jupyter_openbis_extension.egg-info
+venv
+.ipynb_checkpoints
+.DS_Store
+*.ipynb
diff --git a/jupyter-openbis-extension/__init__.py b/jupyter-openbis-extension/__init__.py
index 22b2aabac22028215549cb4634d33428f05c7124..032652cf4581de8167edaad981442e54fdc49552 100644
--- a/jupyter-openbis-extension/__init__.py
+++ b/jupyter-openbis-extension/__init__.py
@@ -19,7 +19,7 @@ def _jupyter_nbextension_paths():
     #    # directory in the `nbextension/` namespace
         'dest': "openbis",
         # _also_ in the `nbextension/` namespace
-        'require' : "openbis/dialog"
+        'require' : "openbis/main"
     }]
 
 def load_jupyter_server_extension(nbapp):
diff --git a/jupyter-openbis-extension/__pycache__/extension.cpython-36.pyc b/jupyter-openbis-extension/__pycache__/extension.cpython-36.pyc
deleted file mode 100644
index d5f03417cffd6d3922495cb9dca6bf4efab96353..0000000000000000000000000000000000000000
Binary files a/jupyter-openbis-extension/__pycache__/extension.cpython-36.pyc and /dev/null differ
diff --git a/jupyter-openbis-extension/static/common.js b/jupyter-openbis-extension/static/common.js
new file mode 100644
index 0000000000000000000000000000000000000000..41f42fdba90ec6f3205e0f169f00a893e65ca722
--- /dev/null
+++ b/jupyter-openbis-extension/static/common.js
@@ -0,0 +1,30 @@
+define([
+        "base/js/namespace"
+    ],
+    function (IPython) {
+
+        function createFeedback(type, content) {
+            var close = document.createElement("BUTTON")
+            close.className = "close"
+            close.setAttribute("data-dismiss", "alert")
+            close.setAttribute("aria-label", "Close")
+            var x = document.createElement("SPAN")
+            x.setAttribute("aria-hidden", true)
+            x.innerHTML = "×"
+            close.appendChild(x)
+
+            var feedbackBox = document.createElement("DIV")
+            feedbackBox.className = "openbis-feedback alert alert-dismissible alert-" + type
+            feedbackBox.setAttribute("role", "alert")
+            feedbackBox.innerHTML = content
+            feedbackBox.prepend(close)
+
+            var nb_container = document.getElementById('notebook-container')
+            nb_container.prepend(feedbackBox)
+        }
+
+        return {
+            createFeedback: createFeedback
+        }
+    }
+)
\ No newline at end of file
diff --git a/jupyter-openbis-extension/static/connectionDialog.js b/jupyter-openbis-extension/static/connectionDialog.js
new file mode 100644
index 0000000000000000000000000000000000000000..4028d6b00862201dd9be12e22d774ccde2c3d901
--- /dev/null
+++ b/jupyter-openbis-extension/static/connectionDialog.js
@@ -0,0 +1,245 @@
+define(
+    [
+        "base/js/dialog",
+        "./state",
+        "./connections"
+    ],
+    function (dialog, state, connections) {
+
+        var currentDownloadPath = null
+
+        function show_available_connections(env, data, conn_table, onclick_cbf) {
+            if (!currentDownloadPath) {
+                currentDownloadPath = data.cwd
+            }
+
+            var table = document.createElement("TABLE")
+            table.className = 'table-bordered table-striped table-condensed'
+            var thead = table.createTHead()
+            var thead_row = thead.insertRow(0)
+            var titles = ['', 'Name', 'URL', 'Status', 'Username / Password']
+            for (title of titles) {
+                thead_row.insertCell().textContent = title
+            }
+
+            tbody = table.createTBody()
+            for (connection of data.connections) {
+                var conn = document.createElement("INPUT")
+                conn.type = "radio"
+                conn.name = "connection_name"
+                conn.value = connection.name
+                conn.setAttribute("url", connection.url)
+
+                conn.checked = connection.name === state.connection.candidateName;
+                if (onclick_cbf === undefined) {
+                    conn.onclick = function () {
+                        state.connection.candidateName = this.value
+                    }
+                } else {
+                    conn.onclick = onclick_cbf
+                }
+
+                var row = tbody.insertRow()
+                row.insertCell().appendChild(conn)
+                row.insertCell().textContent = connection.name
+                row.insertCell().textContent = connection.url
+
+                var status_cell = row.insertCell()
+
+                var status_badge = document.createElement("SPAN")
+                status_badge.id = connection.name + "-badge"
+                status_badge.textContent = connection.status
+                if (connection.status === "connected") {
+                    status_badge.className = "label label-success"
+                } else {
+                    status_badge.className = "label label-danger"
+                }
+                status_cell.appendChild(status_badge)
+
+                var username = document.createElement("INPUT")
+                username.type = "text"
+                username.name = "username"
+                username.autocomplete = "on"
+                username.value = connection.username
+                username.setAttribute("form", connection.name)
+
+                var password = document.createElement("INPUT")
+                password.type = "password"
+                password.name = "password"
+                password.autocomplete = "current-password"
+                password.value = connection.password
+                password.setAttribute("form", connection.name)
+
+                // Username / Password form
+                var pwform = document.createElement("FORM")
+                pwform.id = connection.name
+                pwform.onsubmit = function (event) {
+                    var form_data = new FormData(this)
+                    var status_badge = document.getElementById(this.id + "-badge")
+                    connections.connect(env, this.id,
+                            form_data.get("username"), form_data.get("password")
+                        )
+                        .then(function (response) {
+                            //console.log(response)
+                            if (status_badge.nextElementSibling !== null) {
+                                status_badge.parentNode.removeChild(status_badge.nextElementSibling)
+                            }
+                            if (response.ok) {
+                                status_badge.textContent = "connected"
+                                status_badge.className = "label label-success"
+                            } else {
+                                status_badge.textContent = "not connected"
+                                status_badge.className = "label label-danger"
+                                message = document.createElement("p")
+                                if (response.status === 401) {
+                                    message.textContent = "username/password incorrect"
+                                } else if (response.status === 500) {
+                                    message.textContent = "Connection error"
+                                } else {
+                                    message.textContent = "General error"
+                                }
+                                status_badge.parentNode.insertBefore(message, status_badge.nextSibling)
+                            }
+                        })
+                        .catch(error => console.error("Error while attempting to reconnect: ", error))
+
+                    return false
+                }
+
+
+                var connect_button = document.createElement("BUTTON")
+                connect_button.className = "btn btn-primary btn-xs"
+                connect_button.textContent = "connect"
+
+                pwform.appendChild(username)
+                pwform.appendChild(password)
+                pwform.appendChild(connect_button)
+
+                var cell = row.insertCell()
+                cell.appendChild(pwform)
+            }
+
+            // add row for new connection
+            var row = tbody.insertRow()
+
+            var conn_form = document.createElement("FORM")
+            conn_form.id = "new_connection"
+            conn_form.onsubmit = function (event) {
+                var inputs = document.querySelectorAll("input[form=new_connection]")
+
+                data = {}
+                for (input of inputs) {
+                    data[input.name] = input.value
+                }
+                for (missing of ['connection_name', 'url', 'username', 'password']) {
+                    if (data[missing] === "") {
+                        alert("Please provide: " + missing)
+                        return false
+                    }
+                }
+                connections.create(env, data.connection_name, data.url, data.username, data.password)
+                    .then(function (response) {
+                        if (response.ok) {
+                            response.json()
+                                .then(function (data) {
+                                    show_available_connections(env, data, conn_table)
+                                })
+                        }
+                    })
+                return false
+            }
+            var conn_name = document.createElement("INPUT")
+            conn_name.type = "input"
+            conn_name.name = "connection_name"
+            conn_name.setAttribute("form", conn_form.id)
+            conn_name.placeholder = "openBIS instance name"
+            row.insertCell().appendChild(conn_form)
+            row.insertCell().appendChild(conn_name)
+
+            var conn_url = document.createElement("INPUT")
+            conn_url.type = "input"
+            conn_url.name = "url"
+            conn_url.setAttribute("form", conn_form.id)
+            conn_url.placeholder = "https://openbis.domain:port"
+            row.insertCell().appendChild(conn_url)
+            row.insertCell()
+
+            var username = document.createElement("INPUT")
+            username.autocomplete = "off"
+            username.type = "text"
+            username.name = "username"
+            username.setAttribute("form", conn_form.id)
+            username.placeholder = "username"
+            var password = document.createElement("INPUT")
+            password.type = "password"
+            password.name = "password"
+            password.autocomplete = "new-password"
+            password.setAttribute("form", conn_form.id)
+            var create_btn = document.createElement("BUTTON")
+            create_btn.setAttribute("form", conn_form.id)
+            create_btn.textContent = "create"
+            var uname_pw_cell = row.insertCell()
+            uname_pw_cell.appendChild(username)
+            uname_pw_cell.appendChild(password)
+            uname_pw_cell.appendChild(create_btn)
+
+            conn_table.innerHTML = ""
+            table_title = document.createElement("STRONG")
+            table_title.textContent = "Please choose a connection"
+            conn_table.appendChild(table_title)
+            conn_table.appendChild(table)
+        }
+
+        return {
+            help: 'configure openBIS connections',
+            icon: 'fa-sliders',
+            help_index: '',
+            handler: function (env) {
+                conn_table = document.createElement("DIV")
+                var dst_title = document.createElement("STRONG")
+                dst_title.textContent = "DataSet type"
+                var dataset_types = document.createElement("SELECT")
+                dataset_types.id = "dataset_type"
+                dataset_types.className = "form-control select-xs"
+
+                var input_fields = document.createElement("DIV")
+                conn_table.id = "openbis_connections"
+
+                var onclick_cbf = function () {
+                    state.connection.candidateName = this.value
+                }
+
+                connections.list(env)
+                    .done(function (data) {
+                        show_available_connections(env, data, conn_table, onclick_cbf)
+                    })
+                    .fail(function (data) {
+                        alert(data.status)
+                    })
+
+                var uploadDialogBox = $('<div/>').append(conn_table)
+
+                // get the canvas for user feedback
+                var container = $('#notebook-container')
+
+                function onOk() {
+                    state.connection.name = state.connection.candidateName
+                }
+
+                dialog.modal({
+                    body: uploadDialogBox,
+                    title: 'Choose openBIS connection',
+                    buttons: {
+                        'Cancel': {},
+                        'Choose connection': {
+                            class: 'btn-primary btn-large',
+                            click: onOk
+                        }
+                    },
+                    notebook: env.notebook,
+                    keyboard_manager: env.notebook.keyboard_manager
+                })
+            }
+        }
+    }
+)
\ No newline at end of file
diff --git a/jupyter-openbis-extension/static/connections.js b/jupyter-openbis-extension/static/connections.js
new file mode 100644
index 0000000000000000000000000000000000000000..1f5972cf8f4f83b06d944796970186bba9982853
--- /dev/null
+++ b/jupyter-openbis-extension/static/connections.js
@@ -0,0 +1,67 @@
+define([
+        "base/js/utils"
+    ],
+    function (utils) {
+
+        function list(env) {
+
+            var connectionsUrl = env.notebook.base_url + 'openbis/conns'
+            var settings = {
+                url: connectionsUrl,
+                processData: false,
+                type: 'GET',
+                dataType: 'json',
+                contentType: 'application/json'
+            }
+            return utils.ajax(settings)
+        }
+
+        function connect(env, connection, username, password) {
+            var url = env.notebook.base_url + 'openbis/conn/' + connection
+            body = {
+                "username": username,
+                "password": password
+            }
+
+            var cookie = decodeURIComponent(document.cookie)
+            var xsrf_token = cookie.split("_xsrf=")[1]
+
+            return fetch(url, {
+                method: "PUT",
+                headers: {
+                    "Content-Type": "application/json",
+                    "X-XSRFToken": xsrf_token,
+                },
+                body: JSON.stringify(body)
+            })
+        }
+
+        function create(env, connection_name, connection_url, username, password) {
+            var endpoint = env.notebook.base_url + 'openbis/conns'
+            body = {
+                "name": connection_name,
+                "url": connection_url,
+                "username": username,
+                "password": password
+            }
+
+            var cookie = decodeURIComponent(document.cookie)
+            var xsrf_token = cookie.split("_xsrf=")[1]
+
+            return fetch(endpoint, {
+                method: "POST",
+                headers: {
+                    "Content-Type": "application/json",
+                    "X-XSRFToken": xsrf_token,
+                },
+                body: JSON.stringify(body)
+            })
+        }
+
+        return {
+            list: list,
+            create: create,
+            connect: connect
+        }
+    }
+)
\ No newline at end of file
diff --git a/jupyter-openbis-extension/static/dialog.js b/jupyter-openbis-extension/static/dialog.js
deleted file mode 100644
index 8c999c89682dff741940e0bba896a84448ff209f..0000000000000000000000000000000000000000
--- a/jupyter-openbis-extension/static/dialog.js
+++ /dev/null
@@ -1,826 +0,0 @@
-define([
-        "base/js/namespace",
-        "base/js/dialog",
-        "base/js/utils",
-        "jquery"
-    ],
-    function(IPython, dialog, utils, $) {
-        var currentConnection = ''
-        var currentSampleIdentifier = null
-        var currentDownloadPath = null
-        var ds_type = document.createElement("SELECT")
-        ds_type.size = '40'
-        ds_type.className = "select-xs"
-        ds_type.id = "dataset_type"
-        var conn_table = null
-
-        function show_available_connections(env, data, conn_table, onclick_cbf) {
-            if (!currentDownloadPath) {
-                currentDownloadPath = data.cwd
-            }
-
-            var table = document.createElement("TABLE")
-            table.className = 'table-bordered table-striped table-condensed'
-            var thead = table.createTHead()
-            var thead_row = thead.insertRow(0)
-            var titles = ['', 'Name', 'URL', 'Status', 'Username / Password']
-            for (title of titles) {
-                thead_row.insertCell().textContent = title
-            }
-
-            tbody = table.createTBody()
-            for (connection of data.connections) {
-                var conn = document.createElement("INPUT")
-                conn.type = "radio"
-                conn.name = "connection_name"
-                conn.value = connection.name
-                conn.setAttribute("url", connection.url)
-
-                conn.checked = connection.name === currentConnection;
-                if (onclick_cbf === undefined) {
-                    conn.onclick = function() {
-                        currentConnection = this.value
-                    }
-                }
-                else {
-                    conn.onclick = onclick_cbf
-                }
-
-                var row = tbody.insertRow()
-                row.insertCell().appendChild(conn)
-                row.insertCell().textContent = connection.name
-                row.insertCell().textContent = connection.url
-
-                var status_cell = row.insertCell()
-
-                var status_badge = document.createElement("SPAN")
-                status_badge.id = connection.name + "-badge"
-                status_badge.textContent = connection.status
-                if (connection.status === "connected") {
-                    status_badge.className = "label label-success"
-                }
-                else {
-                        status_badge.className = "label label-danger"
-                }
-                status_cell.appendChild(status_badge)
-
-                var username = document.createElement("INPUT")
-                username.type = "text"
-                username.name = "username"
-                username.autocomplete = "on"
-                username.value = connection.username
-                username.setAttribute("form", connection.name)
-
-                var password = document.createElement("INPUT")
-                password.type = "password"
-                password.name = "password"
-                password.autocomplete = "current-password"
-                password.value = connection.password
-                password.setAttribute("form", connection.name)
-
-                // Username / Password form
-                var pwform = document.createElement("FORM")
-                pwform.id = connection.name
-                pwform.onsubmit = function(event) {
-                    var form_data = new FormData(this)
-                    var status_badge = document.getElementById(this.id + "-badge")
-                    reconnect_connection(env, this.id,
-                        form_data.get("username"), form_data.get("password")
-                    )
-                        .then( function(response) {
-                            //console.log(response)
-                            if (status_badge.nextElementSibling !== null) {
-                                status_badge.parentNode.removeChild(status_badge.nextElementSibling)
-                            }
-                            if (response.ok) {
-                                status_badge.textContent = "connected"
-                                status_badge.className = "label label-success"
-                            }
-                            else {
-                                status_badge.textContent = "not connected"
-                                status_badge.className = "label label-danger"
-                                message = document.createElement("p")
-                                if (response.status === 401) {
-                                    message.textContent = "username/password incorrect"
-                                }
-                                else if (response.status === 500) {
-                                    message.textContent = "Connection error"
-                                }
-                                else {
-                                    message.textContent = "General error"
-                                }
-                                status_badge.parentNode.insertBefore(message, status_badge.nextSibling)
-                            }
-                        })
-                        .catch( error => console.error("Error while attempting to reconnect: ",error) )
-
-                    return false
-                }
-
-
-                var connect_button = document.createElement("BUTTON")
-                connect_button.className = "btn btn-primary btn-xs"
-                connect_button.textContent  = "connect"
-
-                pwform.appendChild(username)
-                pwform.appendChild(password)
-                pwform.appendChild(connect_button)
-
-                var cell = row.insertCell()
-                cell.appendChild(pwform)
-            }
-
-            // add row for new connection
-            var row = tbody.insertRow()
-
-            var conn_form = document.createElement("FORM")
-            conn_form.id = "new_connection"
-            conn_form.onsubmit = function(event) {
-                var inputs = document.querySelectorAll("input[form=new_connection]")
-
-                data = {}
-                for (input of inputs) {
-                    data[input.name] = input.value
-                }
-                for (missing of ['connection_name','url', 'username', 'password']) {
-                    if (data[missing] === ""){
-                        alert("Please provide: " + missing)
-                        return false
-                    }
-                }
-                new_connection(
-                    env, data.connection_name, data.url, data.username, data.password
-                )
-                    .then( function(response){
-                        if (response.ok) {
-                            response.json()
-                                .then( function(data){
-                                    show_available_connections(env, data, conn_table)
-                                })
-                        }
-                    })
-                return false
-            }
-            var conn_name = document.createElement("INPUT")
-            conn_name.type = "input"
-            conn_name.name = "connection_name"
-            conn_name.setAttribute("form", conn_form.id)
-            conn_name.placeholder = "openBIS instance name"
-            row.insertCell().appendChild(conn_form)
-            row.insertCell().appendChild(conn_name)
-
-            var conn_url = document.createElement("INPUT")
-            conn_url.type = "input"
-            conn_url.name = "url"
-            conn_url.setAttribute("form", conn_form.id)
-            conn_url.placeholder = "https://openbis.domain:port"
-            row.insertCell().appendChild(conn_url)
-            row.insertCell()
-
-            var username = document.createElement("INPUT")
-            username.autocomplete = "off"
-            username.type = "text"
-            username.name = "username"
-            username.setAttribute("form", conn_form.id)
-            username.placeholder = "username"
-            var password = document.createElement("INPUT")
-            password.type = "password"
-            password.name = "password"
-            password.autocomplete = "new-password"
-            password.setAttribute("form", conn_form.id)
-            var create_btn = document.createElement("BUTTON")
-            create_btn.setAttribute("form", conn_form.id)
-            create_btn.textContent = "create"
-            var uname_pw_cell = row.insertCell()
-            uname_pw_cell.appendChild(username)
-            uname_pw_cell.appendChild(password)
-            uname_pw_cell.appendChild(create_btn)
-
-            conn_table.innerHTML = ""
-            table_title = document.createElement("STRONG")
-            table_title.textContent = "Please choose a connection"
-            conn_table.appendChild(table_title)
-            conn_table.appendChild(table)
-        }
-
-        function show_datasets_table( data, datasets_table) {
-            var table = document.createElement("TABLE")
-            table.className = "table-bordered table-striped table-condensed text-nowrap"
-            var thead = table.createTHead()
-            var t_row = thead.insertRow()
-            var titles = ['', 'permId', 'Type', 'Experiment', 'Registration Date', 'Status', 'Size']
-            titles.forEach( function(title) {
-                t_row.insertCell().textContent = title
-            })
-            var tbody = table.createTBody()
-
-            for (dataSet of data.dataSets) {
-
-                var permId = document.createElement("INPUT")
-                permId.type = "checkbox"
-                permId.name = "permId"
-                permId.value = dataSet.permId
-
-                var row = tbody.insertRow()
-                row.insertCell().appendChild(permId)
-                row.insertCell().textContent = dataSet.permId
-                row.insertCell().textContent = dataSet.type
-                row.insertCell().textContent = dataSet.experiment
-                row.insertCell().textContent = dataSet.registrationDate
-                row.insertCell().textContent = dataSet.status
-                row.insertCell().textContent = dataSet.size
-            }
-
-            while (datasets_table.firstChild) {
-                datasets_table.removeChild(datasets_table.firstChild);
-            }
-            datasets_table.appendChild(table)
-        }
-
-        // gets the status of the avialable openBIS connections
-        function getOpenBisConnections(env) {
-
-             var connectionsUrl = env.notebook.base_url + 'openbis/conns'
-             var settings = {
-                 url: connectionsUrl,
-                 processData: false,
-                 type: 'GET',
-                 dataType: 'json',
-                 contentType: 'application/json'
-             }
-             return utils.ajax(settings)
-        }
-
-        function reconnect_connection(env, connection, username, password){
-            var url = env.notebook.base_url + 'openbis/conn/' + connection
-            body = {
-                "username": username,
-                "password": password
-            }
-
-            var cookie = decodeURIComponent(document.cookie)
-            var xsrf_token = cookie.split("_xsrf=")[1]
-
-            return fetch(url, {
-                method: "PUT",
-                headers: {
-                    "Content-Type": "application/json",
-                    "X-XSRFToken": xsrf_token,
-                },
-                body: JSON.stringify(body)
-            })
-        }
-
-        function new_connection(env, connection_name, connection_url, username, password){
-            var endpoint = env.notebook.base_url + 'openbis/conns'
-            body = {
-                "name"    : connection_name,
-                "url"     : connection_url,
-                "username": username,
-                "password": password
-            }
-
-            var cookie = decodeURIComponent(document.cookie)
-            var xsrf_token = cookie.split("_xsrf=")[1]
-
-            return fetch(endpoint, {
-                method: "POST",
-                headers: {
-                    "Content-Type": "application/json",
-                    "X-XSRFToken": xsrf_token,
-                },
-                body: JSON.stringify(body)
-            })
-        }
-
-        function getDatasetTypes(env, connection_name, dataset_types, input_fields) {
-            // get all DatasetTypes of a given connection
-
-            var url = env.notebook.base_url + 'openbis/datasetTypes/'+ connection_name
-            fetch(url)
-                .then( function(response) {
-                    if (response.ok) {
-                        response.json()
-                            .then( function(data) {
-                                //console.log(data.dataSetTypes)
-
-                                var change_input_fields = function() {
-                                    // remove existing input fields
-                                    while (input_fields.firstChild) {
-                                        input_fields.removeChild(input_fields.firstChild)
-                                    }
-
-                                    // for every property assignment, create an input field.
-                                    for (pa of dts[dataset_type.selectedIndex].propertyAssignments) {
-                                        //var input_title = document.createTextNode(pa.label + ": ")
-                                        var input_field = document.createElement("INPUT")
-                                        input_field.type = "text"
-                                        input_field.name = pa.code
-                                        input_field.placeholder = pa.description ? pa.label + ": " + pa.description : pa.label
-                                        input_field.size = 90
-
-                                        //input_fields.appendChild(input_title)
-                                        input_fields.appendChild(input_field)
-                                        input_fields.appendChild(document.createElement("BR"))
-                                    }
-                                }
-                                dataset_types.onchange = change_input_fields
-
-                                // remove the old and add the new dataset-types
-                                dts = data.dataSetTypes
-                                while (dataset_types.firstChild) {
-                                    dataset_types.removeChild(dataset_types.firstChild);
-                                }
-                                for (dt of dts) {
-                                    var option = document.createElement("OPTION")
-                                    option.value = dt.code
-                                    option.textContent = dt.description ? dt.code + ": " + dt.description : dt.code
-                                    dataset_types.appendChild(option)
-                                }
-                                // change the input fields, since we just received new datasetTypes
-                                change_input_fields()
-
-                            })
-                            .catch( function(error){
-                                console.error("Error while parsing dataset types", error)
-                            })
-
-                    }
-                    else {
-                        while (dataset_types.firstChild) {
-                            dataset_types.removeChild(dataset_types.firstChild);
-                        }
-                    }
-                })
-                .catch (function(error) {
-                    console.error("Error while fetching dataset types:", error)
-                })
-        }
-
-        function createFeedback(type, content) {
-            var close = document.createElement("BUTTON")
-            close.className = "close"
-            close.setAttribute("data-dismiss", "alert")
-            close.setAttribute("aria-label", "Close")
-            var x = document.createElement("SPAN")
-            x.setAttribute("aria-hidden", true)
-            x.innerHTML = "&times;"
-            close.appendChild(x)
-
-            var feedbackBox = document.createElement( "DIV" )
-            feedbackBox.className = "openbis-feedback alert alert-dismissible alert-" + type
-            feedbackBox.setAttribute("role","alert")
-            feedbackBox.innerHTML = content
-            feedbackBox.prepend(close)
-
-            var nb_container = document.getElementById('notebook-container')
-            nb_container.prepend(feedbackBox)
-        }
-
-        function writeMetaData(data) {
-            var notebook = IPython.notebook
-            if (typeof notebook.metadata.openbis_connections === 'undefined') {
-                notebook.metadata.openbis_connections = {}
-            }
-            if (typeof notebook.metadata.openbis_connections[data.url] === 'undefined') {
-                notebook.metadata.openbis_connections[data.url] = {}
-            }
-            // store metadata about the downloaded files into the notebook-metadata
-            if (data.permId) {
-                notebook.metadata.openbis_connections[data.url][data.permId] = {
-                    "permId": data.permId,
-                    "path": data.path,
-                    "dataStore": data.dataStore,
-                    "location": data.location,
-                    "size": data.size,
-                    "status": data.statusText
-                }
-            }
-
-        }
-
-        var fetchDatasetFromOpenBis = {
-            help: 'Download openBIS datasets to your local harddrive',
-            icon: 'fa-download',
-            help_index: '',
-            handler: function (env) {
-                conn_table = document.createElement("DIV")
-                conn_table.id = "openbis_connections"
-                getOpenBisConnections(env)
-                    .done(function (data) {
-                        show_available_connections(env, data, conn_table)
-                    })
-                    .fail(function (data) {
-                        alert(data.status)
-                    })
-                    .always(function () {
-                        showDownloadDialog()
-                    })
-
-                function showDownloadDialog() {
-                    // This function gets called after loading the openBIS connections
-                    // to make sure we can display the download path provided by the server.
-
-                    // show DataSets for Sample identifier/permid
-                    var showDataSets = document.createElement("DIV")
-                    var title = document.createElement("STRONG")
-                    title.textContent = "Sample identfier/permId: "
-                    showDataSets.appendChild(title)
-                    showDataSets.style.marginTop = '10px'
-
-                    var sampleIdentifier = document.createElement("INPUT")
-                    sampleIdentifier.type = "text"
-                    sampleIdentifier.name = "sampleIdentifier"
-                    sampleIdentifier.size = 40
-                    sampleIdentifier.placeholder = "sample identifier or permId"
-                    sampleIdentifier.value = currentSampleIdentifier
-
-                    var datasets_table = document.createElement("DIV")
-                    datasets_table.id = "dataset_table"
-                    datasets_table.className = "output output_scroll"
-                    datasets_table.style.maxHeight = "10em"
-
-                    var show_datasets_btn = document.createElement("BUTTON")
-                    show_datasets_btn.className = "btn-info btn-xs"
-                    show_datasets_btn.textContent = "show datasets"
-
-                    show_datasets_btn.onclick = function() {
-                        var selected_conn = document.querySelector('input[name=connection_name]:checked')
-                        if (!selected_conn) {
-                            alert('Please choose a connection')
-                            return false
-                        }
-                        connection_name = selected_conn.value
-
-                        currentConnection = connection_name
-                        currentSampleIdentifier = sampleIdentifier.value
-                        if (!currentSampleIdentifier) {
-                            alert('Please specify a sample identifier/permId')
-                            return false
-                        }
-                        var url = env.notebook.base_url + 'openbis/sample/' + connection_name + '/' + encodeURIComponent(currentSampleIdentifier)
-
-                        fetch(url)
-                            .then( function(response) {
-                                if (response.ok) {
-                                    response.json()
-                                        .then( function(data) {
-                                            show_datasets_table(data, datasets_table)
-                                        })
-                                }
-                                else {
-                                    response.json()
-                                        .then( function(error) {
-                                            console.log(error.reason)
-                                            alert("Error: " + error.reason)
-                                        })
-                                }
-                            })
-                            .catch( function(error) {
-                                console.error('A serious network problem occured:', error)
-                            })
-
-                    }
-                    showDataSets.appendChild(sampleIdentifier)
-                    showDataSets.appendChild(show_datasets_btn)
-                    showDataSets.appendChild(datasets_table)
-
-                    // dataSetPermId only
-                    var dataset_direct = document.createElement("P")
-                    dataset_direct.style.marginTop='10px'
-                    dataset_direct.innerHTML = '<strong>enter DataSet permId directly: </strong>'
-
-                    //var dataset = $('<p>')
-                    //    .css('margin-top', '10px')
-                    //    .append($('<b>').text('... or enter DataSet permId directly: '))
-                    var datasetPermId = document.createElement("INPUT")
-                    datasetPermId.type = "text"
-                    datasetPermId.name = "datasetPermId"
-                    datasetPermId.size = "40"
-                    datasetPermId.placeholder = "dataSet permId"
-
-                    dataset_direct.appendChild(datasetPermId)
-
-                    var downloadPath = document.createElement("INPUT")
-                    downloadPath.type = "text"
-                    downloadPath.name = "downloadPath"
-                    downloadPath.size = "90"
-                    downloadPath.value = currentDownloadPath
-
-                    var path = document.createElement("DIV")
-                    path.innerHTML = "<strong>download data to path: </strong>"
-                    path.appendChild(downloadPath)
-
-                    var download_dialog_box = document.createElement("DIV")
-                    download_dialog_box.appendChild(conn_table)
-                    download_dialog_box.appendChild(showDataSets)
-                    download_dialog_box.appendChild(dataset_direct)
-                    download_dialog_box.appendChild(path)
-
-                    function downloadDataset(selected_conn, selectedPermIds, downloadPath) {
-                        var connection_name = selected_conn.value
-
-                        for (permId of selectedPermIds) {
-                            var downloadUrl = env.notebook.base_url + 'openbis/dataset/'
-                                + connection_name + '/' + permId + '/' + encodeURIComponent(downloadPath)
-
-                            fetch(downloadUrl)
-                                .then( function(response) {
-                                    if (response.ok) {
-                                        response.json()
-                                            .then( function(data) {
-                                                createFeedback('success', data.statusText)
-
-                                                // successful download:
-                                                // write statusText from returned data to notebooks metadata
-                                                writeMetaData(data)
-
-                                                // keep current download path for later use
-                                                currentDownloadPath = downloadPath
-                                            })
-                                    }
-                                    else {
-                                        response.json()
-                                            .then( function(error) {
-                                                console.log(error.reason)
-                                                alert("Error: " + error.reason)
-                                            })
-                                    }
-                                })
-                                .catch( function(error) {
-                                    console.error('A serious network problem occured:', error)
-                                })
-                        }
-                    }
-
-                    function onDownloadClick() {
-                        var selected_conn = document.querySelector('input[name=connection_name]:checked')
-                        if (! selected_conn) {
-                            alert('please choose a connection')
-                            return false
-                        }
-
-                        var selectedPermIds = []
-                        for (row of document.querySelectorAll('input[name=permId]:checked') ) {
-                            selectedPermIds.push(row.value)
-                        }
-                        if (datasetPermId.value) {
-                            selectedPermIds.push(datasetPermId.value)
-                        }
-                        if (!selectedPermIds) {
-                            alert('please select a dataset or provide a permId')
-                            return false
-                        }
-
-                        if (!downloadPath.value) {
-                            alert('Please specify where you would like to download your files!')
-                            return false
-                        }
-                        downloadDataset(selected_conn, selectedPermIds, downloadPath.value)
-                    }
-
-                    dialog.modal({
-                        body: download_dialog_box,
-                        title: 'Download openBIS DataSets',
-                        buttons: {
-                            'Cancel': {},
-                            'Download': {
-                                class: 'btn-primary btn-large',
-                                click: onDownloadClick,
-                            }
-                        },
-                        notebook: env.notebook,
-                        keyboard_manager: env.notebook.keyboard_manager
-                    })
-                }
-            }
-        }
-
-        var uploadDatasetsToOpenBis = {
-            help: 'upload Notebook and Data to openBIS',
-            icon: 'fa-upload',
-            help_index: '',
-            handler: function (env) {
-                conn_table = document.createElement("DIV")
-                var dst_title = document.createElement("STRONG")
-                dst_title.textContent = "DataSet type"
-                var dataset_types = document.createElement("SELECT")
-                dataset_types.id = "dataset_type"
-                dataset_types.className = "form-control select-xs"
-
-                var input_fields = document.createElement("DIV")
-                conn_table.id = "openbis_connections"
-
-                var onclick_cbf = function() {
-                    currentConnection = this.value
-                    getDatasetTypes(env, this.value, dataset_types, input_fields)
-                }
-
-                getOpenBisConnections(env)
-                    .done(function (data) {
-                        show_available_connections(env, data, conn_table, onclick_cbf)
-                    })
-                    .fail(function (data) {
-                        alert(data.status)
-                    })
-
-                var sample_title = document.createElement("STRONG")
-                sample_title.textContent = "Sample Identifier"
-                var sampleIdentifier = document.createElement("INPUT")
-                sampleIdentifier.type = "text"
-                sampleIdentifier.name = 'sampleIdentifier'
-                sampleIdentifier.placeholder = "Sample Identifier or permId"
-                sampleIdentifier.value = currentSampleIdentifier
-                sampleIdentifier.size  = "90"
-
-                var ds_title = document.createElement("STRONG")
-                ds_title.textContent = "DataSet files"
-                var ds_files         = document.createElement("INPUT")
-                ds_files.type        = "text"
-                ds_files.placeholder = "filenames"
-                ds_files.name        = "ds_files"
-                ds_files.size        = "90"
-
-                var inputs = document.createElement("DIV")
-                inputs.style.marginTop = '10px'
-                inputs.appendChild(dst_title)
-                inputs.appendChild(dataset_types)
-                inputs.appendChild(input_fields)
-                inputs.appendChild(sample_title)
-                inputs.appendChild(sampleIdentifier)
-                inputs.appendChild(ds_title)
-                inputs.appendChild(ds_files)
-
-                var uploadDialogBox = $('<div/>').append(conn_table).append(inputs)
-
-
-                // get the canvas for user feedback
-                var container = $('#notebook-container')
-
-                function onOk () {
-                    //var connection_name = $('input[name="connection_name"]:checked').val()
-                    var selected_connection = document.querySelector('input[name=connection_name]:checked')
-                    if (!selected_connection) {
-                        alert("No connection selected")
-                        return false
-                    }
-                    var connection_name = selected_connection.value
-
-                    var uploadUrl = env.notebook.base_url + 'openbis/dataset/' + connection_name
-
-                    var notebook = IPython.notebook
-                    var re = /\/notebooks\/(.*?)$/
-                    var files = []
-                    var filepath = window.location.pathname.match(re)[1]
-                    files.push(filepath)
-                    // FIXME
-                    //if (ds_files.val()) {
-                    //    files.push(ds_files.value)
-                    //}
-
-                    var dataSetInfo = {
-                        "type"            : dataset_types.value,
-                        "files"           : files,
-                        "sampleIdentifier": sampleIdentifier.value
-                    }
-
-                    var settings = {
-                        url: uploadUrl,
-                        processData: false,
-                        type: 'POST',
-                        dataType: 'json',
-                        data: JSON.stringify(dataSetInfo),
-                        contentType: 'application/json',
-                        success: function (data) {
-                            // display feedback to user
-                            createFeedback('success', data.statusText)
-
-                            // write statusText from returned data to notebooks metadata
-                            if ( typeof notebook.metadata.openbis === 'undefined') {
-                                notebook.metadata.openbis = {}
-                            }
-                            if ( typeof notebook.metadata.openbis.permIds === 'undefined' ) {
-                                notebook.metadata.openbis.permIds = {}
-                            }
-                            if ( data.permId ) {
-                                notebook.metadata.openbis.permIds[data.permId] = data.statusText
-                            }
-
-                        },
-                        error: function (data) {
-                            // display feedback to user
-                            var feedback = "<strong>Error: </strong>Dataset was not uploaded.<div>"
-                                + data.statusText
-                                + "</div>"
-                            createFeedback('danger', feedback)
-                        }
-                    }
-
-                    // display preloader during commit and push
-                    var preloader = '<img class="openbis-feedback" src="https://cdnjs.cloudflare.com/ajax/libs/slick-carousel/1.5.8/ajax-loader.gif">'
-
-                    // commit and push
-                    utils.ajax(settings)
-                }
-
-                if (IPython.notebook.dirty === true) {
-                    dialog.modal({
-                        body: 'Please save the notebook before uploading it to openBIS.',
-                        title: 'Save notebook first',
-                        buttons: {
-                            'Back': {}
-                        },
-                        notebook: env.notebook,
-                        keyboard_manager: env.notebook.keyboard_manager
-                    })
-                }
-                else {
-                    dialog.modal({
-                        body: uploadDialogBox,
-                        title: 'Upload openBIS DataSet',
-                        buttons: {
-                            'Cancel': {},
-                            'Upload': {
-                                class: 'btn-primary btn-large',
-                                click: onOk
-                            }
-                        },
-                        notebook: env.notebook,
-                        keyboard_manager: env.notebook.keyboard_manager
-                    })
-                }
-            }
-        }
-
-        var configureOpenBisConnections = {
-            help: 'configure openBIS connections',
-            icon: 'fa-sliders',
-            help_index: '',
-            handler: function (env) {
-                conn_table = document.createElement("DIV")
-                var dst_title = document.createElement("STRONG")
-                dst_title.textContent = "DataSet type"
-                var dataset_types = document.createElement("SELECT")
-                dataset_types.id = "dataset_type"
-                dataset_types.className = "form-control select-xs"
-
-                var input_fields = document.createElement("DIV")
-                conn_table.id = "openbis_connections"
-
-                var onclick_cbf = function() {
-                    currentConnection = this.value
-                    getDatasetTypes(env, this.value, dataset_types, input_fields)
-                }
-
-                getOpenBisConnections(env)
-                    .done(function (data) {
-                        show_available_connections(env, data, conn_table, onclick_cbf)
-                    })
-                    .fail(function (data) {
-                        alert(data.status)
-                    })
-
-                var uploadDialogBox = $('<div/>').append(conn_table)
-
-                // get the canvas for user feedback
-                var container = $('#notebook-container')
-
-                function onOk () {
-
-                }
-
-                dialog.modal({
-                    body: uploadDialogBox,
-                    title: 'Choose openBIS connection',
-                    buttons: {
-                        'Cancel': {},
-                        'Choose connection': {
-                            class: 'btn-primary btn-large',
-                            click: onOk
-                        }
-                    },
-                    notebook: env.notebook,
-                    keyboard_manager: env.notebook.keyboard_manager
-                })
-            }
-        }
-
-        function _onLoad () {
-            // show connections
-            var configure_openbis_connections = IPython.keyboard_manager.actions.register(
-                configureOpenBisConnections, 'openbis-connections', 'jupyter-openBIS')
-
-            // dnownload
-            var download_datasets = IPython.keyboard_manager.actions.register(
-                fetchDatasetFromOpenBis, 'openbis-dataset-download', 'jupyter-openBIS')
-
-            // upload
-            var upload_datasets = IPython.keyboard_manager.actions.register(
-                uploadDatasetsToOpenBis, 'openbis-dataset-upload', 'jupyter-openBIS')
-
-            // add button for new action
-            IPython.toolbar.add_buttons_group([configure_openbis_connections, download_datasets, upload_datasets])
-        }
-
-        return {load_ipython_extension: _onLoad}
-    })
diff --git a/jupyter-openbis-extension/static/downloadDialog.js b/jupyter-openbis-extension/static/downloadDialog.js
new file mode 100644
index 0000000000000000000000000000000000000000..973ff5e3066bedc3a272f487cc0329f68119e48f
--- /dev/null
+++ b/jupyter-openbis-extension/static/downloadDialog.js
@@ -0,0 +1,242 @@
+define([
+        "base/js/dialog",
+        "./common",
+        "./state"
+    ],
+    function (dialog, common, state) {
+
+        function writeMetaData(data) {
+            var notebook = IPython.notebook
+            if (typeof notebook.metadata.openbis_connections === 'undefined') {
+                notebook.metadata.openbis_connections = {}
+            }
+            if (typeof notebook.metadata.openbis_connections[data.url] === 'undefined') {
+                notebook.metadata.openbis_connections[data.url] = {}
+            }
+            // store metadata about the downloaded files into the notebook-metadata
+            if (data.permId) {
+                notebook.metadata.openbis_connections[data.url][data.permId] = {
+                    "permId": data.permId,
+                    "path": data.path,
+                    "dataStore": data.dataStore,
+                    "location": data.location,
+                    "size": data.size,
+                    "status": data.statusText
+                }
+            }
+        }
+
+        function show_datasets_table(data, datasets_table) {
+            var table = document.createElement("TABLE")
+            table.className = "table-bordered table-striped table-condensed text-nowrap"
+            var thead = table.createTHead()
+            var t_row = thead.insertRow()
+            var titles = ['', 'permId', 'Type', 'Experiment', 'Registration Date', 'Status', 'Size']
+            titles.forEach(function (title) {
+                t_row.insertCell().textContent = title
+            })
+            var tbody = table.createTBody()
+
+            for (dataSet of data.dataSets) {
+
+                var permId = document.createElement("INPUT")
+                permId.type = "checkbox"
+                permId.name = "permId"
+                permId.value = dataSet.permId
+
+                var row = tbody.insertRow()
+                row.insertCell().appendChild(permId)
+                row.insertCell().textContent = dataSet.permId
+                row.insertCell().textContent = dataSet.type
+                row.insertCell().textContent = dataSet.experiment
+                row.insertCell().textContent = dataSet.registrationDate
+                row.insertCell().textContent = dataSet.status
+                row.insertCell().textContent = dataSet.size
+            }
+
+            while (datasets_table.firstChild) {
+                datasets_table.removeChild(datasets_table.firstChild);
+            }
+            datasets_table.appendChild(table)
+        }
+
+
+        return {
+            help: 'Download openBIS datasets to your local harddrive',
+            icon: 'fa-download',
+            help_index: '',
+            handler: function (env) {
+                conn_table = document.createElement("DIV")
+                conn_table.id = "openbis_connections"
+
+                var showDataSets = document.createElement("DIV")
+                var title = document.createElement("STRONG")
+                title.textContent = "Sample identifier / permId: "
+                showDataSets.appendChild(title)
+                showDataSets.style.marginTop = '10px'
+
+                var sampleIdentifier = document.createElement("INPUT")
+                sampleIdentifier.type = "text"
+                sampleIdentifier.name = "sampleIdentifier"
+                sampleIdentifier.size = 40
+                sampleIdentifier.placeholder = "sample identifier or permId"
+                sampleIdentifier.value = ''
+
+                var datasets_table = document.createElement("DIV")
+                datasets_table.id = "dataset_table"
+                datasets_table.className = "output output_scroll"
+                datasets_table.style.maxHeight = "10em"
+
+                var show_datasets_btn = document.createElement("BUTTON")
+                show_datasets_btn.className = "btn-info btn-xs"
+                show_datasets_btn.textContent = "show datasets"
+
+                show_datasets_btn.onclick = function () {
+                    var selected_conn = state.connection.name
+                    if (!selected_conn) {
+                        alert('Please choose a connection')
+                        return false
+                    }
+                    connection_name = state.connection.name
+
+                    currentConnection = connection_name
+                    currentSampleIdentifier = sampleIdentifier.value
+                    if (!currentSampleIdentifier) {
+                        alert('Please specify a sample identifier/permId')
+                        return false
+                    }
+                    var url = env.notebook.base_url + 'openbis/sample/' + connection_name + '/' + encodeURIComponent(currentSampleIdentifier)
+
+                    fetch(url)
+                        .then(function (response) {
+                            if (response.ok) {
+                                response.json()
+                                    .then(function (data) {
+                                        show_datasets_table(data, datasets_table)
+                                    })
+                            } else {
+                                response.json()
+                                    .then(function (error) {
+                                        console.log(error.reason)
+                                        alert("Error: " + error.reason)
+                                    })
+                            }
+                        })
+                        .catch(function (error) {
+                            console.error('A serious network problem occured:', error)
+                        })
+
+                }
+                showDataSets.appendChild(sampleIdentifier)
+                showDataSets.appendChild(show_datasets_btn)
+                showDataSets.appendChild(datasets_table)
+
+                // dataSetPermId only
+                var dataset_direct = document.createElement("P")
+                dataset_direct.style.marginTop = '10px'
+                dataset_direct.innerHTML = '<strong>enter DataSet permId directly: </strong>'
+
+                //var dataset = $('<p>')
+                //    .css('margin-top', '10px')
+                //    .append($('<b>').text('... or enter DataSet permId directly: '))
+                var datasetPermId = document.createElement("INPUT")
+                datasetPermId.type = "text"
+                datasetPermId.name = "datasetPermId"
+                datasetPermId.size = "40"
+                datasetPermId.placeholder = "dataSet permId"
+
+                dataset_direct.appendChild(datasetPermId)
+
+                var downloadPath = document.createElement("INPUT")
+                downloadPath.type = "text"
+                downloadPath.name = "downloadPath"
+                downloadPath.size = "90"
+                downloadPath.value = ''
+
+                var path = document.createElement("DIV")
+                path.innerHTML = "<strong>download data to path: </strong>"
+                path.appendChild(downloadPath)
+
+                var download_dialog_box = document.createElement("DIV")
+                download_dialog_box.appendChild(conn_table)
+                download_dialog_box.appendChild(showDataSets)
+                download_dialog_box.appendChild(dataset_direct)
+                download_dialog_box.appendChild(path)
+
+                function downloadDataset(connection_name, selectedPermIds, downloadPath) {
+
+                    for (permId of selectedPermIds) {
+                        var downloadUrl = env.notebook.base_url + 'openbis/dataset/' +
+                            connection_name + '/' + permId + '/' + encodeURIComponent(downloadPath)
+
+                        fetch(downloadUrl)
+                            .then(function (response) {
+                                if (response.ok) {
+                                    response.json()
+                                        .then(function (data) {
+                                            common.createFeedback('success', data.statusText)
+
+                                            // successful download:
+                                            // write statusText from returned data to notebooks metadata
+                                            writeMetaData(data)
+
+                                            // keep current download path for later use
+                                            currentDownloadPath = downloadPath
+                                        })
+                                } else {
+                                    response.json()
+                                        .then(function (error) {
+                                            console.log(error.reason)
+                                            alert("Error: " + error.reason)
+                                        })
+                                }
+                            })
+                            .catch(function (error) {
+                                console.error('A serious network problem occured:', error)
+                            })
+                    }
+                }
+
+                function onDownloadClick() {
+                    var selected_conn = state.connection.name
+                    if (!selected_conn) {
+                        alert('please choose a connection')
+                        return false
+                    }
+
+                    var selectedPermIds = []
+                    for (row of document.querySelectorAll('input[name=permId]:checked')) {
+                        selectedPermIds.push(row.value)
+                    }
+                    if (datasetPermId.value) {
+                        selectedPermIds.push(datasetPermId.value)
+                    }
+                    if (!selectedPermIds) {
+                        alert('please select a dataset or provide a permId')
+                        return false
+                    }
+
+                    if (!downloadPath.value) {
+                        alert('Please specify where you would like to download your files!')
+                        return false
+                    }
+                    downloadDataset(selected_conn, selectedPermIds, downloadPath.value)
+                }
+
+                dialog.modal({
+                    body: download_dialog_box,
+                    title: 'Download openBIS DataSets',
+                    buttons: {
+                        'Cancel': {},
+                        'Download': {
+                            class: 'btn-primary btn-large',
+                            click: onDownloadClick,
+                        }
+                    },
+                    notebook: env.notebook,
+                    keyboard_manager: env.notebook.keyboard_manager
+                })
+            }
+        }
+    }
+)
\ No newline at end of file
diff --git a/jupyter-openbis-extension/static/main.js b/jupyter-openbis-extension/static/main.js
new file mode 100644
index 0000000000000000000000000000000000000000..777e69d8ab57d2c25566d5425d904d546db115ef
--- /dev/null
+++ b/jupyter-openbis-extension/static/main.js
@@ -0,0 +1,33 @@
+define([
+        "base/js/namespace",
+        "./connectionDialog",
+        "./uploadDialog",
+        "./downloadDialog"
+    ],
+    function (IPython, connectionDialog, uploadDialog, downloadDialog) {
+        var ds_type = document.createElement("SELECT")
+        ds_type.size = '40'
+        ds_type.className = "select-xs"
+        ds_type.id = "dataset_type"
+
+        function _onLoad() {
+            // show connections
+            var configure_openbis_connections = IPython.keyboard_manager.actions.register(
+                connectionDialog, 'openbis-connections', 'jupyter-openBIS')
+
+            // dnownload
+            var download_datasets = IPython.keyboard_manager.actions.register(
+                downloadDialog, 'openbis-dataset-download', 'jupyter-openBIS')
+
+            // upload
+            var upload_datasets = IPython.keyboard_manager.actions.register(
+                uploadDialog, 'openbis-dataset-upload', 'jupyter-openBIS')
+
+            // add button for new action
+            IPython.toolbar.add_buttons_group([configure_openbis_connections, download_datasets, upload_datasets])
+        }
+
+        return {
+            load_ipython_extension: _onLoad
+        }
+    })
\ No newline at end of file
diff --git a/jupyter-openbis-extension/static/state.js b/jupyter-openbis-extension/static/state.js
new file mode 100644
index 0000000000000000000000000000000000000000..e72d76e13f2952f91369b45130869e27d5c567ec
--- /dev/null
+++ b/jupyter-openbis-extension/static/state.js
@@ -0,0 +1,10 @@
+define([],
+    function () {
+        return {
+            connection: {
+                name: null,
+                candidateName: null
+            }
+        }
+    }
+)
\ No newline at end of file
diff --git a/jupyter-openbis-extension/static/uploadDIalog.js b/jupyter-openbis-extension/static/uploadDIalog.js
new file mode 100644
index 0000000000000000000000000000000000000000..ff3a51d8a54a15f64513bc5df2eebe2055c86792
--- /dev/null
+++ b/jupyter-openbis-extension/static/uploadDIalog.js
@@ -0,0 +1,213 @@
+define([
+        "base/js/dialog",
+        "base/js/utils",
+        "jquery",
+        "./state",
+        "./common",
+    ],
+    function (dialog, utils, $, state, common) {
+
+        function getDatasetTypes(env, connection_name, dataset_types, input_fields) {
+            // get all DatasetTypes of a given connection
+
+            var url = env.notebook.base_url + 'openbis/datasetTypes/' + connection_name
+            fetch(url)
+                .then(function (response) {
+                    if (response.ok) {
+                        response.json()
+                            .then(function (data) {
+                                //console.log(data.dataSetTypes)
+
+                                var change_input_fields = function () {
+                                    // remove existing input fields
+                                    while (input_fields.firstChild) {
+                                        input_fields.removeChild(input_fields.firstChild)
+                                    }
+
+                                    // for every property assignment, create an input field.
+                                    for (pa of dts[dataset_types.selectedIndex].propertyAssignments) {
+                                        //var input_title = document.createTextNode(pa.label + ": ")
+                                        var input_field = document.createElement("INPUT")
+                                        input_field.type = "text"
+                                        input_field.name = pa.code
+                                        input_field.placeholder = pa.description ? pa.label + ": " + pa.description : pa.label
+                                        input_field.size = 90
+
+                                        //input_fields.appendChild(input_title)
+                                        input_fields.appendChild(input_field)
+                                        input_fields.appendChild(document.createElement("BR"))
+                                    }
+                                }
+                                dataset_types.onchange = change_input_fields
+
+                                // remove the old and add the new dataset-types
+                                dts = data.dataSetTypes
+                                while (dataset_types.firstChild) {
+                                    dataset_types.removeChild(dataset_types.firstChild);
+                                }
+                                for (dt of dts) {
+                                    var option = document.createElement("OPTION")
+                                    option.value = dt.code
+                                    option.textContent = dt.description ? dt.code + ": " + dt.description : dt.code
+                                    dataset_types.appendChild(option)
+                                }
+                                // change the input fields, since we just received new datasetTypes
+                                change_input_fields()
+
+                            })
+                            .catch(function (error) {
+                                console.error("Error while parsing dataset types", error)
+                            })
+
+                    } else {
+                        while (dataset_types.firstChild) {
+                            dataset_types.removeChild(dataset_types.firstChild);
+                        }
+                    }
+                })
+                .catch(function (error) {
+                    console.error("Error while fetching dataset types:", error)
+                })
+        }
+
+        return {
+            help: 'upload Notebook and Data to openBIS',
+            icon: 'fa-upload',
+            help_index: '',
+            handler: function (env) {
+
+                conn_table = document.createElement("DIV")
+                var dst_title = document.createElement("STRONG")
+                dst_title.textContent = "DataSet type"
+                var dataset_types = document.createElement("SELECT")
+                dataset_types.id = "dataset_type"
+                dataset_types.className = "form-control select-xs"
+
+                var input_fields = document.createElement("DIV")
+                conn_table.id = "openbis_connections"
+
+                getDatasetTypes(env, state.connection.name, dataset_types, input_fields)
+
+                var sample_title = document.createElement("STRONG")
+                sample_title.textContent = "Sample Identifier"
+                var sampleIdentifier = document.createElement("INPUT")
+                sampleIdentifier.type = "text"
+                sampleIdentifier.name = 'sampleIdentifier'
+                sampleIdentifier.placeholder = "Sample Identifier or permId"
+                sampleIdentifier.value = ''
+                sampleIdentifier.size = "90"
+
+                var ds_title = document.createElement("STRONG")
+                ds_title.textContent = "DataSet files"
+                var ds_files = document.createElement("INPUT")
+                ds_files.type = "text"
+                ds_files.placeholder = "filenames"
+                ds_files.name = "ds_files"
+                ds_files.size = "90"
+
+                var inputs = document.createElement("DIV")
+                inputs.style.marginTop = '10px'
+                inputs.appendChild(dst_title)
+                inputs.appendChild(dataset_types)
+                inputs.appendChild(input_fields)
+                inputs.appendChild(sample_title)
+                inputs.appendChild(sampleIdentifier)
+                inputs.appendChild(ds_title)
+                inputs.appendChild(ds_files)
+
+                var uploadDialogBox = $('<div/>').append(conn_table).append(inputs)
+
+                function onOk() {
+                    var connection_name = state.connection.name
+
+                    if (!connection_name) {
+                        alert("No connection selected")
+                        return false
+                    }
+
+                    var uploadUrl = env.notebook.base_url + 'openbis/dataset/' + connection_name
+
+                    var notebook = IPython.notebook
+                    var re = /\/notebooks\/(.*?)$/
+                    var files = []
+                    var filepath = window.location.pathname.match(re)[1]
+                    files.push(filepath)
+                    // FIXME
+                    //if (ds_files.val()) {
+                    //    files.push(ds_files.value)
+                    //}
+
+                    var dataSetInfo = {
+                        "type": dataset_types.value,
+                        "files": files,
+                        "sampleIdentifier": sampleIdentifier.value
+                    }
+
+                    var settings = {
+                        url: uploadUrl,
+                        processData: false,
+                        type: 'POST',
+                        dataType: 'json',
+                        data: JSON.stringify(dataSetInfo),
+                        contentType: 'application/json',
+                        success: function (data) {
+                            // display feedback to user
+                            common.createFeedback('success', data.statusText)
+
+                            // write statusText from returned data to notebooks metadata
+                            if (typeof notebook.metadata.openbis === 'undefined') {
+                                notebook.metadata.openbis = {}
+                            }
+                            if (typeof notebook.metadata.openbis.permIds === 'undefined') {
+                                notebook.metadata.openbis.permIds = {}
+                            }
+                            if (data.permId) {
+                                notebook.metadata.openbis.permIds[data.permId] = data.statusText
+                            }
+
+                        },
+                        error: function (data) {
+                            // display feedback to user
+                            var feedback = "<strong>Error: </strong>Dataset was not uploaded.<div>" +
+                                data.statusText +
+                                "</div>"
+                            common.createFeedback('danger', feedback)
+                        }
+                    }
+
+                    // display preloader during commit and push
+                    var preloader = '<img class="openbis-feedback" src="https://cdnjs.cloudflare.com/ajax/libs/slick-carousel/1.5.8/ajax-loader.gif">'
+
+                    // commit and push
+                    utils.ajax(settings)
+                }
+
+                if (IPython.notebook.dirty === true) {
+                    dialog.modal({
+                        body: 'Please save the notebook before uploading it to openBIS.',
+                        title: 'Save notebook first',
+                        buttons: {
+                            'Back': {}
+                        },
+                        notebook: env.notebook,
+                        keyboard_manager: env.notebook.keyboard_manager
+                    })
+                } else {
+                    dialog.modal({
+                        body: uploadDialogBox,
+                        title: 'Upload openBIS DataSet',
+                        buttons: {
+                            'Cancel': {},
+                            'Upload': {
+                                class: 'btn-primary btn-large',
+                                click: onOk
+                            }
+                        },
+                        notebook: env.notebook,
+                        keyboard_manager: env.notebook.keyboard_manager
+                    })
+                }
+            }
+        }
+    }
+)
\ No newline at end of file
diff --git a/openbis-connections.yaml b/openbis-connections.yaml
index 4e02d7ff96ecd04da4ee3b7195f6a75e416c719d..754cd1c4c91dfe88e34e94d6368a2e36304705da 100644
--- a/openbis-connections.yaml
+++ b/openbis-connections.yaml
@@ -1,11 +1,16 @@
 connections:
-    - name                : local test openBIS instance
-      url                 : https://localhost:8443
-      verify_certificates : false
-      username            : username
-      password            : password
-    - name                : productive openBIS instance
-      url                 : https://openbis.example.com
-      verify_certificates : true
-      username            : username
-      password            : password
+  - name: local test openBIS instance
+    url: https://localhost:8443
+    verify_certificates: false
+    username: username
+    password: password
+  - name: vagrant openBIS instance
+    url: https://localhost:8122
+    verify_certificates: false
+    username: admin 
+    password: password
+  - name: productive openBIS instance
+    url: https://openbis.example.com
+    verify_certificates: true
+    username: username
+    password: password
diff --git a/setup.py b/setup.py
index 15616447e27ea2606253377bd93f459a8a43d147..5eb05d0e8fa2ed017d3c0c8b07134ecc7377537e 100644
--- a/setup.py
+++ b/setup.py
@@ -36,7 +36,7 @@ setup(
     data_files=[
         # like `jupyter nbextension install --sys-prefix`
         ("share/jupyter/nbextensions/jupyter-openbis-extension", [
-            "jupyter-openbis-extension/static/dialog.js",
+            "jupyter-openbis-extension/static/main.js",
         ]),
         # like `jupyter nbextension enable --sys-prefix`
         ("etc/jupyter/nbconfig/notebook.d", [
diff --git a/vagrant/Vagrantfile b/vagrant/Vagrantfile
new file mode 100644
index 0000000000000000000000000000000000000000..b21d6f4e6bf9ebc4cf8ecda3d33c32897b9aedf2
--- /dev/null
+++ b/vagrant/Vagrantfile
@@ -0,0 +1,28 @@
+# -*- mode: ruby -*-
+# vi: set ft=ruby :
+
+Vagrant.configure("2") do |config|
+  config.vm.box = "ubuntu/bionic64"
+  config.vm.box_version = "20180531.0.0"
+
+  config.vm.provider "virtualbox" do |v|
+    v.memory = 4096
+    v.cpus = 4
+    v.name = "jupyter-openbis-extension"
+  end
+
+  # Needs to be unique among VMs
+  config.notify_forwarder.port = 27282
+
+  config.vm.synced_folder "..", "/home/vagrant/jupyter-openbis-extension"
+  config.vm.synced_folder '.', '/vagrant', disabled: true
+  config.vm.synced_folder "files", "/files", :mount_options => ["ro"]
+  config.vm.synced_folder "shared", "/shared"
+
+  config.vm.network "forwarded_port", guest: 8122, host: 8122, host_ip: "0.0.0.0"
+  config.vm.network "forwarded_port", guest: 8123, host: 8123, host_ip: "0.0.0.0"
+  config.vm.network "forwarded_port", guest: 8888, host: 8888, host_ip: "0.0.0.0"
+
+  config.vm.provision :shell, path: "bootstrap.sh"
+  config.vm.provision :shell, path: "files/start-services.sh", run: "always", privileged: true
+end
diff --git a/vagrant/bootstrap.sh b/vagrant/bootstrap.sh
new file mode 100644
index 0000000000000000000000000000000000000000..64620765b3dc52e51c25bb8de94bc42f0df97988
--- /dev/null
+++ b/vagrant/bootstrap.sh
@@ -0,0 +1,18 @@
+#!/usr/bin/env bash
+
+sudo locale-gen en_US.UTF-8
+
+echo "
+LC_ALL=en_US.UTF-8
+LANG=en_US.UTF-8
+" >> /etc/environment
+
+apt update
+apt install -y python3 python3-pip openjdk-8-jdk postgresql unzip
+
+cp /files/pg_hba.conf /etc/postgresql/10/main/pg_hba.conf 
+service postgresql restart
+sleep 10 # let the db engine start
+
+sudo -E -u postgres -H -i /files/setup-postgres.sh
+sudo -E -u vagrant -H -i /files/setup-vagrant.sh
diff --git a/vagrant/files/console.properties b/vagrant/files/console.properties
new file mode 100644
index 0000000000000000000000000000000000000000..faa327b56b38bcb8352771ef160acba48fddf193
--- /dev/null
+++ b/vagrant/files/console.properties
@@ -0,0 +1,62 @@
+#
+# The path where openBIS will be installed.
+# 
+# Example : 
+#     INSTALL_PATH=/home/openbis/
+#
+#   will result in the following directory structure
+#
+#   + /home/openbis
+#     +  bin/
+#     +  servers/
+#        + core-plugins/
+#        + openBIS-server/
+#        + datastore_server/
+# 
+INSTALL_PATH=/home/vagrant/openbis
+
+#
+# The path where openBIS will keep the imported data (e.g. images, analysis files)
+# and its incoming folders. 
+#
+DSS_ROOT_DIR=/home/vagrant/dss_root
+
+# Possible configuration options
+#   'local' - if the openBIS servers will only be accessed from this machine
+#   'server' - if the installation is meant to be accessible for remote users
+INSTALLATION_TYPE=local
+
+# Path to the file which should replace the current Java key store file
+#KEY_STORE_FILE = <path to key store>
+
+# Password of the key store
+KEY_STORE_PASSWORD = changeit
+
+# Password of the key
+KEY_PASSWORD = changeit
+
+# Standard technology PROTEOMICS is disabled by default
+#PROTEOMICS = true
+
+# Standard technology SCREENING is disabled by default
+#SCREENING = true
+
+# Standard technology ILLUMINA-NGS (ETH BSSE Setup) is disabled by default
+#ILLUMINA-NGS = true
+
+# Standard technology ELN-LIMS is disabled by default
+#ELN-LIMS = true
+
+# Standard technology MICROSCOPY is disabled by default
+#MICROSCOPY = true
+
+# Standard technology FLOW CYTOMETRY is disabled by default
+#FLOW = true
+
+# Full ELN/LIMS master data is enabled by default. This setting is meaningful only if ELN-LIMS is enabled
+ELN-LIMS-MASTER-DATA = false
+
+#
+# Comma-separated list of databases to backup. If the list is empty or undefined all databases
+# will be backauped.
+#DATABASES_TO_BACKUP =
\ No newline at end of file
diff --git a/vagrant/files/jupyter_notebook_config.py b/vagrant/files/jupyter_notebook_config.py
new file mode 100644
index 0000000000000000000000000000000000000000..e137e1661539d2436a296e0332a2219a90ec8fd1
--- /dev/null
+++ b/vagrant/files/jupyter_notebook_config.py
@@ -0,0 +1,765 @@
+# Configuration file for jupyter-notebook.
+
+# ------------------------------------------------------------------------------
+# Application(SingletonConfigurable) configuration
+# ------------------------------------------------------------------------------
+
+# This is an application.
+
+# The date format used by logging formatters for %(asctime)s
+#c.Application.log_datefmt = '%Y-%m-%d %H:%M:%S'
+
+# The Logging format template
+#c.Application.log_format = '[%(name)s]%(highlevel)s %(message)s'
+
+# Set the log level by value or name.
+#c.Application.log_level = 30
+
+# ------------------------------------------------------------------------------
+# JupyterApp(Application) configuration
+# ------------------------------------------------------------------------------
+
+# Base class for Jupyter applications
+
+# Answer yes to any prompts.
+#c.JupyterApp.answer_yes = False
+
+# Full path of a config file.
+#c.JupyterApp.config_file = ''
+
+# Specify a config file to load.
+#c.JupyterApp.config_file_name = ''
+
+# Generate default config file.
+#c.JupyterApp.generate_config = False
+
+# ------------------------------------------------------------------------------
+# NotebookApp(JupyterApp) configuration
+# ------------------------------------------------------------------------------
+
+# Set the Access-Control-Allow-Credentials: true header
+#c.NotebookApp.allow_credentials = False
+
+# Set the Access-Control-Allow-Origin header
+#
+#  Use '*' to allow any origin to access your server.
+#
+#  Takes precedence over allow_origin_pat.
+c.NotebookApp.allow_origin = '*'
+
+# Use a regular expression for the Access-Control-Allow-Origin header
+#
+#  Requests from an origin matching the expression will get replies with:
+#
+#      Access-Control-Allow-Origin: origin
+#
+#  where `origin` is the origin of the request.
+#
+#  Ignored if allow_origin is set.
+#c.NotebookApp.allow_origin_pat = ''
+
+# Allow password to be changed at login for the notebook server.
+#
+#  While loggin in with a token, the notebook server UI will give the opportunity
+#  to the user to enter a new password at the same time that will replace the
+#  token login mechanism.
+#
+#  This can be set to false to prevent changing password from the UI/API.
+#c.NotebookApp.allow_password_change = True
+
+# Allow requests where the Host header doesn't point to a local server
+#
+#  By default, requests get a 403 forbidden response if the 'Host' header shows
+#  that the browser thinks it's on a non-local domain. Setting this option to
+#  True disables this check.
+#
+#  This protects against 'DNS rebinding' attacks, where a remote web server
+#  serves you a page and then changes its DNS to send later requests to a local
+#  IP, bypassing same-origin checks.
+#
+#  Local IP addresses (such as 127.0.0.1 and ::1) are allowed as local, along
+#  with hostnames configured in local_hostnames.
+c.NotebookApp.allow_remote_access = True
+
+# Whether to allow the user to run the notebook as root.
+#c.NotebookApp.allow_root = False
+
+# DEPRECATED use base_url
+#c.NotebookApp.base_project_url = '/'
+
+# The base URL for the notebook server.
+#
+#  Leading and trailing slashes can be omitted, and will automatically be added.
+#c.NotebookApp.base_url = '/'
+
+# Specify what command to use to invoke a web browser when opening the notebook.
+#  If not specified, the default browser will be determined by the `webbrowser`
+#  standard library module, which allows setting of the BROWSER environment
+#  variable to override it.
+#c.NotebookApp.browser = ''
+
+# The full path to an SSL/TLS certificate file.
+#c.NotebookApp.certfile = ''
+
+# The full path to a certificate authority certificate for SSL/TLS client
+#  authentication.
+#c.NotebookApp.client_ca = ''
+
+# The config manager class to use
+#c.NotebookApp.config_manager_class = 'notebook.services.config.manager.ConfigManager'
+
+# The notebook manager class to use.
+#c.NotebookApp.contents_manager_class = 'notebook.services.contents.largefilemanager.LargeFileManager'
+
+# Extra keyword arguments to pass to `set_secure_cookie`. See tornado's
+#  set_secure_cookie docs for details.
+#c.NotebookApp.cookie_options = {}
+
+# The random bytes used to secure cookies. By default this is a new random
+#  number every time you start the Notebook. Set it to a value in a config file
+#  to enable logins to persist across server sessions.
+#
+#  Note: Cookie secrets should be kept private, do not share config files with
+#  cookie_secret stored in plaintext (you can read the value from a file).
+#c.NotebookApp.cookie_secret = b''
+
+# The file where the cookie secret is stored.
+#c.NotebookApp.cookie_secret_file = ''
+
+# Override URL shown to users.
+#
+#  Replace actual URL, including protocol, address, port and base URL, with the
+#  given value when displaying URL to the users. Do not change the actual
+#  connection URL. If authentication token is enabled, the token is added to the
+#  custom URL automatically.
+#
+#  This option is intended to be used when the URL to display to the user cannot
+#  be determined reliably by the Jupyter notebook server (proxified or
+#  containerized setups for example).
+#c.NotebookApp.custom_display_url = ''
+
+# The default URL to redirect to from `/`
+#c.NotebookApp.default_url = '/tree'
+
+# Disable cross-site-request-forgery protection
+#
+#  Jupyter notebook 4.3.1 introduces protection from cross-site request
+#  forgeries, requiring API requests to either:
+#
+#  - originate from pages served by this server (validated with XSRF cookie and
+#  token), or - authenticate with a token
+#
+#  Some anonymous compute resources still desire the ability to run code,
+#  completely without authentication. These services can disable all
+#  authentication and security checks, with the full knowledge of what that
+#  implies.
+c.NotebookApp.disable_check_xsrf = True 
+
+# Whether to enable MathJax for typesetting math/TeX
+#
+#  MathJax is the javascript library Jupyter uses to render math/LaTeX. It is
+#  very large, so you may want to disable it if you have a slow internet
+#  connection, or for offline use of the notebook.
+#
+#  When disabled, equations etc. will appear as their untransformed TeX source.
+#c.NotebookApp.enable_mathjax = True
+
+# extra paths to look for Javascript notebook extensions
+#c.NotebookApp.extra_nbextensions_path = []
+
+# handlers that should be loaded at higher priority than the default services
+#c.NotebookApp.extra_services = []
+
+# Extra paths to search for serving static files.
+#
+#  This allows adding javascript/css to be available from the notebook server
+#  machine, or overriding individual files in the IPython
+#c.NotebookApp.extra_static_paths = []
+
+# Extra paths to search for serving jinja templates.
+#
+#  Can be used to override templates from notebook.templates.
+#c.NotebookApp.extra_template_paths = []
+
+##
+#c.NotebookApp.file_to_run = ''
+
+# Extra keyword arguments to pass to `get_secure_cookie`. See tornado's
+#  get_secure_cookie docs for details.
+#c.NotebookApp.get_secure_cookie_kwargs = {}
+
+# Deprecated: Use minified JS file or not, mainly use during dev to avoid JS
+#  recompilation
+#c.NotebookApp.ignore_minified_js = False
+
+# (bytes/sec) Maximum rate at which stream output can be sent on iopub before
+#  they are limited.
+#c.NotebookApp.iopub_data_rate_limit = 1000000
+
+# (msgs/sec) Maximum rate at which messages can be sent on iopub before they are
+#  limited.
+#c.NotebookApp.iopub_msg_rate_limit = 1000
+
+# The IP address the notebook server will listen on.
+c.NotebookApp.ip = '0.0.0.0'
+
+# Supply extra arguments that will be passed to Jinja environment.
+#c.NotebookApp.jinja_environment_options = {}
+
+# Extra variables to supply to jinja templates when rendering.
+#c.NotebookApp.jinja_template_vars = {}
+
+# The kernel manager class to use.
+#c.NotebookApp.kernel_manager_class = 'notebook.services.kernels.kernelmanager.MappingKernelManager'
+
+# The kernel spec manager class to use. Should be a subclass of
+#  `jupyter_client.kernelspec.KernelSpecManager`.
+#
+#  The Api of KernelSpecManager is provisional and might change without warning
+#  between this version of Jupyter and the next stable one.
+#c.NotebookApp.kernel_spec_manager_class = 'jupyter_client.kernelspec.KernelSpecManager'
+
+# The full path to a private key file for usage with SSL/TLS.
+#c.NotebookApp.keyfile = ''
+
+# Hostnames to allow as local when allow_remote_access is False.
+#
+#  Local IP addresses (such as 127.0.0.1 and ::1) are automatically accepted as
+#  local as well.
+#c.NotebookApp.local_hostnames = ['localhost']
+
+# The login handler class to use.
+#c.NotebookApp.login_handler_class = 'notebook.auth.login.LoginHandler'
+
+# The logout handler class to use.
+#c.NotebookApp.logout_handler_class = 'notebook.auth.logout.LogoutHandler'
+
+# The MathJax.js configuration file that is to be used.
+#c.NotebookApp.mathjax_config = 'TeX-AMS-MML_HTMLorMML-full,Safe'
+
+# A custom url for MathJax.js. Should be in the form of a case-sensitive url to
+#  MathJax, for example:  /static/components/MathJax/MathJax.js
+#c.NotebookApp.mathjax_url = ''
+
+# Sets the maximum allowed size of the client request body, specified in  the
+#  Content-Length request header field. If the size in a request  exceeds the
+#  configured value, a malformed HTTP message is returned to the client.
+#
+#  Note: max_body_size is applied even in streaming mode.
+#c.NotebookApp.max_body_size = 536870912
+
+# Gets or sets the maximum amount of memory, in bytes, that is allocated  for
+#  use by the buffer manager.
+#c.NotebookApp.max_buffer_size = 536870912
+
+# Dict of Python modules to load as notebook server extensions.Entry values can
+#  be used to enable and disable the loading ofthe extensions. The extensions
+#  will be loaded in alphabetical order.
+#c.NotebookApp.nbserver_extensions = {}
+
+# The directory to use for notebooks and kernels.
+#c.NotebookApp.notebook_dir = ''
+
+# Whether to open in a browser after starting. The specific browser used is
+#  platform dependent and determined by the python standard library `webbrowser`
+#  module, unless it is overridden using the --browser (NotebookApp.browser)
+#  configuration option.
+c.NotebookApp.open_browser = False
+
+# Hashed password to use for web authentication.
+#
+#  To generate, type in a python/IPython shell:
+#
+#    from notebook.auth import passwd; passwd()
+#
+#  The string should be of the form type:salt:hashed-password.
+#c.NotebookApp.password = ''
+
+# Forces users to use a password for the Notebook server. This is useful in a
+#  multi user environment, for instance when everybody in the LAN can access each
+#  other's machine through ssh.
+#
+#  In such a case, server the notebook server on localhost is not secure since
+#  any user can connect to the notebook server via ssh.
+c.NotebookApp.password_required = False
+
+# The port the notebook server will listen on.
+#c.NotebookApp.port = 8888
+
+# The number of additional ports to try if the specified port is not available.
+#c.NotebookApp.port_retries = 50
+
+# DISABLED: use %pylab or %matplotlib in the notebook to enable matplotlib.
+#c.NotebookApp.pylab = 'disabled'
+
+# If True, display a button in the dashboard to quit (shutdown the notebook
+#  server).
+#c.NotebookApp.quit_button = True
+
+# (sec) Time window used to  check the message and data rate limits.
+#c.NotebookApp.rate_limit_window = 3
+
+# Reraise exceptions encountered loading server extensions?
+#c.NotebookApp.reraise_server_extension_failures = False
+
+# DEPRECATED use the nbserver_extensions dict instead
+#c.NotebookApp.server_extensions = []
+
+# The session manager class to use.
+#c.NotebookApp.session_manager_class = 'notebook.services.sessions.sessionmanager.SessionManager'
+
+# Shut down the server after N seconds with no kernels or terminals running and
+#  no activity. This can be used together with culling idle kernels
+#  (MappingKernelManager.cull_idle_timeout) to shutdown the notebook server when
+#  it's not in use. This is not precisely timed: it may shut down up to a minute
+#  later. 0 (the default) disables this automatic shutdown.
+#c.NotebookApp.shutdown_no_activity_timeout = 0
+
+# Supply SSL options for the tornado HTTPServer. See the tornado docs for
+#  details.
+#c.NotebookApp.ssl_options = {}
+
+# Supply overrides for terminado. Currently only supports "shell_command".
+#c.NotebookApp.terminado_settings = {}
+
+# Set to False to disable terminals.
+#
+#  This does *not* make the notebook server more secure by itself. Anything the
+#  user can in a terminal, they can also do in a notebook.
+#
+#  Terminals may also be automatically disabled if the terminado package is not
+#  available.
+#c.NotebookApp.terminals_enabled = True
+
+# Token used for authenticating first-time connections to the server.
+#
+#  When no password is enabled, the default is to generate a new, random token.
+#
+#  Setting to an empty string disables authentication altogether, which is NOT
+#  RECOMMENDED.
+c.NotebookApp.token = ''
+
+# Supply overrides for the tornado.web.Application that the Jupyter notebook
+#  uses.
+#c.NotebookApp.tornado_settings = {}
+
+# Whether to trust or not X-Scheme/X-Forwarded-Proto and X-Real-Ip/X-Forwarded-
+#  For headerssent by the upstream reverse proxy. Necessary if the proxy handles
+#  SSL
+#c.NotebookApp.trust_xheaders = False
+
+# DEPRECATED, use tornado_settings
+#c.NotebookApp.webapp_settings = {}
+
+# Specify Where to open the notebook on startup. This is the `new` argument
+#  passed to the standard library method `webbrowser.open`. The behaviour is not
+#  guaranteed, but depends on browser support. Valid values are:
+#
+#   - 2 opens a new tab,
+#   - 1 opens a new window,
+#   - 0 opens in an existing window.
+#
+#  See the `webbrowser.open` documentation for details.
+#c.NotebookApp.webbrowser_open_new = 2
+
+# Set the tornado compression options for websocket connections.
+#
+#  This value will be returned from
+#  :meth:`WebSocketHandler.get_compression_options`. None (default) will disable
+#  compression. A dict (even an empty one) will enable compression.
+#
+#  See the tornado docs for WebSocketHandler.get_compression_options for details.
+#c.NotebookApp.websocket_compression_options = None
+
+# The base URL for websockets, if it differs from the HTTP server (hint: it
+#  almost certainly doesn't).
+#
+#  Should be in the form of an HTTP origin: ws[s]://hostname[:port]
+#c.NotebookApp.websocket_url = ''
+
+# ------------------------------------------------------------------------------
+# ConnectionFileMixin(LoggingConfigurable) configuration
+# ------------------------------------------------------------------------------
+
+# Mixin for configurable classes that work with connection files
+
+# JSON file in which to store connection info [default: kernel-<pid>.json]
+#
+#  This file will contain the IP, ports, and authentication key needed to connect
+#  clients to this kernel. By default, this file will be created in the security
+#  dir of the current profile, but can be specified by absolute path.
+#c.ConnectionFileMixin.connection_file = ''
+
+# set the control (ROUTER) port [default: random]
+#c.ConnectionFileMixin.control_port = 0
+
+# set the heartbeat port [default: random]
+#c.ConnectionFileMixin.hb_port = 0
+
+# set the iopub (PUB) port [default: random]
+#c.ConnectionFileMixin.iopub_port = 0
+
+# Set the kernel's IP address [default localhost]. If the IP address is
+#  something other than localhost, then Consoles on other machines will be able
+#  to connect to the Kernel, so be careful!
+#c.ConnectionFileMixin.ip = ''
+
+# set the shell (ROUTER) port [default: random]
+#c.ConnectionFileMixin.shell_port = 0
+
+# set the stdin (ROUTER) port [default: random]
+#c.ConnectionFileMixin.stdin_port = 0
+
+##
+#c.ConnectionFileMixin.transport = 'tcp'
+
+# ------------------------------------------------------------------------------
+# KernelManager(ConnectionFileMixin) configuration
+# ------------------------------------------------------------------------------
+
+# Manages a single kernel in a subprocess on this host.
+#
+#  This version starts kernels with Popen.
+
+# Should we autorestart the kernel if it dies.
+#c.KernelManager.autorestart = True
+
+# DEPRECATED: Use kernel_name instead.
+#
+#  The Popen Command to launch the kernel. Override this if you have a custom
+#  kernel. If kernel_cmd is specified in a configuration file, Jupyter does not
+#  pass any arguments to the kernel, because it cannot make any assumptions about
+#  the arguments that the kernel understands. In particular, this means that the
+#  kernel does not receive the option --debug if it given on the Jupyter command
+#  line.
+#c.KernelManager.kernel_cmd = []
+
+# Time to wait for a kernel to terminate before killing it, in seconds.
+#c.KernelManager.shutdown_wait_time = 5.0
+
+# ------------------------------------------------------------------------------
+# Session(Configurable) configuration
+# ------------------------------------------------------------------------------
+
+# Object for handling serialization and sending of messages.
+#
+#  The Session object handles building messages and sending them with ZMQ sockets
+#  or ZMQStream objects.  Objects can communicate with each other over the
+#  network via Session objects, and only need to work with the dict-based IPython
+#  message spec. The Session will handle serialization/deserialization, security,
+#  and metadata.
+#
+#  Sessions support configurable serialization via packer/unpacker traits, and
+#  signing with HMAC digests via the key/keyfile traits.
+#
+#  Parameters ----------
+#
+#  debug : bool
+#      whether to trigger extra debugging statements
+#  packer/unpacker : str : 'json', 'pickle' or import_string
+#      importstrings for methods to serialize message parts.  If just
+#      'json' or 'pickle', predefined JSON and pickle packers will be used.
+#      Otherwise, the entire importstring must be used.
+#
+#      The functions must accept at least valid JSON input, and output *bytes*.
+#
+#      For example, to use msgpack:
+#      packer = 'msgpack.packb', unpacker='msgpack.unpackb'
+#  pack/unpack : callables
+#      You can also set the pack/unpack callables for serialization directly.
+#  session : bytes
+#      the ID of this Session object.  The default is to generate a new UUID.
+#  username : unicode
+#      username added to message headers.  The default is to ask the OS.
+#  key : bytes
+#      The key used to initialize an HMAC signature.  If unset, messages
+#      will not be signed or checked.
+#  keyfile : filepath
+#      The file containing a key.  If this is set, `key` will be initialized
+#      to the contents of the file.
+
+# Threshold (in bytes) beyond which an object's buffer should be extracted to
+#  avoid pickling.
+#c.Session.buffer_threshold = 1024
+
+# Whether to check PID to protect against calls after fork.
+#
+#  This check can be disabled if fork-safety is handled elsewhere.
+#c.Session.check_pid = True
+
+# Threshold (in bytes) beyond which a buffer should be sent without copying.
+#c.Session.copy_threshold = 65536
+
+# Debug output in the Session
+#c.Session.debug = False
+
+# The maximum number of digests to remember.
+#
+#  The digest history will be culled when it exceeds this value.
+#c.Session.digest_history_size = 65536
+
+# The maximum number of items for a container to be introspected for custom
+#  serialization. Containers larger than this are pickled outright.
+#c.Session.item_threshold = 64
+
+# execution key, for signing messages.
+#c.Session.key = b''
+
+# path to file containing execution key.
+#c.Session.keyfile = ''
+
+# Metadata dictionary, which serves as the default top-level metadata dict for
+#  each message.
+#c.Session.metadata = {}
+
+# The name of the packer for serializing messages. Should be one of 'json',
+#  'pickle', or an import name for a custom callable serializer.
+#c.Session.packer = 'json'
+
+# The UUID identifying this session.
+#c.Session.session = ''
+
+# The digest scheme used to construct the message signatures. Must have the form
+#  'hmac-HASH'.
+#c.Session.signature_scheme = 'hmac-sha256'
+
+# The name of the unpacker for unserializing messages. Only used with custom
+#  functions for `packer`.
+#c.Session.unpacker = 'json'
+
+# Username for the Session. Default is your system username.
+#c.Session.username = 'vagrant'
+
+# ------------------------------------------------------------------------------
+# MultiKernelManager(LoggingConfigurable) configuration
+# ------------------------------------------------------------------------------
+
+# A class for managing multiple kernels.
+
+# The name of the default kernel to start
+#c.MultiKernelManager.default_kernel_name = 'python3'
+
+# The kernel manager class.  This is configurable to allow subclassing of the
+#  KernelManager for customized behavior.
+#c.MultiKernelManager.kernel_manager_class = 'jupyter_client.ioloop.IOLoopKernelManager'
+
+# ------------------------------------------------------------------------------
+# MappingKernelManager(MultiKernelManager) configuration
+# ------------------------------------------------------------------------------
+
+# A KernelManager that handles notebook mapping and HTTP error handling
+
+# Whether messages from kernels whose frontends have disconnected should be
+#  buffered in-memory.
+#
+#  When True (default), messages are buffered and replayed on reconnect, avoiding
+#  lost messages due to interrupted connectivity.
+#
+#  Disable if long-running kernels will produce too much output while no
+#  frontends are connected.
+#c.MappingKernelManager.buffer_offline_messages = True
+
+# Whether to consider culling kernels which are busy. Only effective if
+#  cull_idle_timeout > 0.
+#c.MappingKernelManager.cull_busy = False
+
+# Whether to consider culling kernels which have one or more connections. Only
+#  effective if cull_idle_timeout > 0.
+#c.MappingKernelManager.cull_connected = False
+
+# Timeout (in seconds) after which a kernel is considered idle and ready to be
+#  culled. Values of 0 or lower disable culling. Very short timeouts may result
+#  in kernels being culled for users with poor network connections.
+#c.MappingKernelManager.cull_idle_timeout = 0
+
+# The interval (in seconds) on which to check for idle kernels exceeding the
+#  cull timeout value.
+#c.MappingKernelManager.cull_interval = 300
+
+# Timeout for giving up on a kernel (in seconds).
+#
+#  On starting and restarting kernels, we check whether the kernel is running and
+#  responsive by sending kernel_info_requests. This sets the timeout in seconds
+#  for how long the kernel can take before being presumed dead.  This affects the
+#  MappingKernelManager (which handles kernel restarts)  and the
+#  ZMQChannelsHandler (which handles the startup).
+#c.MappingKernelManager.kernel_info_timeout = 60
+
+##
+#c.MappingKernelManager.root_dir = ''
+
+# ------------------------------------------------------------------------------
+# ContentsManager(LoggingConfigurable) configuration
+# ------------------------------------------------------------------------------
+
+# Base class for serving files and directories.
+#
+#  This serves any text or binary file, as well as directories, with special
+#  handling for JSON notebook documents.
+#
+#  Most APIs take a path argument, which is always an API-style unicode path, and
+#  always refers to a directory.
+#
+#  - unicode, not url-escaped
+#  - '/'-separated
+#  - leading and trailing '/' will be stripped
+#  - if unspecified, path defaults to '',
+#    indicating the root path.
+
+# Allow access to hidden files
+#c.ContentsManager.allow_hidden = False
+
+##
+#c.ContentsManager.checkpoints = None
+
+##
+#c.ContentsManager.checkpoints_class = 'notebook.services.contents.checkpoints.Checkpoints'
+
+##
+#c.ContentsManager.checkpoints_kwargs = {}
+
+# handler class to use when serving raw file requests.
+#
+#  Default is a fallback that talks to the ContentsManager API, which may be
+#  inefficient, especially for large files.
+#
+#  Local files-based ContentsManagers can use a StaticFileHandler subclass, which
+#  will be much more efficient.
+#
+#  Access to these files should be Authenticated.
+#c.ContentsManager.files_handler_class = 'notebook.files.handlers.FilesHandler'
+
+# Extra parameters to pass to files_handler_class.
+#
+#  For example, StaticFileHandlers generally expect a `path` argument specifying
+#  the root directory from which to serve files.
+#c.ContentsManager.files_handler_params = {}
+
+# Glob patterns to hide in file and directory listings.
+#c.ContentsManager.hide_globs = ['__pycache__', '*.pyc', '*.pyo', '.DS_Store', '*.so', '*.dylib', '*~']
+
+# Python callable or importstring thereof
+#
+#  To be called on a contents model prior to save.
+#
+#  This can be used to process the structure, such as removing notebook outputs
+#  or other side effects that should not be saved.
+#
+#  It will be called as (all arguments passed by keyword)::
+#
+#      hook(path=path, model=model, contents_manager=self)
+#
+#  - model: the model to be saved. Includes file contents.
+#    Modifying this dict will affect the file that is stored.
+#  - path: the API path of the save destination
+#  - contents_manager: this ContentsManager instance
+#c.ContentsManager.pre_save_hook = None
+
+##
+#c.ContentsManager.root_dir = '/'
+
+# The base name used when creating untitled directories.
+#c.ContentsManager.untitled_directory = 'Untitled Folder'
+
+# The base name used when creating untitled files.
+#c.ContentsManager.untitled_file = 'untitled'
+
+# The base name used when creating untitled notebooks.
+#c.ContentsManager.untitled_notebook = 'Untitled'
+
+# ------------------------------------------------------------------------------
+# FileManagerMixin(Configurable) configuration
+# ------------------------------------------------------------------------------
+
+# Mixin for ContentsAPI classes that interact with the filesystem.
+#
+#  Provides facilities for reading, writing, and copying both notebooks and
+#  generic files.
+#
+#  Shared by FileContentsManager and FileCheckpoints.
+#
+#  Note ---- Classes using this mixin must provide the following attributes:
+#
+#  root_dir : unicode
+#      A directory against against which API-style paths are to be resolved.
+#
+#  log : logging.Logger
+
+# By default notebooks are saved on disk on a temporary file and then if
+#  succefully written, it replaces the old ones. This procedure, namely
+#  'atomic_writing', causes some bugs on file system whitout operation order
+#  enforcement (like some networked fs). If set to False, the new notebook is
+#  written directly on the old one which could fail (eg: full filesystem or quota
+#  )
+#c.FileManagerMixin.use_atomic_writing = True
+
+# ------------------------------------------------------------------------------
+# FileContentsManager(FileManagerMixin,ContentsManager) configuration
+# ------------------------------------------------------------------------------
+
+# If True (default), deleting files will send them to the platform's
+#  trash/recycle bin, where they can be recovered. If False, deleting files
+#  really deletes them.
+#c.FileContentsManager.delete_to_trash = True
+
+# Python callable or importstring thereof
+#
+#  to be called on the path of a file just saved.
+#
+#  This can be used to process the file on disk, such as converting the notebook
+#  to a script or HTML via nbconvert.
+#
+#  It will be called as (all arguments passed by keyword)::
+#
+#      hook(os_path=os_path, model=model, contents_manager=instance)
+#
+#  - path: the filesystem path to the file just written - model: the model
+#  representing the file - contents_manager: this ContentsManager instance
+#c.FileContentsManager.post_save_hook = None
+
+##
+#c.FileContentsManager.root_dir = ''
+
+# DEPRECATED, use post_save_hook. Will be removed in Notebook 5.0
+#c.FileContentsManager.save_script = False
+
+# ------------------------------------------------------------------------------
+# NotebookNotary(LoggingConfigurable) configuration
+# ------------------------------------------------------------------------------
+
+# A class for computing and verifying notebook signatures.
+
+# The hashing algorithm used to sign notebooks.
+#c.NotebookNotary.algorithm = 'sha256'
+
+# The sqlite file in which to store notebook signatures. By default, this will
+#  be in your Jupyter data directory. You can set it to ':memory:' to disable
+#  sqlite writing to the filesystem.
+#c.NotebookNotary.db_file = ''
+
+# The secret key with which notebooks are signed.
+#c.NotebookNotary.secret = b''
+
+# The file where the secret key is stored.
+#c.NotebookNotary.secret_file = ''
+
+# A callable returning the storage backend for notebook signatures. The default
+#  uses an SQLite database.
+#c.NotebookNotary.store_factory = traitlets.Undefined
+
+# ------------------------------------------------------------------------------
+# KernelSpecManager(LoggingConfigurable) configuration
+# ------------------------------------------------------------------------------
+
+# If there is no Python kernelspec registered and the IPython kernel is
+#  available, ensure it is added to the spec list.
+#c.KernelSpecManager.ensure_native_kernel = True
+
+# The kernel spec class.  This is configurable to allow subclassing of the
+#  KernelSpecManager for customized behavior.
+#c.KernelSpecManager.kernel_spec_class = 'jupyter_client.kernelspec.KernelSpec'
+
+# Whitelist of allowed kernel names.
+#
+#  By default, all installed kernels are allowed.
+#c.KernelSpecManager.whitelist = set()
diff --git a/vagrant/files/pg_hba.conf b/vagrant/files/pg_hba.conf
new file mode 100644
index 0000000000000000000000000000000000000000..5383ee2765aad917cb2c909e743f2a9cf8b335e3
--- /dev/null
+++ b/vagrant/files/pg_hba.conf
@@ -0,0 +1,99 @@
+# PostgreSQL Client Authentication Configuration File
+# ===================================================
+#
+# Refer to the "Client Authentication" section in the PostgreSQL
+# documentation for a complete description of this file.  A short
+# synopsis follows.
+#
+# This file controls: which hosts are allowed to connect, how clients
+# are authenticated, which PostgreSQL user names they can use, which
+# databases they can access.  Records take one of these forms:
+#
+# local      DATABASE  USER  METHOD  [OPTIONS]
+# host       DATABASE  USER  ADDRESS  METHOD  [OPTIONS]
+# hostssl    DATABASE  USER  ADDRESS  METHOD  [OPTIONS]
+# hostnossl  DATABASE  USER  ADDRESS  METHOD  [OPTIONS]
+#
+# (The uppercase items must be replaced by actual values.)
+#
+# The first field is the connection type: "local" is a Unix-domain
+# socket, "host" is either a plain or SSL-encrypted TCP/IP socket,
+# "hostssl" is an SSL-encrypted TCP/IP socket, and "hostnossl" is a
+# plain TCP/IP socket.
+#
+# DATABASE can be "all", "sameuser", "samerole", "replication", a
+# database name, or a comma-separated list thereof. The "all"
+# keyword does not match "replication". Access to replication
+# must be enabled in a separate record (see example below).
+#
+# USER can be "all", a user name, a group name prefixed with "+", or a
+# comma-separated list thereof.  In both the DATABASE and USER fields
+# you can also write a file name prefixed with "@" to include names
+# from a separate file.
+#
+# ADDRESS specifies the set of hosts the record matches.  It can be a
+# host name, or it is made up of an IP address and a CIDR mask that is
+# an integer (between 0 and 32 (IPv4) or 128 (IPv6) inclusive) that
+# specifies the number of significant bits in the mask.  A host name
+# that starts with a dot (.) matches a suffix of the actual host name.
+# Alternatively, you can write an IP address and netmask in separate
+# columns to specify the set of hosts.  Instead of a CIDR-address, you
+# can write "samehost" to match any of the server's own IP addresses,
+# or "samenet" to match any address in any subnet that the server is
+# directly connected to.
+#
+# METHOD can be "trust", "reject", "md5", "password", "scram-sha-256",
+# "gss", "sspi", "ident", "peer", "pam", "ldap", "radius" or "cert".
+# Note that "password" sends passwords in clear text; "md5" or
+# "scram-sha-256" are preferred since they send encrypted passwords.
+#
+# OPTIONS are a set of options for the authentication in the format
+# NAME=VALUE.  The available options depend on the different
+# authentication methods -- refer to the "Client Authentication"
+# section in the documentation for a list of which options are
+# available for which authentication methods.
+#
+# Database and user names containing spaces, commas, quotes and other
+# special characters must be quoted.  Quoting one of the keywords
+# "all", "sameuser", "samerole" or "replication" makes the name lose
+# its special character, and just match a database or username with
+# that name.
+#
+# This file is read on server startup and when the server receives a
+# SIGHUP signal.  If you edit the file on a running system, you have to
+# SIGHUP the server for the changes to take effect, run "pg_ctl reload",
+# or execute "SELECT pg_reload_conf()".
+#
+# Put your actual configuration here
+# ----------------------------------
+#
+# If you want to allow non-local connections, you need to add more
+# "host" records.  In that case you will also need to make PostgreSQL
+# listen on a non-local interface via the listen_addresses
+# configuration parameter, or via the -i or -h command line switches.
+
+
+
+
+# DO NOT DISABLE!
+# If you change this first entry you will need to make sure that the
+# database superuser can access the database using some other method.
+# Noninteractive access to all databases is required during automatic
+# maintenance (custom daily cronjobs, replication, and similar tasks).
+#
+# Database administrative login by Unix domain socket
+#local   all             postgres                                peer
+
+# TYPE  DATABASE        USER            ADDRESS                 METHOD
+
+# "local" is for Unix domain socket connections only
+local   all             all                                     trust
+# IPv4 local connections:
+host    all             all             127.0.0.1/32            trust
+# IPv6 local connections:
+host    all             all             ::1/128                 md5
+# Allow replication connections from localhost, by a user with the
+# replication privilege.
+#local   replication     all                                     peer
+#host    replication     all             127.0.0.1/32            md5
+#host    replication     all             ::1/128                 md5
\ No newline at end of file
diff --git a/vagrant/files/setup-postgres.sh b/vagrant/files/setup-postgres.sh
new file mode 100755
index 0000000000000000000000000000000000000000..5e9f9f54c71da14349f5f60cbdca240d55f6b2fa
--- /dev/null
+++ b/vagrant/files/setup-postgres.sh
@@ -0,0 +1,3 @@
+echo " --- setup-postgres.sh ---"
+
+createuser vagrant
diff --git a/vagrant/files/setup-vagrant.sh b/vagrant/files/setup-vagrant.sh
new file mode 100755
index 0000000000000000000000000000000000000000..0d0ff35f4ba280a128d5223bc8d2ac448553ad0b
--- /dev/null
+++ b/vagrant/files/setup-vagrant.sh
@@ -0,0 +1,35 @@
+echo " --- setup-vagrant.sh ---"
+
+rm -rf ~/jupyter-openbis-extension/venv
+rm -rf ~/jupyter-openbis-extension/jupyter_openbis_extension.egg-info
+rm -rf ~/jupyter-openbis-extension/.ipynb_checkpoints
+
+build="http://stage-jenkins.ethz.ch:8090/job/installation-18.06/lastSuccessfulBuild"
+path=$(curl -s "$build/api/xml?xpath=//relativePath"|sed -e "s/<relativePath>//"|sed -e "s/<\/relativePath>//")
+wget -q $build/artifact/$path
+archive=$(basename $path)
+tar xvfz $archive
+directory=$(echo "$archive" | cut -f 1 -d '.')
+cp /files/console.properties $directory
+export ADMIN_PASSWORD='password'
+$directory/run-console.sh
+
+sed -i "/jetty.ssl.port=/ s/=.*/=8122/" /home/vagrant/openbis/servers/openBIS-server/jetty/start.d/ssl.ini
+sed -i "/host-address =/ s/=.*/= https:\/\/localhost/" /home/vagrant/openbis/servers/datastore_server/etc/service.properties
+sed -i "/port =/ s/=.*/= 8123/" /home/vagrant/openbis/servers/datastore_server/etc/service.properties
+sed -i "/server-url =/ s/=.*/= \${host-address}:8122/" /home/vagrant/openbis/servers/datastore_server/etc/service.properties
+
+rm -rf ~/openBIS-installation-*
+
+mkdir ~/.jupyter
+cp /files/jupyter_notebook_config.py ~/.jupyter
+
+cd jupyter-openbis-extension
+pip3 install -e .
+export PATH=$PATH:/home/vagrant/.local/bin
+pip3 install --upgrade pybis
+pip3 install 'tornado==5.1.1' --force-reinstall 
+jupyter serverextension enable --py jupyter-openbis-extension
+jupyter nbextension install --py jupyter-openbis-extension --user --symlink
+jupyter nbextension enable jupyter-openbis-extension --user --py
+cd
diff --git a/vagrant/files/start-services-vagrant.sh b/vagrant/files/start-services-vagrant.sh
new file mode 100755
index 0000000000000000000000000000000000000000..9bcd30ff0584281f51a4c098b51a0664a0da9c39
--- /dev/null
+++ b/vagrant/files/start-services-vagrant.sh
@@ -0,0 +1,8 @@
+echo " --- start-services-vagrant.sh ---"
+/home/vagrant/openbis/bin/allup.sh
+
+screen -S dev -t jupyter -Adm bash -c "
+  cd ~/jupyter-openbis-extension;
+  jupyter notebook;
+  bash;
+"
diff --git a/vagrant/files/start-services.sh b/vagrant/files/start-services.sh
new file mode 100755
index 0000000000000000000000000000000000000000..4fd56076ced3de09010ec899d8129d1b1b503621
--- /dev/null
+++ b/vagrant/files/start-services.sh
@@ -0,0 +1,2 @@
+echo " --- start-services.sh ---"
+runuser -l vagrant bash -c /files/start-services-vagrant.sh
diff --git a/vagrant/shared/.gitignore b/vagrant/shared/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..c96a04f008ee21e260b28f7701595ed59e2839e3
--- /dev/null
+++ b/vagrant/shared/.gitignore
@@ -0,0 +1,2 @@
+*
+!.gitignore
\ No newline at end of file