Commit 45d87016 authored by Cédric Le Ninivin's avatar Cédric Le Ninivin

Applications: Add the possibility to deploy apps on another CribJS

* By default Apps are now deployed to a Crib with a different domain
* App JSON has been updated to have necessary parameters for this
* App list redirect to url list, editor, and export pages with the
crib_enable parameter of the applications to have these pages work
 with the crib of the application
* URL List, Editor and Export pages set crib enable url when the
parameter is provided
parent ed667023
......@@ -36,3 +36,24 @@ iframe {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
details {
border: 1px solid #aaa;
border-radius: 4px;
padding: .5em .5em 0;
}
summary {
font-weight: bold;
margin: -.5em -.5em 0;
padding: .5em;
}
details[open] {
padding: .5em;
}
details[open] summary {
border-bottom: 1px solid #aaa;
margin-bottom: .5em;
}
\ No newline at end of file
......@@ -30,12 +30,12 @@
</label>
</div>
<div class="form-group">
<label>Application Path:
<input name="load-zip-to-path"
class="load-zip-to-path form-control" type="text"
<label>Application ID:
<input name="load-zip-to-subdomain"
class="load-zip-to-subdomain form-control" type="text"
size="30" value="" pattern="[a-zA-Z0-9]+"
title="Alphanumeric Characters" required
placeholder="my-app-path/">
placeholder="myappid">
</label>
</div>
<div class="form-group">
......@@ -45,25 +45,50 @@
<input name="load-zip-url" class="load-zip-url form-control" type="text" size="60"
value="apps/hello-world-template.zip"></label>
</div>
<div class="form-group">
<label> Path in Zip:
<input name="load-zip-path"
class="load-from-zip-path form-control" type="text"
size="50" value=""
placeholder="Use if index.html is not at the root of the zip. e.g.: cribjs-editor-master/"
title="Use if index.html is not at the root of the zip. e.g.: cribjs-editor-master/">
</label>
</div>
<div class="form-group">
<label> And redirect to:
<input name="redirect-url" class="redirect-url form-control"
type="text" size="30" value="index.html"
title="Will be used as home page">
</label>
</div>
<details>
<summary>Advanced Parameters</summary>
<div class="form-group">
<label> Path in Zip:
<input name="load-zip-path"
class="load-from-zip-path form-control" type="text"
size="50" value=""
placeholder="Use if you want to only select part of the Zip"
title="Use if you want to only select part of the Zip">
</label>
</div>
<div class="form-group">
<label> Defaut home page for the App:
<input name="redirect-url" class="redirect-url form-control"
type="text" size="30" value="index.html"
title="Will be used as home page">
</label>
</div>
<div class="form-group">
<label>Crib URL to Deploy to:
<input name="load-zip-to-crib-url"
class="load-zip-to-crib-url form-control" type="text"
size="55" value=""
placeholder="https://${myappid}.cribjs.nexedi.net/crib-enable.html">
</label>
</div>
<div class="form-group">
<label>Path to Deploy to:
<input name="load-zip-to-path"
class="load-zip-to-path form-control" type="text"
size="30" value="" pattern="[a-zA-Z0-9]+"
title="Alphanumeric Characters"
placeholder="myapppath">
</label>
</div>
</details>
<div class="form-group">
<button name="load-zip-contents" class="load-zip-contents btn btn-primary" type="submit">Import Application</button>
</div>
<div class="crib_enable_validator" style="display:none;">
<div data-gadget-url="./crib_enable_validator.html"
data-gadget-scope="crib_enable_validator"
data-gadget-sandbox="public"></div>
</div>
</form>
</div>
......
......@@ -4,19 +4,8 @@
(function (window, rJS, jIO) {
"use strict";
var DEFAULT_APP_LIST_DOC = "app-list.json",
SAMPLE_APP_LIST_DOC = "sample-app-list.json";
function getParameterDict() {
var hash = window.location.hash.substring(1),
params = {};
/*
hash.split('&').map(hk => {
let temp = hk.split('=');
params[temp[0]] = temp[1];
});
*/
return params;
}
SAMPLE_APP_LIST_DOC = "sample-app-list.json",
BASE_CRIB_WILDCARD = ".cribjs.nexedi.net/crib-enable.html";
function log(gadget, str, klass) {
var p = document.createElement('p'),
......@@ -78,12 +67,66 @@
});
}
/*
* This function validate the Crib URL to use, set it if correct and return
* the scope.
*/
function validateAndSetCribEnableGadget(gadget, crib_url, logAddApp) {
return RSVP.Queue()
.push(function () {
logAddApp(gadget, "Testing Crib URL: " + crib_url);
gadget.props.element.querySelector(".crib_enable_validator")
.style = "display:none;";
return gadget.getDeclaredGadget("crib_enable_validator");
})
.push(function (validator_gadget) {
return validator_gadget.validateCribEnableGadgetUrl(crib_url);
})
.push(function (result) {
if (!result) {
gadget.props.element.querySelector(".crib_enable_validator")
.style = "";
logAddApp(gadget, "Could not Validate CribJS Gadget URL: " +
crib_url);
throw "Could not Validate CribJS Gadget URL: " + crib_url;
}
logAddApp(gadget, "Validated Crib URL: " + crib_url);
return gadget.crib_sw_setCribEnableGadgetUrl(crib_url);
})
.push(function () {
return gadget.crib_sw_getScope();
});
}
/*
* This is the Core Function that will "install" the Application
*/
function loadCribJSFromZip(gadget, event) {
var app_list = [],
to_path = gadget.props
.element.querySelector("form.crib-load-from-zip .load-zip-to-path").value + "/",
redirect_to = to_path +
gadget.props.element.querySelector("form.crib-load-from-zip .redirect-url").value;
// Define the list of elements for the app
options = {
name: gadget.props.element.querySelector(".app-name").value,
subdomain: gadget.props.element
.querySelector(".load-zip-to-subdomain").value,
from_path: gadget.props.element
.querySelector("form.crib-load-from-zip .load-from-zip-path").value,
redirect_url: gadget.props.element
.querySelector("form.crib-load-from-zip .redirect-url").value,
crib_url: gadget.props.element
.querySelector("form.crib-load-from-zip .load-zip-to-crib-url").value,
to_path: gadget.props.element
.querySelector("form.crib-load-from-zip .load-zip-to-path").value
};
// If no Crib URL is provided construct one from the App ID
if (!options.crib_url) {
options.crib_url = "https://" + options.subdomain + BASE_CRIB_WILDCARD;
}
// If no Zip is provided no need to continue
if (gadget.props.element
.querySelector("form.crib-load-from-zip .load-zip-file").files.length === 0 &&
gadget.props.element
......@@ -91,8 +134,20 @@
logAddApp(gadget, "Please set a Zip File or Zip Url");
return;
}
// Start the process to add the application
return RSVP.Queue()
.push(function () {
// Check the Crib Enable Gadget and return the Scope
return validateAndSetCribEnableGadget(gadget, options.crib_url, logAddApp);
})
.push(function (scope) {
/*
* Effectively push the App in browser
*/
options.to_path = options.to_path ? options.to_path + "/" : options.to_path;
options.default_page = options.to_path + options.redirect_url;
options.redirect_to = scope + options.default_page;
var promise_list = [];
if (gadget.props.element
.querySelector("form.crib-load-from-zip .load-zip-file").files.length !== 0) {
......@@ -106,6 +161,9 @@
return RSVP.all(promise_list);
})
.push(function (result_list) {
/*
* The app has been pushed, we add it to the app list
*/
app_list = result_list[1];
app_list.splice(
0, 0, {
......@@ -113,19 +171,26 @@
application_image_type: undefined,
application_image_url: undefined,
application_description: undefined,
application_url: redirect_to,
application_path: to_path,
application_edit_page: redirect_to,
application_title: gadget.props.element.querySelector(".app-name").value,
application_url: options.redirect_to,
application_path: options.to_path,
application_edit_page: options.redirect_to,
application_crib: options.crib_url,
application_title: options.name,
application_category: undefined
}
);
logAddApp(gadget, "Adding App to App list");
return putNewAppList(gadget, app_list);
return RSVP.all([
putNewAppList(gadget, app_list),
gadget.setSetting("editor-pattern", options.to_path)
]);
})
.push(function () {
logAddApp(gadget, "Redirecting to Editor");
return gadget.redirect({page: "editor", url: redirect_to});
return gadget.redirect({page: "editor", url: options.default_page});
})
.push(undefined, function (error) {
logAddApp(gadget, error);
});
}
......@@ -192,14 +257,21 @@
//////////////////////////////////////////////
.declareAcquiredMethod("redirect", "redirect")
.declareAcquiredMethod("crib_sw_get", "crib_sw_get")
.declareAcquiredMethod("crib_sw_getScope", "crib_sw_getScope")
.declareAcquiredMethod(
"crib_sw_setCribEnableGadgetUrl",
"crib_sw_setCribEnableGadgetUrl"
)
.allowPublicAcquisition("crib_sw_setCribEnableGadgetUrl", function (argument_list) {
return this.crib_sw_setCribEnableGadgetUrl(argument_list[0]);
})
.declareAcquiredMethod(
"crib_sw_getCribEnableGadgetUrl",
"crib_sw_getCribEnableGadgetUrl"
)
.allowPublicAcquisition("crib_sw_getCribEnableGadgetUrl", function (argument_list) {
return this.crib_sw_getCribEnableGadgetUrl();
})
.allowPublicAcquisition("crib_sw_get", function (argument_list) {
return this.crib_sw_get(argument_list[0]);
})
......@@ -207,22 +279,22 @@
.allowPublicAcquisition("crib_sw_put", function (argument_list) {
return this.crib_sw_put(argument_list[0], argument_list[1]);
})
.declareAcquiredMethod("setSetting", "setSetting")
.declareMethod('render', function (options) {
var gadget = this,
getURL = window.location,
site = getURL.protocol + "//" + getURL.host,
params = getParameterDict();
if (params.hasOwnProperty("from_path")) {
gadget.props.element.querySelector("form.crib-load-from-zip .load-from-zip-path").value = params.from_path;
site = getURL.protocol + "//" + getURL.host;
if (options.hasOwnProperty("from_path")) {
gadget.props.element.querySelector("form.crib-load-from-zip .load-from-zip-path").value = options.from_path;
}
if (params.hasOwnProperty("to_path")) {
gadget.props.element.querySelector("form.crib-load-from-zip .load-zip-to-path").value = params.to_path;
if (options.hasOwnProperty("to_path")) {
gadget.props.element.querySelector("form.crib-load-from-zip .load-zip-to-path").value = options.to_path;
}
if (params.hasOwnProperty("zip_url")) {
gadget.props.element.querySelector("form.crib-load-from-zip .load-zip-url").value = params.zip_url;
if (options.hasOwnProperty("zip_url")) {
gadget.props.element.querySelector("form.crib-load-from-zip .load-zip-url").value = options.zip_url;
}
if (params.hasOwnProperty("redirect_url")) {
gadget.props.element.querySelector("form.crib-load-from-zip .redirect-url").value = params.redirect_url;
if (options.hasOwnProperty("redirect_url")) {
gadget.props.element.querySelector("form.crib-load-from-zip .redirect-url").value = options.redirect_url;
}
return gadget.props.start_deferred.resolve();
})
......
......@@ -5,6 +5,15 @@
var DEFAULT_APP_LIST_DOC = "app-list.json",
SAMPLE_APP_LIST_DOC = "sample-app-list.json";
function isValidUrl(string) {
try {
new URL(string);
} catch (_) {
return false;
}
return true;
}
function displayAppList(gadget) {
var app_list = [];
return gadget.fetch(DEFAULT_APP_LIST_DOC)
......@@ -18,10 +27,12 @@
promise_list.push(RSVP.all([
gadget.getUrlFor({
page: "url_list",
crib_enable_url: app_list[i].application_crib,
pattern: app_list[i].application_path
}),
gadget.getUrlFor({
page: "save_load",
crib_enable_url: app_list[i].application_crib,
zip_download_path: app_list[i].application_path,
zip_name: app_list[i].application_title + ".zip",
start_zip_download: true
......@@ -35,14 +46,12 @@
})
.push(function (url_list) {
var app_list_html = [],
base_url = window.location.origin,
current_path = window.location.pathname,
tmp_url;
for (var i = 0; i < app_list.length; i += 1) {
// XXX Ugly Hack
tmp_url = window.location.pathname + "/" + app_list[i].application_url;
while (tmp_url.indexOf("//") !== -1) {
tmp_url = tmp_url.replace("//", "/");
tmp_url = app_list[i].application_url;
if (!isValidUrl(tmp_url)) {
tmp_url = new URL(tmp_url, window.location.href).href;
}
app_list_html.push(domsugar("div", {class: "col-sm-4 col-md-4"}, [
domsugar("div", {class: "thumbnail"}, [
......@@ -52,7 +61,7 @@
domsugar("a", {href: url_list[i][1], class:"btn btn-default",
role:"button", text:"", title: "Download"}),
domsugar("a", {href: url_list[i][0], class:"btn btn-default", role:"button", text:"Edit"}),
domsugar("a", {href: base_url + tmp_url , class:"btn btn-primary", role:"button", text:"GO", target: "_blank"})
domsugar("a", {href: tmp_url , class:"btn btn-primary", role:"button", text:"GO", target: "_blank"})
])
])
......@@ -90,8 +99,6 @@
// acquired methods
//////////////////////////////////////////////
.declareAcquiredMethod("getUrlFor", "getUrlFor")
.declareAcquiredMethod("crib_sw_get", "crib_sw_get")
.declareAcquiredMethod("crib_sw_put", "crib_sw_put")
.declareMethod('render', function (options) {
return displayAppList(this);
})
......
......@@ -156,6 +156,10 @@
.declareAcquiredMethod("crib_sw_getScope", "crib_sw_getScope")
.declareAcquiredMethod("crib_sw_get", "crib_sw_get")
.declareAcquiredMethod("crib_sw_put", "crib_sw_put")
.declareAcquiredMethod(
"crib_sw_setCribEnableGadgetUrl",
"crib_sw_setCribEnableGadgetUrl"
)
.declareAcquiredMethod("setSetting", "setSetting")
.declareAcquiredMethod("getUrlFor", "getUrlFor")
.declareAcquiredMethod("redirect", "redirect")
......@@ -171,7 +175,9 @@
return new RSVP.Queue()
.push(function () {
if (gadget.props.options.crib_enable_url !== undefined) {
return gadget.setSetting("site_editor_gadget_url", gadget.props.options.crib_enable_url);
return gadget.crib_sw_setCribEnableGadgetUrl(
gadget.props.options.crib_enable_url
);
}
return;
})
......
......@@ -289,12 +289,24 @@
.declareAcquiredMethod("crib_sw_get", "crib_sw_get")
.declareAcquiredMethod("crib_sw_put", "crib_sw_put")
.declareAcquiredMethod("getUrlFor", "getUrlFor")
.declareAcquiredMethod(
"crib_sw_setCribEnableGadgetUrl",
"crib_sw_setCribEnableGadgetUrl"
)
.declareMethod('render', function (options) {
var gadget = this;
if (options === undefined)
options = {};
gadget.props.options = options;
return new RSVP.Queue()
.push(function () {
if (gadget.props.options.crib_enable_url !== undefined) {
return gadget.crib_sw_setCribEnableGadgetUrl(
gadget.props.options.crib_enable_url
);
}
return;
})
.push(function () {
return gadget.props.start_deferred.resolve();
});
......
......@@ -113,6 +113,10 @@
})
.declareAcquiredMethod("crib_sw_allDocs", "crib_sw_allDocs")
.declareAcquiredMethod("crib_sw_getScope", "crib_sw_getScope")
.declareAcquiredMethod(
"crib_sw_setCribEnableGadgetUrl",
"crib_sw_setCribEnableGadgetUrl"
)
.declareAcquiredMethod("getUrlFor", "getUrlFor")
.declareAcquiredMethod("redirect", "redirect")
.declareAcquiredMethod("setSetting", "setSetting")
......@@ -123,6 +127,14 @@
options = {};
gadget.props.options = options;
return new RSVP.Queue()
.push(function () {
if (gadget.props.options.crib_enable_url !== undefined) {
return gadget.crib_sw_setCribEnableGadgetUrl(
gadget.props.options.crib_enable_url
);
}
return;
})
.push(function () {
if (options.pattern !== undefined) {
return gadget.setSetting("editor-pattern", options.pattern)
......
......@@ -5,7 +5,8 @@
"application_image_url": "img/vifib-logo.png",
"application_toc_accept": false,
"application_description":"Hello World",
"application_url":"hello-world/",
"application_url":"./hello-world/",
"application_crib":"./crib-enable.html",
"application_path":"hello-world/",
"application_edit_page":"hello-world/index.html",
"application_title":"Hello World",
......@@ -16,7 +17,8 @@
"application_image_type": "image",
"application_image_url": "pdf_viewer_app_logo.svg",
"application_description":"Read and manage you PDF Offline",
"application_url":"",
"application_url":"./",
"application_crib":"./crib-enable.html",
"application_path":"",
"application_edit_page":"gadget/gadget_cribjs_page_cribjs_home.html",
"application_title":"CribJS",
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment