Commit 8471fad4 authored by Leigh B Stoller's avatar Leigh B Stoller

Switch to out of band description to new rspec_tour section

of the rspec.
parent cb301e70
......@@ -194,7 +194,6 @@ sub Create($$$$$)
}
my $uuid = NewUUID();
my $desc = DBQuoteSpecial($argref->{'description'});
my $rspec = DBQuoteSpecial($argref->{'rspec'});
......@@ -205,7 +204,8 @@ sub Create($$$$$)
$query .= ",uuid='$uuid'";
$query .= ",pid='$pid',pid_idx='$pid_idx'";
$query .= ",creator='$uid',creator_idx='$uid_idx'";
$query .= ",description=$desc";
# This is temporary until all rspecs using tour format.
$query .= ",description=''";
$query .= ",rspec=$rspec";
$query .= ",public=1"
if (exists($argref->{'public'}) && $argref->{'public'});
......
......@@ -13,6 +13,7 @@ window.APT_OPTIONS.config = function ()
'filestyle': 'js/lib/filestyle',
'tablesorter': 'js/lib/jquery.tablesorter.min',
'tablesorterwidgets': 'js/lib/jquery.tablesorter.widgets.min',
'marked': 'js/lib/marked',
},
shim: {
'bootstrap': { deps: ['jquery'] },
......@@ -22,6 +23,7 @@ window.APT_OPTIONS.config = function ()
'filestyle': { deps: ['bootstrap']},
'tablesorter': { deps: ['jquery'] },
'tablesorterwidgets': { deps: ['tablesorter'] },
'marked' : { exports: 'marked' },
},
});
}
......
......@@ -6,24 +6,43 @@ require(['jquery', 'js/quickvm_sup',
function ($, sup)
{
'use strict';
var editing = 0;
function initialize()
{
window.APT_OPTIONS.initialize(sup);
try {
$('#rspecfile').change(function() {
editing = window.EDITING;
// This activates the popover subsystem.
$('[data-toggle="popover"]').popover({
trigger: 'hover',
container: 'body'
});
$('body').show();
$('#rspecfile').change(function() {
var reader = new FileReader();
reader.onload = function(event) {
var content = event.target.result;
var xmlDoc = $.parseXML(content);
var xml = $(xmlDoc);
// Allow editing the boxes now that we have an rspec.
$('#profile_instructions').prop("disabled", false);
$('#profile_description').prop("disabled", false);
// Show the hidden buttons (in new profile mode)
$('#showtopo_modal_button').removeClass("invisible");
$('#show_rspec_textarea_button').removeClass("invisible");
// Stick html into the textarea
$('#profile_rspec_textarea').val(content);
ShowRspecContent(content);
ExtractFromRspec(xml);
//ShowRspecTopo(xml);
};
reader.readAsText(this.files[0]);
});
}
catch (e) {
alert(e);
}
});
$.fn.animateBackgroundHighlight = function(highlightColor, duration) {
var highlightBg = highlightColor || "#FFFF9C";
......@@ -35,7 +54,10 @@ function ($, sup)
$('#showtopo_modal_button').click(function (event) {
event.preventDefault();
// The rspec is taken from the text area.
ShowRspecContent($('#profile_rspec_textarea').val());
var xmlDoc = $.parseXML($('#profile_rspec_textarea').val());
var xml = $(xmlDoc);
ShowRspecTopo(xml);
});
$('#expand_rspec_modal_button').click(function (event) {
$('#modal_profile_rspec_textarea').val(
......@@ -53,6 +75,7 @@ function ($, sup)
$('#profile_rspec_textarea').css({"opacity":"0.2"});
$('#profile_rspec_textarea').animate({"opacity":"1.0"}, 1500);
});
if (0) {
// Enable the Modify button if the form changes.
$('#quickvm_create_profile_form :input').each(function() {
// Need to use keyup since an input does not "change"
......@@ -65,20 +88,124 @@ function ($, sup)
$('#quickvm_create_profile_form').change(function() {
$('#profile_submit_button').prop('disabled', false);
});
}
// Auto select the URL if the user clicks in the box.
$('#profile_url').click(function() {
$(this).focus();
$(this).select();
});
// Prevent submit if the description is empty.
$('#profile_submit_button').click(function (event) {
var description = $('#profile_description').val();
if (description === "") {
event.preventDefault();
alert("Please provide a description. Its handy!");
return false;
}
return true;
});
/*
* If the description/instructions textarea are edited, copy
* the text back into the rspec since that is what actually
* gets submitted; the rspec is authoritative.
*/
$('#profile_instructions').change(function() {
ChangeHandlerAux("instructions");
});
$('#profile_description').change(function() {
ChangeHandlerAux("description");
});
/*
* If editing, need to suck the description and instructions
* out of the rspec and put them into the text boxes. But
* watch for some already in the description box, it is an old
* one and we want to use it if no description in the rspec.
*/
if (editing) {
var old_text = $('#profile_description').val();
if (old_text != "") {
ChangeHandlerAux("description");
}
var xmlDoc = $.parseXML($('#profile_rspec_textarea').val());
var xml = $(xmlDoc);
ExtractFromRspec(xml);
}
else {
/*
* Not editing, so disable the text boxes until we get
* an rspec.
*/
$('#profile_instructions').prop("disabled", true);
$('#profile_description').prop("disabled", true);
}
}
//
// Helper function for instructions/description change handler above.
//
function ChangeHandlerAux(which)
{
var text = $('#profile_' + which).val();
var rspec = $('#profile_rspec_textarea').val();
if (rspec === "") {
return;
}
console.log(text);
var xmlDoc = $.parseXML(rspec);
var xml = $(xmlDoc);
// See if we need to add the section to top level.
var tour = $(xml).find("rspec_tour");
if (! tour.length) {
$(xml).find("rspec").prepend($('<rspec_tour xmlns=' +
'"http://www.protogeni.net/resources/rspec/ext/apt-tour/1">' +
'</rspec_tour>'));
}
var tour = $(xml).find("rspec_tour");
// Ditto the subsection.
var sub = $(tour).find(which);
if (!sub.length) {
$(xml).find("rspec_tour").append($('<' + which + ' type="text">' +
'</' + which + '>'));
}
var sub = $(tour).find(which);
$(sub).text(text);
console.log(xml);
var s = new XMLSerializer();
var str = s.serializeToString(xml[0]);
console.log(str);
$('#profile_rspec_textarea').val(str);
}
/*
* We want to look for and pull out the introduction and overview text,
* and put them into the text boxes. The user can edit them in the
* boxes. More likely, they will not be in the rspec, and we have to
* add them to the rspec_tour section.
*/
function ExtractFromRspec(xml)
{
$(xml).find("rspec_tour").each(function() {
$(this).find("description").each(function() {
var text = $(this).text();
$('#profile_description').val(text);
});
$(this).find("instructions").each(function() {
var text = $(this).text();
$('#profile_instructions').val(text);
});
});
}
//
// Show the rspec text in the modal.
//
function ShowRspecContent(content)
function ShowRspecTopo(xml)
{
var xmlDoc = $.parseXML(content);
var xml = $(xmlDoc);
var topo = sup.ConvertManifestToJSON(null, xml);
console.info(topo);
......
define(['jquery', 'd3', 'dateformat'],
define(['jquery', 'd3', 'dateformat', 'marked'],
function ($, d3) {
var myuuid = null;
......@@ -127,8 +127,24 @@ function ShowTopo(uuid)
var xmlDoc = $.parseXML(json.value);
var xml = $(xmlDoc);
var topo = ConvertManifestToJSON(null, xml);
console.info(topo);
console.log(json.value);
// Deal with the instructions.
$(xml).find("rspec_tour").each(function() {
$(this).find("instructions").each(function() {
var marked = require('marked');
marked.setOptions({"sanitize" : true});
var text = $(this).text();
console.log(text);
// Make the div visible.
$('#instructions_panel').removeClass("invisible");
// And stick the text in
$('#instructions_text').html(marked(text));
});
});
$("#showtopo_container").removeClass("invisible");
// Subtract -2 cause of the border.
maketopmap("#showtopo_statuspage",
......@@ -179,10 +195,30 @@ function ShowProfileList(selectedElement)
var xmlDoc = $.parseXML(json.value.rspec);
var xml = $(xmlDoc);
var topo = ConvertManifestToJSON(profile, xml);
console.info(topo);
$('#showtopo_title').html("<h3>" + json.value.name + "</h3>");
$('#showtopo_description').html(json.value.description);
/*
* We now use the desciption from inside the rspec, unless there
* is none, in which case look to see if the we got one in the
* rpc reply, which we will until all profiles converted over to
* new format rspecs.
*/
var description = null;
$(xml).find("rspec_tour").each(function() {
$(this).find("description").each(function() {
description = $(this).text();
});
});
if (!description) {
if (json.value.description != "") {
description = json.value.description;
}
else {
description = "Hmm, no description for this profile";
}
}
$('#showtopo_description').html(description);
maketopmap("#showtopo_div",
($("#showtopo_div").outerWidth()),
......@@ -271,11 +307,6 @@ function RegisterAccount(uid, email)
function InitQuickVM(uuid, slice_expires)
{
// This activates the popover subsystem.
$('[data-toggle="popover"]').popover({
trigger: 'hover',
'placement': 'top'
});
// Use an unload event to terminate any shells.
$(window).bind("unload", function() {
console.info("Unload function called");
......
......@@ -10,6 +10,13 @@ function ($, sup)
function initialize()
{
window.APT_OPTIONS.initialize(sup);
// This activates the popover subsystem.
$('[data-toggle="popover"]').popover({
trigger: 'hover',
'placement': 'top'
});
sup.InitQuickVM(window.APT_OPTIONS.uuid,
window.APT_OPTIONS.sliceExpires);
$('button#register-account').click(function (event) {
......
......@@ -72,21 +72,31 @@ function SPITFORM($formfields, $errors)
}
}
$formatter = function($field, $label, $html, $help = null) use ($errors) {
$class = "form-group";
if ($errors && array_key_exists($field, $errors)) {
$class .= " has-error";
}
echo "<div class='$class'>\n";
$format_label = function($field, $label, $help = null) {
echo " <label for='$field' ".
" class='col-sm-3 control-label'>$label ";
" class='col-sm-2 control-label'>$label ";
if ($help) {
echo "<a href='#' class='btn btn-xs'
data-toggle='popover' data-content='$help'>".
"<span class='glyphicon glyphicon-question-sign'></span></a>";
"<span class='glyphicon glyphicon-question-sign'>
</span></a>";
}
echo " </label>\n";
echo " <div class='col-sm-9'>\n";
};
$formatter = function($field, $label, $html, $help = null)
use ($errors, $format_label) {
$class = "form-group";
if ($errors && array_key_exists($field, $errors)) {
$class .= " has-error";
}
$size = 12;
echo "<div class='$class'>\n";
if ($label) {
$format_label($field, $label, $help);
$size = 10;
}
echo " <div class='col-sm-${size}'>\n";
echo " $html\n";
if ($errors && array_key_exists($field, $errors)) {
echo "<label class='control-label' for='inputError'>" .
......@@ -115,7 +125,6 @@ function SPITFORM($formfields, $errors)
method='post' action='manage_profile.php'>\n";
echo " <div class='row'>\n";
echo " <div class='col-sm-12'>\n";
echo " <fieldset>\n";
#
# Look for non-specific error.
......@@ -131,26 +140,38 @@ function SPITFORM($formfields, $errors)
if ($editing) {
echo "<input type='hidden' name='action' value='edit'>\n";
}
echo " </div></div><fieldset>\n";
# First row has both name and project, which makes the layout odd.
echo "<div class='row'>\n";
$format_label("profile_name", "Name",
($editing ? null :
"alphanumeric, dash, underscore, no whitespace"));
echo "<div class='col-sm-4'>\n";
# In editing mode, pass through static values.
if ($editing) {
$formatter("profile_name", "Profile Name",
$formatter("profile_name", null,
"<p class='form-control-static'>" .
$formfields["profile_name"] .
" (created " . $formfields["profile_created"] . ")</p>");
$formfields["profile_name"] . "</p>");
echo "<input type='hidden' name='formfields[profile_name]' ".
"value='" . $formfields["profile_name"] . "'>\n";
}
else {
$formatter("profile_name", "Profile Name",
$formatter("profile_name", null,
"<input name=\"formfields[profile_name]\"
id='profile_name'
value='" . $formfields["profile_name"] . "'
class='form-control'
placeholder='' type='text'>",
"alphanumeric, dash, underscore, no whitespace");
placeholder='' type='text'>");
}
# End of first half of row
echo " </div>\n";
# Second half of the row.
$format_label("profile_pid", "Project");
echo "<div class='col-sm-4'>\n";
#
# If user is a member of only one project, then just pass it
# through, no need for the user to see it. Otherwise we have
......@@ -159,15 +180,13 @@ function SPITFORM($formfields, $errors)
if (count($projlist) == 1 || $editing) {
$pid = ($editing ? $formfields["profile_pid"] : $projlist[0]);
if ($editing) {
$formatter("profile_pid", "Project",
"<p class='form-control-static'>$pid</p>");
}
$formatter("profile_pid", null,
"<p class='form-control-static'>$pid</p>");
echo "<input type='hidden' name='formfields[profile_pid]' ".
"value='$pid'>\n";
}
else {
$pid_options = "";
$pid_options = "<option value=''>Please Select</option>\n";
while (list($project) = each($projlist)) {
$selected = "";
if ($formfields["profile_pid"] == $project) {
......@@ -176,73 +195,86 @@ function SPITFORM($formfields, $errors)
$pid_options .=
"<option $selected value='$project'>$project</option>\n";
}
$formatter("profile_pid", "Project",
$formatter("profile_pid", null,
"<select name=\"formfields[profile_pid]\"
id='profile_pid' class='form-control'
placeholder='Please Select'>$pid_options</select>");
}
$formatter("profile_description", "Description",
"<textarea name=\"formfields[profile_description]\"
id='profile_description'
rows=3
class='form-control'
placeholder=''
type='textarea'>" .
$formfields["profile_description"] . "</textarea>");
# End of first row.
echo " </div>
</div>\n";
echo "</fieldset><fieldset>\n";
#
# In edit mode, display current rspec in text area inside a modal.
# See below for the modal. So, we need buttons to display the source
# modal, the topo modal, in addition to a file chooser for a new rspec.
#
if ($editing) {
$rspec_html =
"<div class='row'>
<div class='col-xs-2'>
<button class='btn btn-primary btn-xs'
id='showtopo_modal_button'>
Show</button>
</div>
<div class='col-xs-2'>
<button class='btn btn-primary btn-xs' type='button'
data-toggle='collapse' data-target='#rspec_textarea'>
Edit</button>
</div>
<div class='col-xs-8'>
<input name='rspecfile' id='rspecfile' type=file
class='filestyle'
data-classButton='btn btn-primary btn-xs'
data-input='false' data-buttonText='Choose new file'>
</div>
</div>
<div class='collapse' id='rspec_textarea'
style='margin-top: 4px;'>
<div class='row'>
<div class='col-xs-12'>
<textarea name=\"formfields[profile_rspec]\"
id='profile_rspec_textarea'
rows=5
class='form-control'
type='textarea'>" .
$invisible = ($editing ? "" : "invisible");
$rspec_html =
"<div class='row'>
<div class='col-xs-3'>
<input name='rspecfile' id='rspecfile' type=file
class='filestyle'
data-classButton='btn btn-primary btn-xs'
data-input='false'
data-buttonText='Choose " . ($editing ? "new" : "") . " file'>
</div>
<div class='col-xs-2'>
<button class='btn btn-primary btn-xs $invisible'
id='showtopo_modal_button'>
Show</button>
</div>
<div class='col-xs-2'>
<button class='btn btn-primary btn-xs $invisible' type='button'
id='show_rspec_textarea_button'
data-toggle='collapse' data-target='#rspec_textarea'>
Edit</button>
</div>
</div>
<div class='collapse' id='rspec_textarea'
style='margin-top: 4px;'>
<div class='row'>
<div class='col-xs-12'>
<textarea name=\"formfields[profile_rspec]\"
id='profile_rspec_textarea'
rows=5
class='form-control'
type='textarea'>" .
$formfields["profile_rspec"] . "</textarea>
</div>
</div>
<div class='row' style='margin-top: 4px;'>
<div class='col-xs-12'>
<button class='btn btn-primary btn-xs' type='button'
id='expand_rspec_modal_button'>
Expand</button>
</div>
</div>
</div>\n";
}
else {
$rspec_html = "<input name='rspecfile' id='rspecfile'
type=file class='form-control'>";
}
</div>
</div>
<div class='row' style='margin-top: 4px;'>
<div class='col-xs-12'>
<button class='btn btn-primary btn-xs' type='button'
id='expand_rspec_modal_button'>
Expand</button>
</div>
</div>
</div>\n";
$formatter("profile_rspec", "Your rspec", $rspec_html);
$formatter("profile_description", "Description",
"<textarea name=\"formfields[profile_description]\"
id='profile_description'
rows=3
class='form-control'
placeholder=''
type='textarea'>" .
$formfields["profile_description"] . "</textarea>",
"Briefly describe what this profile does");
$formatter("profile_instructions", "Instructions",
"<textarea name=\"formfields[profile_instructions]\"
id='profile_instructions'
rows=3
class='form-control'
placeholder=''
type='textarea'></textarea>",
"Briefly describe how to use this profile after it starts");
$formatter("profile_listed", "Listed?",
"<div class='checkbox'>
<label><input name=\"formfields[profile_listed]\" ".
......@@ -265,7 +297,6 @@ function SPITFORM($formfields, $errors)
<div class='col-sm-offset-2 col-sm-10'>
<button class='btn btn-primary btn-sm pull-right'
id='profile_submit_button'
disabled='disabled'
style='margin-right: 10px;'
type='submit' name='create'>$button_label</button>\n";
if ($editing) {
......@@ -278,8 +309,6 @@ function SPITFORM($formfields, $errors)
href='manage_profile.php?action=delete&idx=$idx'
type='button' name='delete'>Delete</a>\n";
}
echo " </div>\n";
echo "</div>\n";
echo " </div>\n";
echo " </div>\n";
......@@ -340,6 +369,9 @@ function SPITFORM($formfields, $errors)
</div>
</div>\n";
echo "<script type='text/javascript'>\n";
echo " window.EDITING = $editing;\n";
echo "</script>\n";
echo "<script src='js/lib/require.js' data-main='js/manage_profile'>
</script>";
SPITFOOTER();
......@@ -428,7 +460,7 @@ $errors = array();
#
# Quick check for required fields.
#
$required = array("pid", "name", "description");
$required = array("pid", "name");
foreach ($required as $key) {
if (!isset($formfields["profile_${key}"]) ||
......@@ -444,7 +476,7 @@ foreach ($required as $key) {
#
# The rspec file has to be treated specially of course.
#
if (isset($_FILES['rspecfile']) &&
if (0 && isset($_FILES['rspecfile']) &&
$_FILES['rspecfile']['name'] != "" &&
$_FILES['rspecfile']['name'] != "none") {
......@@ -519,11 +551,6 @@ else {
fwrite($fp, " <value>" .
htmlspecialchars($formfields["profile_name"]) . "</value>");
fwrite($fp, "</attribute>\n");
fwrite($fp, "<attribute name='profile_description'>");
fwrite($fp, " <value>" .
htmlspecialchars($formfields["profile_description"]) .
"</value>");
fwrite($fp, "</attribute>\n");
fwrite($fp, "<attribute name='rspec'>");
fwrite($fp, " <value>" . htmlspecialchars($rspec) . "</value>");
fwrite($fp, "</attribute>\n");
......
......@@ -270,6 +270,34 @@ echo "</div>\n";
echo "</div>\n";
echo "</div>\n";
#
# Add a div for the instructions if there are instructions. The client
# will actually fill in the div though, since it is markdown and that
# is more easily done on the client side for now.
#