From d5091b8c6b0fd03d8e9ef82d2ce176eb8b967e85 Mon Sep 17 00:00:00 2001
From: Steffen Vogel <post@steffenvogel.de>
Date: Wed, 7 Oct 2020 15:34:38 +0200
Subject: [PATCH] add link-creation wizard

---
 script.js          | 105 +++++++++++++++++++++++++++++++++++++++++++--
 src/usage/Links.md |  72 ++++++++++++++++++++-----------
 style.css          |  23 ++++++++++
 3 files changed, 172 insertions(+), 28 deletions(-)

diff --git a/script.js b/script.js
index 6389939..1d12edd 100644
--- a/script.js
+++ b/script.js
@@ -1,13 +1,107 @@
-const apiBase = 'https://profiles.jupyter.rwth-aachen.de/api/v1';
+const serviceUrl = 'https://jupyter.rwth-aachen.de/services';
+const spawnBaseUrl = "https://jupyter.rwth-aachen.de/hub/spawn";
+const badgeURL = "https://jupyter.pages.rwth-aachen.de/documentation/images/badge-launch-rwth-jupyter.svg";
+
+// taken from https://stackoverflow.com/questions/6234773/can-i-escape-html-special-chars-in-javascript
+function escapeHtml(unsafe) {
+  return unsafe
+    .replace(/&/g, "&amp;")
+    .replace(/</g, "&lt;")
+    .replace(/>/g, "&gt;")
+    .replace(/"/g, "&quot;")
+    .replace(/'/g, "&#039;");
+}
+
+function getProfiles() {
+    window.fetch(serviceUrl + '/profile/all', { mode: 'no-cors' })
+        .then((response) => response.json())
+        .then((json) => {
+            this.console.log(json);
+            for (const i in json.jobs) {
+                let job = json.jobs[i];
+                elm = this.document.getElementById('build-' + job.name);
+                
+                if (elm) {
+                    elm.classList.add(job.status);
+                    elm.innerText = job.status;
+                    elm.insertAdjacentHTML('afterend', '<span class="build-log">(open <a href="' + job.web_url + '">build log</a> #' + job.id + ' from ' + new Date(job.created_at).toLocaleString() + ')</span>');
+                }
+            }
+        });
+
+    return [
+        {
+            slug: "pti",
+            display_name: "[PTI] Praktikum Technische Informatik"
+        },
+        {
+            slug: "gdet3",
+            display_name: "[GDET3] Grundgebiete der Elektrotechnik 3"
+        }
+    ]
+}
+
+function updateLink(event) {
+    var profileSlug = document.getElementById("link-wizard-profile");
+    var filePath = document.getElementById("link-wizard-path");
+    var outputDiv = document.getElementById("link-wizard-output");
+    var directLink = document.getElementById("link-wizard-direct-link");
+    var badgePre_md = document.getElementById("link-wizard-badge-md");
+    var badgePreHtml = document.getElementById("link-wizard-badge-html");
+
+    // Strip leading slash
+    var path = filePath.value.replace(/^\//, '');
+    var slug = profileSlug.value;
+
+    if (profileSlug.length > 0) {
+        var link = updateDirectLink(slug, path);
+
+        // Link
+        directLink.innerHTML = link;
+
+        // Badges
+        var badge_md = '[![](' + badgeURL + ')](' + link +')';
+        var badgeHtml = '<a href="' + link + '"><img src="' + badgeURL + '" /></a>';
+
+        badgePre_md.innerHTML = badge_md;
+        badgePreHtml.innerHTML = escapeHtml(badgeHtml);
+
+        outputDiv.style.display = 'block';
+    }
+}
+
+function generateProfileDropdown(profilesSelect) {
+    // Fill profiles select
+    var profiles = getProfiles();
+
+    for(var i = 0; i < profiles.length; i++) {
+        var o = document.createElement("option");
+
+        o.value = profiles[i].slug;
+        o.textContent = profiles[i].display_name;
+
+        profilesSelect.appendChild(o);
+    }
+}
+
+function updateDirectLink(profileSlug, path) {
+    var url = spawnBaseUrl + '?profile=' + profileSlug;
+
+    if (path && path.length > 0)
+        url = url + '&next=/user-redirect/lab/tree/' + encodeURIComponent(path);
+
+    return url;
+}
 
 function loadBuildStatus() {
-    window.fetch(apiBase + '/profiles')
+    window.fetch(serviceUrl + '/profile/all')
         .then((response) => response.json())
         .then((json) => {
             this.console.log(json);
             for (const i in json.jobs) {
                 let job = json.jobs[i];
                 elm = this.document.getElementById('build-' + job.name);
+                
                 if (elm) {
                     elm.classList.add(job.status);
                     elm.innerText = job.status;
@@ -54,7 +148,7 @@ function insertVersionCollapsibles() {
         coll.insertAdjacentElement('beforebegin', btn)
 
         btn.addEventListener('click', () => {
-            window.fetch(apiBase + '/versions/' + slug + '/' + repo)
+            window.fetch(apiBaseUrl + '/versions/' + slug + '/' + repo)
                 .then((response) => response.json())
                 .then((pkgs) => {
                     tbl = document.createElement('table');
@@ -114,4 +208,9 @@ window.addEventListener('load', function () {
         loadBuildStatus();
         insertVersionCollapsibles();
     }
+
+    if (window.location.pathname.endsWith('/Links.html')) {
+        var profileSelect = document.getElementById("link-wizard-profile");
+        generateProfileDropdown(profileSelect);
+    }
 });
diff --git a/src/usage/Links.md b/src/usage/Links.md
index 2dabd68..04aa980 100644
--- a/src/usage/Links.md
+++ b/src/usage/Links.md
@@ -2,40 +2,67 @@
 
 This page shows a few examples of permanent links for accessing certain profiles or Notebooks in the Jupyter cluster.
 
-## Individual link (contains username)
+## Link creation wizard
+
+You can use the following wizard to generate links and badges which open specific notebooks and profiles.
+
+<html>
+  <div class="link-wizard">
+    <form>
+        <fieldset>
+            <label for="link-wizard-profile">Jupyter Profile</label><br>
+            <select onchange="updateLink(event)" id="link-wizard-profile">
+                <option selected="selected" value="">Select profile</option>
+            </select>
+        </fieldset>
+        <fieldset>
+            <label for="link-wizard-path">Path to a notebook file (optional)</label><br>
+            <input oninput="updateLink(event)" onkeydown="event.stopPropagation()" type="text" id="link-wizard-path" placeholder="Index.ipynb">
+        </fieldset>
+    </form>
+    <div id="link-wizard-output">
+        <h3>Link</h3>
+        <pre id="link-wizard-direct-link"></pre>
+        <h3>Badge</h3>
+        <img src="https://jupyter.pages.rwth-aachen.de/documentation/images/badge-launch-rwth-jupyter.svg" alt="RWTHjupyter badge">
+        <h5>Markdown Snippet</h5>
+        <pre id="link-wizard-badge-md"></pre>
+        <h5>HTML Snippet</h5>
+        <pre id="link-wizard-badge-html"></pre>
+    </div>
+  </div>
+</html>
+
+## Spawn and open Notebook
+
+Opens a specific notebook using the default profile (Python) or your running Jupyter server.
 
-Opens a specific notebook for a specific user using the default profile (Python).
-This type link is personalized due to the included username and can therefore not be shared with other users.
-
-If there is no running server existing for the user, a new server is started.
-
-**Schema:** `https://jupyter.rwth-aachen.de/user/{{ user_name }}/lab/tree/{{ notebook_path }}`  
-**Example:** https://jupyter.rwth-aachen.de/user/vzi3jsam/lab/tree/gdet3/GDET3%20Faltung%20GUI.ipynb
+**Schema:** `https://jupyter.rwth-aachen.de/user-redirect/lab/tree/{{ notebook_path }}`  
+**Example:** https://jupyter.rwth-aachen.de/user-redirect/lab/tree/gdet3/GDET3%20Faltung%20GUI.ipynb
 
 #### Arguments
 
-- `user_name` your Jupyter username which is a pseudo-random 8 digit alpha numeric identfier displayed after login in the top right cormer.
-- `notebook_path` and URL encoded path to the Notebook which should be opened.
-
-## Generic link (user-redirect)
+- `notebook_path` and URL encoded path to the Notebook which should be opened. Please not that this path must be [URL encoded](https://www.w3schools.com/tags/ref_urlencode.ASP).
 
-This is a generic version of the previous link which can be shared with other users.
-
-**Schema:** `https://jupyter.rwth-aachen.de/user-redirect/lab/tree/{{ notebook_path }}`  
-**Example:** https://jupyter.rwth-aachen.de/user-redirect/lab/tree/gdet3/GDET3%20Faltung%20GUI.ipynb
-
-## Spawn specific profile
+## Spawn with a specific profile
 
 This link to spawn a specific profile.
 
 **Note:** If a server is already running with a different profile, no action will be taken.
 
+**Schema:** `https://jupyter.rwth-aachen.de/hub/spawn?profile={{ profile_slug }}`  
+**Example:** https://jupyter.rwth-aachen.de/hub/spawn?profile=gdet3
+
 #### Arguments
 
 - `profile_slug` a short alpha numeric identifier for the profile. Please take a look at the [profile list](Profiles.md) for available profiles.
 
-**Schema:** `https://jupyter.rwth-aachen.de/hub/spawn?profile={{ profile_slug }}`  
-**Example:** https://jupyter.rwth-aachen.de/hub/spawn?profile=gdet3
+## Spawn with a specific profile and open a notebook
+
+This version combines the first two types of links:
+
+**Schema:** `https://jupyter.rwth-aachen.de/hub/spawn?profile={{ profile_slug }}&next=/user-redirect/lab/tree/{{ notebook_path }}`  
+**Example:** https://jupyter.rwth-aachen.de/hub/spawn?profile=gdet3&next=/user-redirect/lab/tree/gdet3/GDET3%20Faltung%20GUI.ipynb
 
 ### Specify username and server name
 
@@ -51,11 +78,6 @@ A normal user can start up to 10 named servers. E.g. for using several profiles
 
 - `server_name`  is a alpha numeric identifier for the named server
 
-## Spawn specific profile and open a notebook
-
-**Schema:** `https://jupyter.rwth-aachen.de/hub/spawn?profile={{ profile_slug }}&next=/user-redirect/lab/tree/{{ notebook_path }}`  
-**Example:** https://jupyter.rwth-aachen.de/hub/spawn?profile=gdet3&next=/user-redirect/lab/tree/gdet3/GDET3%20Faltung%20GUI.ipynb
-
 ## Badge
 
 You can use the following Markdown or HTML snippets to embed a badge into your `README.md` files or Moodle activities for launching a Notebook:
diff --git a/style.css b/style.css
index 821f94b..c3dd432 100644
--- a/style.css
+++ b/style.css
@@ -72,3 +72,26 @@ span.manual {
 table.versions {
     margin: 5px;
 }
+
+
+/* Link Wizard */
+
+.link-wizard form {
+	border: 1px grey solid;
+}
+
+.link-wizard h3 {
+	margin-top: 0.5em;
+}
+
+.link-wizard fieldset {
+	border: 0;
+}
+
+.link-wizard #link-wizard-path {
+	width: 100%;
+}
+
+.link-wizard #link-wizard-output {
+	display: none;
+}
-- 
GitLab