Commit 3e1e712b authored by Leigh B Stoller's avatar Leigh B Stoller

More cleanup of ajax code, convert status page to a template

and new ajax handling.
parent 791a2d49
...@@ -271,6 +271,9 @@ body { ...@@ -271,6 +271,9 @@ body {
.text-danger { .text-danger {
color: red; color: red;
} }
.border-none {
border: none;
}
/* /*
.panel-body { .panel-body {
...@@ -348,3 +351,8 @@ body { ...@@ -348,3 +351,8 @@ body {
blockquote #selected_profile_description { blockquote #selected_profile_description {
font-size: 14px; font-size: 14px;
} }
.popover
{
min-width: 350px ! important;
}
<?php
#
# Copyright (c) 2000-2014 University of Utah and the Flux Group.
#
# {{{EMULAB-LICENSE
#
# This file is part of the Emulab network testbed software.
#
# This file is free software: you can redistribute it and/or modify it
# under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or (at
# your option) any later version.
#
# This file is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
# License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this file. If not, see <http://www.gnu.org/licenses/>.
#
# }}}
#
chdir("..");
include_once("webtask.php");
chdir("apt");
include_once("profile_defs.php");
#
# Return info about specific profile.
#
function Do_GetProfile()
{
global $this_user;
global $ajax_args;
if (!isset($ajax_args["uuid"])) {
SPITAJAX_ERROR(1, "Missing profile uuid");
return;
}
$uuid = $ajax_args["uuid"];
if (!IsValidUUID($uuid)) {
SPITAJAX_ERROR(1, "Not a valid UUID: $uuid");
return;
}
$profile = Profile::Lookup($uuid);
if (!$profile) {
SPITAJAX_ERROR(1, "No such profile $uuid");
return;
}
#
# We need permission checks on this path.
#
if (! $profile->ispublic()) {
if (! isset($this_user)) {
SPITAJAX_ERROR(1, "You must be logged in to access profile");
return;
}
if (!(ISADMIN() ||
$this_user->uid_idx() == $profile->creator_idx() ||
($profile->IsPrivate() &&
$profile->GetProject()->IsMember($this_user, $approved) &&
$approved))) {
SPITAJAX_ERROR(1, "Not enough permission to access profile");
return;
}
}
SPITAJAX_RESPONSE(array('rspec' => $profile->rspec(),
'name' => $profile->name(),
'idx' => $profile->idx(),
'description' => $profile->description()));
}
# Local Variables:
# mode:php
# End:
?>
...@@ -46,39 +46,7 @@ $optargs = OptionalPageArguments("create", PAGEARG_STRING, ...@@ -46,39 +46,7 @@ $optargs = OptionalPageArguments("create", PAGEARG_STRING,
"stuffing", PAGEARG_STRING, "stuffing", PAGEARG_STRING,
"verify", PAGEARG_STRING, "verify", PAGEARG_STRING,
"project", PAGEARG_PROJECT, "project", PAGEARG_PROJECT,
"formfields", PAGEARG_ARRAY, "formfields", PAGEARG_ARRAY);
"ajax_request", PAGEARG_BOOLEAN,
"ajax_method", PAGEARG_STRING,
"ajax_argument", PAGEARG_STRING);
#
# Deal with ajax requests.
#
if (isset($ajax_request)) {
if ($ajax_method == "getprofile") {
#
# We require the UUID on this path, until proper permission
# checks are done; too easy to guess an index.
#
if (!IsValidUUID($ajax_argument)) {
SPITAJAX_ERROR(1, "Not a valid UUID: $ajax_argument");
exit();
}
$obj = Profile::Lookup($ajax_argument);
if (!$obj) {
SPITAJAX_ERROR(1, "No such profile $ajax_argument");
exit();
}
#
# Need permission checks here.
#
SPITAJAX_RESPONSE(array('rspec' => $obj->rspec(),
'name' => $obj->name(),
'description' => $obj->description()));
}
exit();
}
$profile_default = "OneVM"; $profile_default = "OneVM";
$profile_array = array(); $profile_array = array();
...@@ -361,6 +329,7 @@ function SPITFORM($formfields, $newuser, $errors) ...@@ -361,6 +329,7 @@ function SPITFORM($formfields, $newuser, $errors)
echo "<script type='text/javascript'>\n"; echo "<script type='text/javascript'>\n";
echo " window.PROFILE = '" . $formfields["profile"] . "';\n"; echo " window.PROFILE = '" . $formfields["profile"] . "';\n";
echo " window.AJAXURL = 'server-ajax.php';\n";
if ($newuser) { if ($newuser) {
echo "window.APT_OPTIONS.isNewUser = true;\n"; echo "window.APT_OPTIONS.isNewUser = true;\n";
} }
......
...@@ -16,6 +16,7 @@ window.APT_OPTIONS.config = function () ...@@ -16,6 +16,7 @@ window.APT_OPTIONS.config = function ()
'marked': 'js/lib/marked', 'marked': 'js/lib/marked',
'moment': 'js/lib/moment', 'moment': 'js/lib/moment',
'underscore': 'js/lib/underscore-min', 'underscore': 'js/lib/underscore-min',
'filesize': 'js/lib/filesize.min',
'jacks': 'https://www.emulab.net/protogeni/jacks-stable/js/jacks' 'jacks': 'https://www.emulab.net/protogeni/jacks-stable/js/jacks'
}, },
shim: { shim: {
...@@ -28,7 +29,8 @@ window.APT_OPTIONS.config = function () ...@@ -28,7 +29,8 @@ window.APT_OPTIONS.config = function ()
'tablesorter': { }, 'tablesorter': { },
'tablesorterwidgets': { deps: ['tablesorter'] }, 'tablesorterwidgets': { deps: ['tablesorter'] },
'marked' : { exports: 'marked' }, 'marked' : { exports: 'marked' },
'underscore': { exports: '_' } 'underscore': { exports: '_' },
'filesize' : { exports: 'filesize' },
}, },
}); });
}; };
......
...@@ -8,10 +8,12 @@ function (_, sup) ...@@ -8,10 +8,12 @@ function (_, sup)
var jacksInstance; var jacksInstance;
var jacksUpdate; var jacksUpdate;
var ajaxurl;
function initialize() function initialize()
{ {
window.APT_OPTIONS.initialize(); window.APT_OPTIONS.initialize();
ajaxurl = window.AJAXURL;
if (window.APT_OPTIONS.isNewUser) { if (window.APT_OPTIONS.isNewUser) {
$('#verify_modal_submit').click(function (event) { $('#verify_modal_submit').click(function (event) {
...@@ -143,7 +145,9 @@ function (_, sup) ...@@ -143,7 +145,9 @@ function (_, sup)
[{ rspec: json.value.rspec }]); [{ rspec: json.value.rspec }]);
} }
} }
var $xmlthing = sup.CallMethod("getprofile", null, 0, profile); var $xmlthing = sup.CallServerMethod(ajaxurl,
"instantiate", "GetProfile",
{"uuid" : profile});
$xmlthing.done(callback); $xmlthing.done(callback);
} }
......
/*
2013 Jason Mulligan
@license BSD-3 <https://raw.github.com/avoidwork/filesize.js/master/LICENSE>
@link http://filesizejs.com
@module filesize
@version 2.0.0
*/
(function(r){function f(f,c){var b="",g=!1,l=6,a,h,q,d,m,n,p,e,k;if(isNaN(f))throw Error("Invalid arguments");c=c||{};h=!0===c.bits;e=!0===c.unix;a=void 0!==c.base?c.base:e?2:10;m=void 0!==c.round?c.round:e?1:2;k=void 0!==c.spacer?c.spacer:e?"":" ";d=Number(f);(q=0>d)&&(d=-d);if(0===d)b=e?"0":"0"+k+"B";else for(p=s[a][h?"bits":"bytes"];l--;)if(n=p[l][1],a=p[l][0],d>=n){t.test(a)&&(g=!0,m=0);b=(d/n).toFixed(m);!g&&e?(h&&u.test(a)&&(a=a.toLowerCase()),a=a.charAt(0),g=v.exec(b),h||"k"!==a||(a="K"),null!==
g&&void 0!==g[1]&&w.test(g[1])&&(b=parseInt(b,x)),b+=k+a):e||(b+=k+a);break}q&&(b="-"+b);return b}var u=/b$/,t=/^B$/,x=10,v=/\.(.*)/,w=/^0$/,s={2:{bits:[["B",1],["kb",128],["Mb",131072],["Gb",134217728],["Tb",137438953472],["Pb",0x800000000000]],bytes:[["B",1],["kB",1024],["MB",1048576],["GB",1073741824],["TB",1099511627776],["PB",0x4000000000000]]},10:{bits:[["B",1],["kb",125],["Mb",125E3],["Gb",125E6],["Tb",125E9],["Pb",125E12]],bytes:[["B",1],["kB",1E3],["MB",1E6],["GB",1E9],["TB",1E12],["PB",
1E15]]}};"undefined"!==typeof exports?module.exports=f:"function"===typeof define?define(function(){return f}):r.filesize=f})(this);
//@ sourceMappingURL=filesize.map
window.APT_OPTIONS.config(); window.APT_OPTIONS.config();
require(['underscore', 'js/quickvm_sup', require(['underscore', 'js/quickvm_sup', 'filesize',
'js/lib/text!template/manage-profile.html', 'js/lib/text!template/manage-profile.html',
'js/lib/text!template/waitwait-modal.html', 'js/lib/text!template/waitwait-modal.html',
'js/lib/text!template/imaging-modal.html', 'js/lib/text!template/imaging-modal.html',
...@@ -9,7 +9,7 @@ require(['underscore', 'js/quickvm_sup', ...@@ -9,7 +9,7 @@ require(['underscore', 'js/quickvm_sup',
'js/lib/text!template/rspectextview-modal.html', 'js/lib/text!template/rspectextview-modal.html',
// jQuery modules // jQuery modules
'filestyle','marked','jquery-ui','jquery-grid'], 'filestyle','marked','jquery-ui','jquery-grid'],
function (_, sup, function (_, sup, filesize,
manageString, waitwaitString, imagingString, manageString, waitwaitString, imagingString,
rendererString, showtopoString, rspectextviewString) rendererString, showtopoString, rspectextviewString)
{ {
...@@ -269,6 +269,8 @@ require(['underscore', 'js/quickvm_sup', ...@@ -269,6 +269,8 @@ require(['underscore', 'js/quickvm_sup',
label_text = label_text + label_text = label_text +
"<a href='#' class='btn btn-xs' " + "<a href='#' class='btn btn-xs' " +
" data-toggle='popover' " + " data-toggle='popover' " +
" data-html='true' " +
" data-delay='{\"hide\":1000}' " +
" data-content='" + item.dataset['help'] + "'>" + " data-content='" + item.dataset['help'] + "'>" +
"<span class='glyphicon glyphicon-question-sign'>" + "<span class='glyphicon glyphicon-question-sign'>" +
" </span></a>"; " </span></a>";
...@@ -522,7 +524,6 @@ require(['underscore', 'js/quickvm_sup', ...@@ -522,7 +524,6 @@ require(['underscore', 'js/quickvm_sup',
// Ask the server for information to populate the imaging modal. // Ask the server for information to populate the imaging modal.
// //
var callback = function(json) { var callback = function(json) {
console.info(json);
var value = json.value; var value = json.value;
if (json.code) { if (json.code) {
...@@ -553,7 +554,11 @@ require(['underscore', 'js/quickvm_sup', ...@@ -553,7 +554,11 @@ require(['underscore', 'js/quickvm_sup',
if (! _.has(value, "node_status")) { if (! _.has(value, "node_status")) {
value["node_status"] = "unknown"; value["node_status"] = "unknown";
} }
if (! _.has(value, "image_size")) { if (_.has(value, "image_size")) {
// We get KB to avoid overflow along the way.
value["image_size"] = filesize(value["image_size"] * 1024);
}
else {
value["image_size"] = "unknown"; value["image_size"] = "unknown";
} }
$('#imaging_modal_node_status').html(value["node_status"]); $('#imaging_modal_node_status').html(value["node_status"]);
...@@ -612,7 +617,7 @@ require(['underscore', 'js/quickvm_sup', ...@@ -612,7 +617,7 @@ require(['underscore', 'js/quickvm_sup',
var $xmlthing = sup.CallServerMethod(ajaxurl, var $xmlthing = sup.CallServerMethod(ajaxurl,
"manage_profile", "manage_profile",
"SnapShotStatus", "CloneStatus",
{"uuid" : uuid}); {"uuid" : uuid});
$xmlthing.done(callback); $xmlthing.done(callback);
} }
......
window.APT_OPTIONS.config(); window.APT_OPTIONS.config();
require(['js/quickvm_sup', 'moment', require(['underscore', 'js/quickvm_sup', 'moment',
'js/lib/text!template/status.html',
'js/lib/text!template/waitwait-modal.html',
'js/lib/text!template/oops-modal.html',
'js/lib/text!template/register-modal.html',
'js/lib/text!template/terminate-modal.html',
'js/lib/text!template/extend-modal.html',
'js/lib/text!template/clone-help.html',
'tablesorter', 'tablesorterwidgets'], 'tablesorter', 'tablesorterwidgets'],
function (sup, moment) function (_, sup, moment, statusString, waitwaitString, oopsString,
registerString, terminateString, extendString, cloneHelpString)
{ {
'use strict'; 'use strict';
var CurrentTopo = null; var CurrentTopo = null;
var nodecount = 0; var nodecount = 0;
var ajaxurl = null; var ajaxurl = null;
var statusTemplate = _.template(statusString);
var waitwaitTemplate = _.template(waitwaitString);
var oopsTemplate = _.template(oopsString);
var registerTemplate = _.template(registerString);
var terminateTemplate = _.template(terminateString);
var extendTemplate = _.template(extendString);
function initialize() function initialize()
{ {
window.APT_OPTIONS.initialize(sup); window.APT_OPTIONS.initialize(sup);
ajaxurl = window.APT_OPTIONS.AJAXURL; ajaxurl = window.APT_OPTIONS.AJAXURL;
// Generate the templates.
var template_args = {
uuid: window.APT_OPTIONS.UUID,
profileName: window.APT_OPTIONS.profileName,
sliceURN: window.APT_OPTIONS.sliceURN,
sliceExpires: window.APT_OPTIONS.sliceExpires,
sliceExpiresText: window.APT_OPTIONS.sliceExpiresText,
creatorUid: window.APT_OPTIONS.creatorUid,
creatorEmail: window.APT_OPTIONS.creatorEmail,
registered: window.APT_OPTIONS.registered,
};
var status_html = statusTemplate(template_args);
$('#status-body').html(status_html);
var waitwait_html = waitwaitTemplate(template_args);
$('#waitwait_div').html(waitwait_html);
var oops_html = oopsTemplate(template_args);
$('#oops_div').html(oops_html);
var register_html = registerTemplate(template_args);
$('#register_div').html(register_html);
var extend_html = extendTemplate(template_args);
$('#extend_div').html(extend_html);
var terminate_html = terminateTemplate(template_args);
$('#terminate_div').html(terminate_html);
//
// Look at initial status to determine if we show the progress bar.
//
var spinwidth = 0;
var instanceStatus = window.APT_OPTIONS.instanceStatus;
if (instanceStatus == "created") {
spinwidth = "33";
}
else if (instanceStatus == "provisioned") {
spinwidth = "66";
}
if (spinwidth) {
$("#status_progress_bar").width(spinwidth + "%");
$('#status_progress_outerdiv').removeClass("hidden");
}
// This activates the popover subsystem. // This activates the popover subsystem.
$('[data-toggle="popover"]').popover({ $('[data-toggle="popover"]').popover({
trigger: 'hover', trigger: 'hover',
...@@ -48,6 +102,41 @@ function (sup, moment) ...@@ -48,6 +102,41 @@ function (sup, moment)
win.focus(); win.focus();
}); });
// Handler for the Clone button.
$('button#clone_button').click(function (event) {
event.preventDefault();
window.location.replace('manage_profile.php?action=clone' +
'&snapuuid=' + window.APT_OPTIONS.uuid);
});
//
// Attach a hover popover to explain what Clone means. We need
// the hover action delayed by our own code, since we want to
// use a manual trigger to close the popover, or else the user
// will not have enough time to read the content.
//
var popover_timer;
$("button#clone_button").mouseenter(function(){
popover_timer = setTimeout(function() {
$('button#clone_button').popover({
html: true,
content: cloneHelpString +
'<span id=clone_popover_close class=close>' +
'&times;</span>',
trigger: 'manual',
placement:'left',
container:'body',
});
$('button#clone_button').popover('show');
$('#clone_popover_close').on('click', function(e) {
$('button#clone_button').popover('hide');
});
},1000)
}).mouseleave(function(){
clearTimeout(popover_timer);
});
$('button#request-extension').click(function (event) { $('button#request-extension').click(function (event) {
event.preventDefault(); event.preventDefault();
RequestExtension(window.APT_OPTIONS.uuid); RequestExtension(window.APT_OPTIONS.uuid);
...@@ -64,7 +153,7 @@ function (sup, moment) ...@@ -64,7 +153,7 @@ function (sup, moment)
// This is considered the home page, for now. // This is considered the home page, for now.
window.location.replace('instantiate.php'); window.location.replace('instantiate.php');
} }
sup.ShowModal("#waitwait"); sup.ShowModal("#waitwait-modal");
var xmlthing = sup.CallServerMethod(ajaxurl, var xmlthing = sup.CallServerMethod(ajaxurl,
"status", "status",
...@@ -119,18 +208,18 @@ function (sup, moment) ...@@ -119,18 +208,18 @@ function (sup, moment)
var statustext = "Please wait while we get your experiment ready"; var statustext = "Please wait while we get your experiment ready";
if (status == 'provisioned') { if (status == 'provisioned') {
$("#quickvm_progress_bar").width("66%"); $("#status_progress_bar").width("66%");
status_html = "booting"; status_html = "booting";
} }
else if (status == 'ready') { else if (status == 'ready') {
bgtype = "bg-success"; bgtype = "bg-success";
statustext = "Your experiment is ready!"; statustext = "Your experiment is ready!";
status_html = "<font color=green>ready</font>"; status_html = "<font color=green>ready</font>";
if ($("#quickvm_progress").length) { if ($("#status_progress_div").length) {
$("#quickvm_progress").removeClass("progress-striped"); $("#status_progress_div").removeClass("progress-striped");
$("#quickvm_progress").removeClass("active"); $("#status_progress_div").removeClass("active");
$("#quickvm_progress").addClass("progress-bar-success"); $("#status_progress_div").addClass("progress-bar-success");
$("#quickvm_progress_bar").width("100%"); $("#status_progress_bar").width("100%");
} }
if (! StatusWatchCallBack.active) { if (! StatusWatchCallBack.active) {
ShowTopo(uuid); ShowTopo(uuid);
...@@ -144,11 +233,11 @@ function (sup, moment) ...@@ -144,11 +233,11 @@ function (sup, moment)
statustext = "Something went wrong, sorry! " + statustext = "Something went wrong, sorry! " +
"We've been notified."; "We've been notified.";
status_html = "<font color=red>failed</font>"; status_html = "<font color=red>failed</font>";
if ($("#quickvm_progress").length) { if ($("#status_progress_div").length) {
$("#quickvm_progress").removeClass("progress-striped"); $("#status_progress_div").removeClass("progress-striped");
$("#quickvm_progress").removeClass("active"); $("#status_progress_div").removeClass("active");
$("#quickvm_progress").addClass("progress-bar-danger"); $("#status_progress_div").addClass("progress-bar-danger");
$("#quickvm_progress_bar").width("100%"); $("#status_progress_bar").width("100%");
} }
DisableButtons(); DisableButtons();
EnableButton("terminate"); EnableButton("terminate");
...@@ -186,13 +275,13 @@ function (sup, moment) ...@@ -186,13 +275,13 @@ function (sup, moment)
{ {
EnableButton("terminate"); EnableButton("terminate");
EnableButton("extend"); EnableButton("extend");
EnableButton("snapshot"); EnableButton("clone");
} }
function DisableButtons() function DisableButtons()
{ {
DisableButton("terminate"); DisableButton("terminate");
DisableButton("extend"); DisableButton("extend");
DisableButton("snapshot"); DisableButton("clone");
} }
function EnableButton(button) function EnableButton(button)
{ {
...@@ -208,8 +297,8 @@ function (sup, moment) ...@@ -208,8 +297,8 @@ function (sup, moment)
button = "#terminate_button"; button = "#terminate_button";
else if (button == "extend") else if (button == "extend")
button = "#extend_button"; button = "#extend_button";
else if (button == "snapshot" && nodecount == 1) else if (button == "clone" && nodecount == 1)
button = "#snapshot_button"; button = "#clone_button";
else else
return; return;
...@@ -360,7 +449,7 @@ function (sup, moment) ...@@ -360,7 +449,7 @@ function (sup, moment)
return; return;
} }
var callback = function(json) { var callback = function(json) {
sup.HideModal("#waitwait"); sup.HideModal("#waitwait-modal");
// console.info(json.value); // console.info(json.value);
var message; var message;
...@@ -382,7 +471,7 @@ function (sup, moment) ...@@ -382,7 +471,7 @@ function (sup, moment)
StartCountdownClock.reset = json.value; StartCountdownClock.reset = json.value;
} }
sup.HideModal('#extend_modal'); sup.HideModal('#extend_modal');
sup.ShowModal("#waitwait"); sup.ShowModal("#waitwait-modal");
var xmlthing = sup.CallServerMethod(ajaxurl, var xmlthing = sup.CallServerMethod(ajaxurl,
"status", "status",
"RequestExtension", "RequestExtension",
...@@ -608,15 +697,15 @@ function (sup, moment) ...@@ -608,15 +697,15 @@ function (sup, moment)
ReDrawTopoMap(); ReDrawTopoMap();
$("#showtopo_container").removeClass("invisible"); $("#showtopo_container").removeClass("invisible");
// If a single node, show the snapshot button. Only // If a single node, show the clone button. Only
// single node experiments can do this. // single node experiments can do this.
if (nodecount == 1) { if (nodecount == 1) {
$("#snapshot_button").removeClass("invisible"); $("#clone_button").removeClass("invisible");
EnableButton("snapshot"); EnableButton("clone");
} }
// And start up ssh for single node topologies. // And start up ssh for single node topologies.
if (nodecount == 1 && nodehostport) { if (nodecount == 1 && nodehostport && 0) {
NewSSHTab(nodehostport, nodename); NewSSHTab(nodehostport, nodename);
} }
} }
......
...@@ -21,22 +21,15 @@ ...@@ -21,22 +21,15 @@
# #
# }}} # }}}
# #
# Local Variables:
# mode:php
# End: