Commit f673e2f8 authored by Jonathon Duerig's avatar Jonathon Duerig
Browse files

New genilib-editor refactoring. Various bugfixes.

Fix error-handling errors.
Make the waitwait modal non-dismissable everywhere.
The new genilib-editor is now a kind of mega-modal which can live in both the show-profile and modify_profile pages. It is read-only in the former case and read/writable in the latter.

All of the form-submitting stuff that recent ajaxification broke has been stripped out. It is now a pure editor that notifies the surrounding page of changes when you click on 'accept'.
parent f646c14a
......@@ -48,7 +48,7 @@ sub usage()
}
my $optlist = "dvt:m";
my $debug = 0;
my $verbose = 1;
my $verbose = 0;
my $webtask;
my $webtask_id;
# VerifyXML sets these, need to declare early. Need to clean this up.
......@@ -959,6 +959,9 @@ sub UserError($)
my $errors = {};
if (ref($ref) eq "SCALAR") {
$errors->{"error"} = $$ref;
}
elsif (ref($ref) eq "") {
$errors->{"error"} = $ref;
}
else {
......
......@@ -289,6 +289,7 @@ if ($exit_status) {
}
unlink($outfile);
unlink($infile);
# We want to pass along the exit status, since it indicates an internal
# vs script error.
exit($exit_status >> 8);
......
#editor
#genilib-editor-body #genilib-editor
{
width: 100%;
height: 80%;
}
#jacks-root
#genilib-editor-body #jacks-root
{
display: none;
}
#error-root
#genilib-editor-body #error-root
{
display: none;
}
#settings-root
#genilib-editor-body #settings-root
{
display: none;
}
.edit-buttons
#genilib-editor-body .edit-buttons
{
padding-bottom: 10px;
}
#jacks-container
#genilib-editor-body #jacks-container
{
/*
position: absolute;
......@@ -36,12 +36,12 @@
background-color: #fff;
}
#error-root .panel-body
#genilib-editor-body #error-root .panel-body
{
background-color: #f5f5f5;
}
.error-item > pre
#genilib-editor-body .error-item > pre
{
background-color: inherit;
margin: 0;
......@@ -50,13 +50,13 @@
border: 1px solid #f0f0f0;
}
.error-item > button
#genilib-editor-body .error-item > button
{
margin-top: 5px;
margin-right: 5px;
}
#settingsButton
#genilib-editor-body #settingsButton
{
height: 34px;
}
......
<?php
#
# Copyright (c) 2000-2016 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("defs.php3");
chdir("apt");
include("quickvm_sup.php");
include_once("profile_defs.php");
$page_title = "Genilib Editor";
#
# Get current user.
#
RedirectSecure();
$this_user = CheckLogin($check_status);
if (isset($this_user)) {
# Allow unapproved users to edit their profile ...
CheckLoginOrDie(CHECKLOGIN_UNAPPROVED|CHECKLOGIN_NONLOCAL);
}
else {
CheckLoginOrRedirect();
}
#
# Verify page arguments.
#
$optargs = OptionalPageArguments("uuid", PAGEARG_STRING,
"profile",PAGEARG_STRING,
"project",PAGEARG_PROJECT,
"version",PAGEARG_INTEGER);
$canedit = 0;
$disabled = 0;
$source = "";
$profileObject = null;
$profileWho = "private";
if (isset($uuid)) {
$profileObject = Profile::Lookup($uuid);
}
elseif (isset($project) && isset($profile) && isset($version)) {
$profileObject = Profile::LookupByName($project, $profile, $version);
}
elseif (isset($project) && isset($profile)) {
$profileObject = Profile::LookupByName($project, $profile);
}
if (isset($profileObject)) {
$source = $profileObject->script();
$canedit = ($profileObject->CanEdit($this_user) ? 1 : 0);
$disabled = ($profileObject->isDisabled() ? 1 : 0);
if ($profileObject->shared()) {
$profileWho = "shared";
}
}
# We use a session. in case we need to do verification
session_start();
session_unset();
SPITHEADER(1);
echo "<script>\n";
if (isset($profileObject)) {
echo "window.PROFILE_NAME = '" . $profileObject->name() . "';";
echo "window.PROFILE_PROJECT = '" . $profileObject->pid() . "';";
echo "window.PROFILE_VERSION_UUID = '" . $profileObject->uuid() . "';";
echo "window.PROFILE_LATEST_UUID = '" . $profileObject->profile_uuid() . "';";
echo "window.PROFILE_WHO = '" . $profileWho . "';";
}
echo "window.PROFILE_CANEDIT = $canedit;\n";
echo "window.PROFILE_DISABLED = $disabled;\n";
#echo "window.APT_OPTIONS.nopassword = $nopassword;\n";
echo "</script>\n";
echo "<link rel='stylesheet' href='css/bootstrap-formhelpers.min.css'>\n";
echo "<link rel='stylesheet' href='css/genilib-editor.css'>\n";
echo "<div id='page-body'></div>\n";
echo "<div id='oops_div'></div>\n";
echo "<div id='waitwait_div'></div>\n";
echo "<script type='text/plain' id='source'>\n";
echo base64_encode($source);
#echo htmlentities(json_encode($defaults)) . "\n";
echo "</script>\n";
# Pass project list through. Need to convert to list without groups.
# When editing, pass through a single value. The template treats a
# a single value as a read-only field.
$projlist = $this_user->ProjectAccessList($TB_PROJECT_CREATEEXPT);
$plist = array();
while (list($proj) = each($projlist)) {
$plist[] = $proj;
}
echo "<script type='text/plain' id='projects-json'>\n";
echo htmlentities(json_encode($plist));
echo "</script>\n";
echo "<script src='js/lib/jquery-2.0.3.min.js'></script>\n";
echo "<script src='https://cdn.jsdelivr.net/ace/1.2.3/noconflict/ace.js'></script>\n";
echo "<script src='https://cdn.jsdelivr.net/ace/1.2.3/noconflict/keybinding-vim.js'></script>\n";
echo "<script src='https://cdn.jsdelivr.net/ace/1.2.3/noconflict/keybinding-emacs.js'></script>\n";
REQUIRE_UNDERSCORE();
REQUIRE_SUP();
REQUIRE_APTFORMS();
REQUIRE_JACKS();
SPITREQUIRE("js/genilib-editor.js");
AddTemplateList(array("genilib-editor", "oops-modal", "waitwait-modal", "manage-profile"));
SPITFOOTER();
?>
......@@ -2,12 +2,10 @@ $(function ()
{
'use strict';
var templates = APT_OPTIONS.fetchTemplateList(['genilib-editor', 'oops-modal', 'waitwait-modal', 'manage-profile']);
var templates = APT_OPTIONS.fetchTemplateList(['genilib-editor']);
var pageString = templates['genilib-editor'];
var oopsString = templates['oops-modal'];
var waitwaitString = templates['waitwait-modal'];
var manageString = templates['manage-profile'];
var isRendered = false;
var editor;
var isWaiting = false;
var isSplit = false;
......@@ -17,58 +15,105 @@ $(function ()
var jacks = null;
var jacksInput = null;
var jacksOutput = null;
var manageTemplate = _.template(manageString);
var callback = null;
function initialize()
{
window.APT_OPTIONS.initialize(sup);
// The including page provides us with a top-level hook and an
// oops and waitwait modal to invoke.
$('#genilib-editor-body').hide();
$('#genilib-editor-body').html(pageString);
}
function render()
{
if (! isRendered)
{
isRendered = true;
editor = ace.edit('genilib-editor');
editor.$blockScrolling = Infinity;
editor.setTheme('ace/theme/chrome');
editor.getSession().setUseWrapMode(true);
editor.getSession().setMode('ace/mode/python');
editor.getSession().on('change', editorChanged);
if (window.EDITOR_READONLY)
{
editor.setReadOnly(true);
$('#genilib-editor-body #loadButton').hide();
$('#genilib-editor-body #cancelButton').hide();
$('#genilib-editor-body #okButton').html('Ok');
}
else
{
editor.setReadOnly(false);
$('#genilib-editor-body #loadButton').on('click', load);
$('#genilib-editor-body #cancelButton').on('click', clickCancel);
}
$('#page-body').html(pageString);
$('#oops_div').html(oopsString);
$('#waitwait_div').html(waitwaitString);
editor = ace.edit('editor');
editor.setTheme('ace/theme/chrome');
editor.getSession().setMode('ace/mode/python');
editor.getSession().on('change', editorChanged);
removeSplit();
removeSplit();
loadSettings();
loadSettings();
sup.DownloadOnClick($('#genilib-editor-body #saveButton'), getSaveText, 'saved.py', saveComplete);
$('#genilib-editor-body #runButton').on('click', run);
$('#genilib-editor-body #settingsButton').on('click', toggleSettings);
$('#genilib-editor-body #closeErrorButton').on('click', removeSplit);
$('#genilib-editor-body #closeSettingsButton').on('click', removeSplit);
$('#genilib-editor-body #closeJacksButton').on('click', removeSplit);
$('#genilib-editor-body #settings-root select').on('change', onChangeSettings);
$('#genilib-editor-body #okButton').on('click', clickOk);
}
}
var source = document.getElementById('source').innerHTML;
editor.setValue(atob(source));
// Hide the current page (#page-body) and show the genilib editor.
// 'source' is a plaintext genilib source code string
// 'callback' is called when the user clicks ok or cancel with either the new source (if they clicked ok in edit mode) or null (if they clicked cancel or are in readonly mode).
window.SHOW_GENILIB_EDITOR = function (source, newCallback)
{
callback = newCallback;
$('#page-body').hide();
$('#genilib-editor-body').show();
render();
editor.setValue(source);
editor.selection.clearSelection();
$(window).off('beforeunload.portal');
$('#waitwait-modal').modal({ backdrop: 'static', keyboard: false, show: false });
sup.DownloadOnClick($('#saveButton'), getSaveText, 'saved.py', saveComplete);
$('#loadButton').on('click', load);
$('#runButton').on('click', clickRun);
$('#settingsButton').on('click', toggleSettings);
$('#closeErrorButton').on('click', removeSplit);
$('#closeSettingsButton').on('click', removeSplit);
$('#closeJacksButton').on('click', removeSplit);
$('#settings-root select').on('change', onChangeSettings);
$('#createButton').on('click', clickCreate);
$('#closeCreateButton').on('click', removeSplit);
if (window.PROFILE_CANEDIT && window.PROFILE_NAME && window.PROFILE_PROJECT &&
window.PROFILE_VERSION_UUID && window.PROFILE_LATEST_UUID)
}
function clickOk()
{
if (window.EDITOR_READONLY)
{
hideEditor(null);
}
else
{
$('#updateButton').on('click', clickEdit);
$('#updateButton').removeClass('hidden');
hideEditor(editor.getValue());
}
}
function clickCancel()
{
hideEditor(null);
}
function hideEditor(source)
{
$('#genilib-editor-body').hide();
$('#page-body').show();
if (callback)
{
callback(source);
}
}
function removeSplit()
{
$('#jacks-root').hide();
$('#error-root').hide();
$('#create-root').hide();
$('#settings-root').hide();
$('#genilib-editor-body #jacks-root').hide();
$('#genilib-editor-body #error-root').hide();
$('#genilib-editor-body #create-root').hide();
$('#genilib-editor-body #settings-root').hide();
settingsShown = false;
createShown = false;
$('#editor-container')
$('#genilib-editor-body #editor-container')
.removeClass('col-lg-6 col-md-6')
.addClass('col-lg-12 col-md-12');
editor.resize();
......@@ -76,7 +121,7 @@ $(function ()
function addSplit()
{
$('#editor-container')
$('#genilib-editor-body #editor-container')
.removeClass('col-lg-12 col-md-12')
.addClass('col-lg-6 col-md-6');
editor.resize();
......@@ -88,7 +133,6 @@ $(function ()
{
removeSplit();
}
$(window).on('beforeunload.portal', beforeUnload);
}
function getSaveText()
......@@ -105,16 +149,15 @@ $(function ()
function saveComplete()
{
$(window).off('beforeunload.portal');
}
function load()
{
if (! isWaiting)
{
$('#load-input').html('<input type="file"/>');
$('#load-input input').on('change', function () {
var file = $('#load-input input')[0].files[0];
$('#genilib-editor-body #load-input').html('<input type="file"/>');
$('#genilib-editor-body #load-input input').on('change', function () {
var file = $('#genilib-editor-body #load-input input')[0].files[0];
if (file)
{
var reader = new FileReader();
......@@ -122,29 +165,19 @@ $(function ()
var contents = e.target.result;
editor.setValue(contents);
editor.selection.clearSelection();
$(window).off('beforeunload.portal');
removeSplit();
// jacksInput.trigger('change-topology', [{ rspec: contents }]);
};
reader.readAsText(file);
}
});
$('#load-input input').click();
$('#genilib-editor-body #load-input input').click();
}
}
function clickRun()
{
run('test');
}
var runOption = 'test';
function run(newRunOption)
function run()
{
if (! isWaiting)
{
runOption = newRunOption;
removeSplit();
$('#waitwait-modal').modal('show');
isWaiting = true;
......@@ -165,26 +198,13 @@ $(function ()
if (json.code == 0)
{
rspec = json.value;
if (runOption == 'create')
{
$('#create-root').show();
createShown = true;
updateCreateBody();
}
else if (runOption == 'edit')
{
$('#create-root').show();
createShown = true;
updateEditBody();
_.defer(function () { $('#profile_submit_button').click(); });
}
$('#jacks-root').show();
$('#genilib-editor-body #jacks-root').show();
_.defer(jacksUpdate);
addSplit();
}
else if (json.code == 2)
{
$('#error-message').html('');
$('#genilib-editor-body #error-message').html('');
var errors = json.value.split('\n');
for (var i in errors)
{
......@@ -199,10 +219,9 @@ $(function ()
}
item.append('<pre>' + _.escape(errors[i]) + '</pre></div>');
$('#error-message').append(item);
$('#genilib-editor-body #error-message').append(item);
}
// $('#error-message').html(_.escape(json.value));
$('#error-root').show();
$('#genilib-editor-body #error-root').show();
addSplit();
}
else
......@@ -220,16 +239,6 @@ $(function ()
return button;
}
function clickCreate()
{
run('create');
}
function clickEdit()
{
run('edit');
}
function onChangeSettings()
{
var settings = saveSettings();
......@@ -246,10 +255,10 @@ $(function ()
else
{
settingsShown = true;
$('#settings-root').show();
$('#jacks-root').hide();
$('#error-root').hide();
$('#create-root').hide();
$('#genilib-editor-body #settings-root').show();
$('#genilib-editor-body #jacks-root').hide();
$('#genilib-editor-body #error-root').hide();
$('#genilib-editor-body #create-root').hide();
createShown = false;
addSplit();
}
......@@ -283,7 +292,7 @@ $(function ()
function saveSettings()
{
var settings = {};
$('#settings-root select').each(function () {
$('#genilib-editor-body #settings-root select').each(function () {
settings[this.id] = $(this).val();
});
try
......@@ -302,9 +311,9 @@ $(function ()
{
for (var key in settings)
{
if ($('#settings-root').find('#' + key).val() !== settings[key])
if ($('#genilib-editor-body #settings-root').find('#' + key).val() !== settings[key])
{
$('#settings-root').find('#' + key).val(settings[key]);
$('#genilib-editor-body #settings-root').find('#' + key).val(settings[key]);
}
}
if (editor.getTheme() !== 'ace/theme/' + settings['theme'])
......@@ -372,172 +381,5 @@ $(function ()
[{ rspec: rspec }]);
}
function updateCreateBody()
{
var projlist = JSON.parse(_.unescape($('#projects-json')[0].textContent));
var project = undefined;
if (projlist.length == 1)
{
project = projlist[0];
}
else if (window.PROFILE_PROJECT && _.contains(projlist, window.PROFILE_PROJECT))
{
project = window.PROFILE_PROJECT;
}
var fields = {
profile_script: editor.getValue(),
profile_rspec: rspec,
profile_who: 'private',
profile_pid: project
};
var manage_html = manageTemplate({
formfields: fields,
projects: projlist,
title: 'Create Profile',
notifyupdate: false,
viewing: false,
action: 'create',
button_label: 'Create',
candelete: false,
canmodify: false,
canpublish: false,
isadmin: false,
history: false,
activity: false,
manual: false,
copyuuid: null,
snapuuid: null,
general_error: '',
isapt: window.ISAPT,
disabled: true,
versions: [],
withpublishing: false,
genilib_editor: true,
canrepo: false,
fromrepo: false
});
manage_html = aptforms.FormatFormFieldsHorizontal(manage_html,
{"wide": false });
$('#create-body').html(manage_html);
$('#profile_instructions').prop("readonly", true);
$('#profile_description').prop("readonly", true);
$('#profile_submit_button').removeAttr('disabled');
$('#profile_submit_button').on('click', submitCreate);
// This activates the popover subsystem.
$('[data-toggle="popover"]').popover({
trigger: 'hover',
placement: 'auto',
container: 'body',
});
parseRspec();
}
function updateEditBody()
{
var projlist = [window.PROFILE_NAME];