Commit 964bd051 authored by Leigh Stoller's avatar Leigh Stoller

Refactor and cleanup all of the form formatting code, moving the common

code into aptforms.js. More prep for portalization.
parent 3d35061c
<?php
#
# Copyright (c) 2000-2015 University of Utah and the Flux Group.
# Copyright (c) 2000-2016 University of Utah and the Flux Group.
#
# {{{EMULAB-LICENSE
#
......@@ -130,9 +130,6 @@ function SPITFORM($formfields, $errors)
echo " window.BUTTONLABEL = '$button_label';\n";
echo "</script>\n";
SpitOopsModal("oops");
SpitWaitModal("waitwait");
SPITREQUIRE("create-dataset",
"<script src='js/lib/jquery-ui.js'></script>");
SPITFOOTER();
......
<?php
#
# Copyright (c) 2000-2015 University of Utah and the Flux Group.
# Copyright (c) 2000-2016 University of Utah and the Flux Group.
#
# {{{EMULAB-LICENSE
#
......@@ -119,9 +119,6 @@ function SPITFORM($formfields, $errors)
echo " window.BUTTONLABEL = '$button_label';\n";
echo "</script>\n";
SpitOopsModal("oops");
SpitWaitModal("waitwait");
SPITREQUIRE("create-dataset");
SPITFOOTER();
}
......
<?php
#
# Copyright (c) 2000-2014 University of Utah and the Flux Group.
# Copyright (c) 2000-2016 University of Utah and the Flux Group.
#
# {{{EMULAB-LICENSE
#
......@@ -25,6 +25,7 @@ chdir("..");
include("defs.php3");
chdir("apt");
include("quickvm_sup.php");
$page_title = "Invite a User";
#
# Get current user.
......
//
// Progress Modal
//
define(['underscore', 'js/quickvm_sup'],
function(_, sup)
{
'use strict';
function FormatFormFields(html) {
var root = $(html);
var list = root.find('.format-me');
list.each(function (index, item) {
if (item.dataset) {
var key = item.dataset['key'];
/*
* Wrap in a div we can name. We assume the form
* is already form-group'ed as needed. We attach a
* name to the wrapper so we can find it later to
* add the error stuff.
*/
var wrapper = $("<div id='form-wrapper-' + key></div>");
// How do I just move the item into the wrapper?
wrapper.append($(item).clone());
$(item).after(wrapper);
$(item).remove();
/*
* A normal placeholder can be used, but sometimes
* we want both a placeholder in the input, and a
* label outside of other text.
*/
if (_.has(item.dataset, "label")) {
var label = item.dataset['label'];
wrapper.prepend("<label for='" + key + "' " +
" class='control-label'> " +
_.escape(label) + '</label>');
}
}
});
return root;
}
function FormatFormFieldsHorizontal(html, options) {
var root = $(html);
var list = root.find('.format-me');
var wide = (options && _.has(options, "wide") ? true : false);
list.each(function (index, item) {
if (item.dataset) {
var key = item.dataset['key'];
var margin = 15;
var colsize = 12;
// Squeeze vertical space for this field.
if (_.has(item.dataset, "compact")) {
margin = 5;
}
/*
* Wrap in a div we can name. We assume the form
* is already form-group'ed as needed. We attach a
* name to the wrapper so we can find it later to
* add the error stuff.
*/
var wrapper = $("<div id='form-wrapper-' + key " +
"style='margin-bottom: " + margin +
"px;'></div>");
/*
* A normal placeholder can be used, but sometimes
* we want both a placeholder in the input, and a
* label outside of other text.
*/
if (_.has(item.dataset, "label")) {
var label_text =
"<label for='" + key + "' " +
" class='col-sm-3 control-label'> " +
item.dataset['label'];
if (_.has(item.dataset, "help")) {
label_text = label_text +
"<a href='#' class='btn btn-xs' " +
" data-toggle='popover' " +
" data-html='true' " +
" data-delay='{\"hide\":1000}' " +
" data-content='" + item.dataset['help'] + "'>"+
"<span class='glyphicon " +
" glyphicon-question-sign'>" +
" </span></a>";
}
label_text = label_text + "</label>";
wrapper.append($(label_text));
colsize = (wide ? 9 : 6);
}
var innerdiv =
$("<div class='col-sm-" + colsize + "'></div>");
innerdiv.html($(item).clone());
wrapper.append(innerdiv);
$(item).after(wrapper);
$(item).remove();
}
});
return root;
}
/*
* Add errors to form
*/
function GenerateFormErrors(form, errors) {
$(form).find(".format-me").each(function () {
if (this.dataset) {
var key = this.dataset['key'];
if (errors && _.has(errors, key)) {
$(this).parent().addClass("has-error");
var html =
'<label class="control-label" ' +
' id="label-error-' + key + '" ' +
' for="inputError">' + _.escape(errors[key]) +
'</label>';
$(this).parent().append(html);
}
}
});
}
function ClearFormErrors(form) {
$(form).find(".format-me").each(function () {
if (this.dataset) {
var key = this.dataset['key'];
// Remove the error label by id, that we added above.
if ($(this).parent().hasClass("has-error")) {
$(this).parent()
.find('#' + 'label-error-' + key).remove();
$(this).parent().removeClass("has-error");
}
}
});
}
/*
* Check a form. We add the errors before we return.
*/
function CheckForm(form, route, method, callback) {
/*
* Convert form data into formfields array, like all our
* form handler pages expect.
*/
var formfields = {};
var fields = $(form).serializeArray();
$.each(fields, function(i, field) {
formfields[field.name] = field.value;
});
ClearFormErrors(form);
var checkonly_callback = function(json) {
console.info(json);
/*
* We deal with these errors, the caller handles other errors.
*/
if (json.code == 2) {
GenerateFormErrors(form, json.value);
}
callback(json);
};
var xmlthing =
sup.CallServerMethod(null, route, method,
{"formfields" : formfields,
"checkonly" : 1});
xmlthing.done(checkonly_callback);
}
/*
* Submit form.
*/
function SubmitForm(form, route, method, callback) {
/*
* Convert form data into formfields array, like all our
* form handler pages expect.
*/
var formfields = {};
var fields = $(form).serializeArray();
$.each(fields, function(i, field) {
formfields[field.name] = field.value;
});
var submit_callback = function(json) {
console.info(json);
sup.HideModal("#waitwait-modal");
callback(json);
};
sup.ShowModal("#waitwait-modal");
var xmlthing =
sup.CallServerMethod(null, route, method,
{"formfields" : formfields,
"checkonly" : 0});
xmlthing.done(submit_callback);
}
// Exports from this module.
return {
"FormatFormFields" : FormatFormFields,
"FormatFormFieldsHorizontal" : FormatFormFieldsHorizontal,
"CheckForm" : CheckForm,
"SubmitForm" : SubmitForm,
"GenerateFormErrors" : GenerateFormErrors,
};
}
);
require(window.APT_OPTIONS.configObject,
['underscore', 'js/quickvm_sup', 'moment',
['underscore', 'js/quickvm_sup', 'moment', 'js/aptforms',
'js/lib/text!template/create-dataset.html',
'js/lib/text!template/dataset-help.html'],
function (_, sup, moment, mainString, helpString)
'js/lib/text!template/dataset-help.html',
'js/lib/text!template/oops-modal.html',
'js/lib/text!template/waitwait-modal.html'],
function (_, sup, moment, aptforms,
mainString, helpString, oopsString, waitwaitString)
{
'use strict';
......@@ -36,14 +39,18 @@ function (_, sup, moment, mainString, helpString)
amlist = JSON.parse(_.unescape($('#amlist-json')[0].textContent));
}
}
GeneratePageBody(fields, null);
GeneratePageBody(fields);
// Now we can do this.
$('#oops_div').html(oopsString);
$('#waitwait_div').html(waitwaitString);
}
//
// Moved into a separate function since we want to regen the form
// after each submit, which happens via ajax on this page.
//
function GeneratePageBody(formfields, errors)
function GeneratePageBody(formfields)
{
// Generate the template.
var html = mainTemplate({
......@@ -57,7 +64,7 @@ function (_, sup, moment, mainString, helpString)
editing: editing,
isadmin: isadmin,
});
html = formatter(html, errors).html();
html = aptforms.FormatFormFieldsHorizontal(html);
$('#main-body').html(html);
// This activates the popover subsystem.
......@@ -166,120 +173,41 @@ function (_, sup, moment, mainString, helpString)
//
$('#dataset_submit_button').click(function (event) {
event.preventDefault();
HandleSubmit();
SubmitForm();
});
}
// Formatter for the form. This did not work out nicely at all!
function formatter(fieldString, errors)
{
var root = $(fieldString);
var list = root.find('.format-me');
list.each(function (index, item) {
if (item.dataset) {
var key = item.dataset['key'];
var margin = 15;
var colsize = 12;
var outerdiv = $("<div class='form-group' " +
" style='margin-bottom: " + margin +
"px;'></div>");
if ($(item).attr('data-label')) {
var label_text =
"<label for='" + key + "' " +
" class='col-sm-3 control-label'> " +
item.dataset['label'];
if ($(item).attr('data-help')) {
label_text = label_text +
"<a href='#' class='btn btn-xs' " +
" data-toggle='popover' " +
" data-html='true' " +
" data-delay='{\"hide\":1000}' " +
" data-content='" + item.dataset['help'] + "'>" +
"<span class='glyphicon glyphicon-question-sign'>" +
" </span></a>";
}
label_text = label_text + "</label>";
outerdiv.append($(label_text));
colsize = 6;
}
var innerdiv = $("<div class='col-sm-" + colsize + "'></div>");
innerdiv.html($(item).clone());
if (errors && _.has(errors, key)) {
outerdiv.addClass('has-error');
innerdiv.append('<label class="control-label" ' +
'for="inputError">' +
_.escape(errors[key]) + '</label>');
}
outerdiv.append(innerdiv);
$(item).after(outerdiv);
$(item).remove();
}
});
return root;
}
function HandleSubmit()
{
// Submit with check only at first, since this will return
// very fast, so no need to throw up a waitwait.
SubmitForm(1);
}
//
// Submit the form.
//
function SubmitForm(checkonly)
function SubmitForm()
{
// Current form contents as formfields array.
var formfields = {};
var callback = function(json) {
console.info(json);
if (!checkonly) {
sup.HideModal("#waitwait");
}
var submit_callback = function(json) {
if (json.code) {
if (checkonly && json.code == 2) {
// Regenerate page with errors.
GeneratePageBody(formfields, json.value);
return;
}
sup.SpitOops("oops", json.value);
return;
}
// Now do the actual create.
if (checkonly) {
sup.ShowModal("#waitwait");
SubmitForm(0);
if (embedded) {
window.parent.location.replace("../" + json.value);
}
else {
if (embedded) {
window.parent.location.replace("../" + json.value);
}
else {
window.location.replace(json.value);
window.location.replace(json.value);
}
};
var checkonly_callback = function(json) {
if (json.code) {
if (json.code != 2) {
sup.SpitOops("oops", json.value);
}
return;
}
}
// Convert form data into formfields array, like all our
// form handler pages expect.
var fields = $('#create_dataset_form').serializeArray();
$.each(fields, function(i, field) {
formfields[field.name] = field.value;
});
// This clears any errors before new submit. Needs more thought.
GeneratePageBody(formfields, null);
var xmlthing = sup.CallServerMethod(null, "dataset",
(editing ? "modify" : "create"),
{"formfields" : formfields,
"checkonly" : checkonly,
"embedded" : window.EMBEDDED});
xmlthing.done(callback);
aptforms.SubmitForm('#create_dataset_form', "dataset",
(editing ? "modify" : "create"),
submit_callback);
};
aptforms.CheckForm('#create_dataset_form', "dataset",
(editing ? "modify" : "create"),
checkonly_callback);
}
/*
......
require(window.APT_OPTIONS.configObject,
['underscore', 'js/quickvm_sup',
['underscore', 'js/quickvm_sup', 'js/aptforms',
'js/lib/text!template/invite.html'],
function (_, sup, inviteString)
function (_, sup, aptforms, inviteString)
{
'use strict';
var inviteTemplate = _.template(inviteString);
......@@ -20,33 +20,9 @@ function (_, sup, inviteString)
projects: projlist,
general_error: (errors.error || '')
});
$('#invite-body').html(formatter(invite_html, errors).html());
$('#invite-body').html(aptforms.FormatFormFields(invite_html));
aptforms.GenerateFormErrors('#invite_form', errors);
}
function formatter(fieldString, errors)
{
var root = $(fieldString);
var list = root.find('.format-me');
list.each(function (index, item) {
if (item.dataset) {
var key = item.dataset['key'];
var wrapper = $('<div></div>');
wrapper.addClass('sidebyside-form');
wrapper.addClass('form-group');
wrapper.html($(item).clone());
if (_.has(errors, key))
{
wrapper.addClass('has-error');
wrapper.append('<label class="control-label" ' +
'for="inputError">' + _.escape(errors[key]) +
'</label>');
}
$(item).after(wrapper);
$(item).remove();
}
});
return root;
}
$(document).ready(initialize);
});
require(window.APT_OPTIONS.configObject,
['underscore', 'js/quickvm_sup', 'filesize', 'js/JacksEditor',
'js/image', 'moment', 'js/ppstart',
'js/image', 'moment', 'js/aptforms',
'js/lib/text!template/manage-profile.html',
'js/lib/text!template/waitwait-modal.html',
'js/lib/text!template/renderer-modal.html',
......@@ -13,7 +13,7 @@ require(window.APT_OPTIONS.configObject,
'js/lib/text!template/share-modal.html',
// jQuery modules
'filestyle','marked'],
function (_, sup, filesize, JacksEditor, ShowImagingModal, moment, ppstart,
function (_, sup, filesize, JacksEditor, ShowImagingModal, moment, aptforms,
manageString, waitwaitString,
rendererString, showtopoString, oopsString, rspectextviewString,
guestInstantiateString, publishString, instantiateString,
......@@ -116,8 +116,10 @@ function (_, sup, filesize, JacksEditor, ShowImagingModal, moment, ppstart,
versions: versions,
withpublishing: window.WITHPUBLISHING,
});
manage_html = formatter(manage_html, errors).html();
manage_html = aptforms.FormatFormFieldsHorizontal(manage_html,
{"wide" : true});
$('#manage-body').html(manage_html);
aptforms.GenerateFormErrors('#quickvm_create_profile_form', errors);
var waitwait_html = waitwaitTemplate({});
$('#waitwait_div').html(waitwait_html);
......@@ -400,25 +402,11 @@ function (_, sup, filesize, JacksEditor, ShowImagingModal, moment, ppstart,
});
/*
* The instantiate button. If a plain profile, throw up the
* confirm modal. If a parameterized profile, hand off to the
* ppstart js code.
* The instantiate button.
*/
$('#profile_instantiate_button').click(function (event) {
if (true) {
window.location.replace("instantiate.php?profile=" +
version_uuid);
return;
}
if (isppprofile) {
ppstart({uuid : version_uuid,
registered : true,
amlist : amlist,
amdefault : window.AMDEFAULT,
});
return;
}
sup.ShowModal('#instantiate_modal');
window.location.replace("instantiate.php?profile=" +
version_uuid);
});
/*
......@@ -509,61 +497,6 @@ function (_, sup, filesize, JacksEditor, ShowImagingModal, moment, ppstart,
}
}
// Formatter for the form. This did not work out nicely at all!
function formatter(fieldString, errors)
{
var root = $(fieldString);
var list = root.find('.format-me');
list.each(function (index, item) {
if (item.dataset) {
var key = item.dataset['key'];
var margin = 15;
var colsize = 12;
if ($(item).attr('data-compact')) {
margin = 5;
}
var outerdiv = $("<div class='form-group' " +
" style='margin-bottom: " + margin +
"px;'></div>");
if ($(item).attr('data-label')) {
var label_text =
"<label for='" + key + "' " +
" class='col-sm-2 control-label'> " +
item.dataset['label'];
if ($(item).attr('data-help')) {
label_text = label_text +
"<a href='#' class='btn btn-xs' " +
" data-toggle='popover' " +
" data-html='true' " +
" data-delay='{\"hide\":1000}' " +
" data-content='" + item.dataset['help'] + "'>" +
"<span class='glyphicon glyphicon-question-sign'>" +
" </span></a>";
}
label_text = label_text + "</label>";
outerdiv.append($(label_text));
colsize = 10;
}
var innerdiv = $("<div class='col-sm-" + colsize + "'></div>");
innerdiv.html($(item).clone());
if (_.has(errors, key)) {
outerdiv.addClass('has-error');
innerdiv.append('<label class="control-label" ' +
'for="inputError">' +
_.escape(errors[key]) + '</label>');
}
outerdiv.append(innerdiv);
$(item).after(outerdiv);
$(item).remove();
}
});
return root;
}
/*
* Yank the steps out of the xml and create the editable table.
* Before the form is submitted, we have to convert (update the
......
require(window.APT_OPTIONS.configObject,
['underscore', 'js/quickvm_sup',
['underscore', 'js/quickvm_sup', 'js/aptforms',
'js/lib/text!template/myaccount.html',
'js/lib/text!template/verify-modal.html',
'js/lib/text!template/oops-modal.html',
'js/lib/text!template/waitwait-modal.html',
// jQuery modules
'formhelpers'],
function (_, sup, myaccountString, verifyString, oopsString, waitwaitString)
function (_, sup, aptforms,
myaccountString, verifyString, oopsString, waitwaitString)
{
'use strict';
......@@ -44,7 +45,7 @@ function (_, sup, myaccountString, verifyString, oopsString, waitwaitString)
$('#submit_button').removeAttr("disabled");
}
function renderForm(formfields, errors)
function renderForm(formfields)
{
var verify = verifyTemplate({
id: 'verify_modal',
......@@ -54,12 +55,12 @@ function (_, sup, myaccountString, verifyString, oopsString, waitwaitString)
if (errors && errors.error) {
generror = errors.error;
}
var myaccount = Formatter(myaccountTemplate({
var myaccount = aptforms.FormatFormFields(myaccountTemplate({
formfields: formfields,
general_error: generror,
verify_modal: verify,
nopassword: window.APT_OPTIONS.nopassword,
}), errors);
});
$('#page-body').html(myaccount);
$('#signup_countries').bfhcountries({ country: formfields.country,
......@@ -78,7 +79,7 @@ function (_, sup, myaccountString, verifyString, oopsString, waitwaitString)
event.preventDefault();
// Disable the Stay on Page alert above.
window.onbeforeunload = null;
SubmitForm(1);
SubmitForm();
return false;
});
$('#verify_modal_submit').click(function (event) {
......@@ -86,92 +87,40 @@ function (_, sup, myaccountString, verifyString, oopsString, waitwaitString)
// Disable the Stay on Page alert above.
window.onbeforeunload = null;
sup.HideModal('#verify_modal');
SubmitForm(1);
SubmitForm();
return false;
});
}
function Formatter(fieldString, errors)
{
var root = $(fieldString);
var list = root.find('.format-me');
list.each(function (index, item) {
if (item.dataset)
{
var key = item.dataset['key'];
var wrapper = $('<div></div>');
var placeholder = item.placeholder;
if (!placeholder) {
placeholder = item.dataset['placeholder'];
}
wrapper.append('<label class="control-label"> ' +
_.escape(placeholder) + '</label>');
wrapper.append($(item).clone());
if (errors && _.has(errors, key))
{
wrapper.addClass('has-error');
wrapper.append('<label class="control-label" ' +
'for="inputError">' + _.escape(errors[key]) +
'</label>');
}
$(item).after(wrapper);
$(item).remove();
}
});
return root;
}
//
// Submit the form.
//
function SubmitForm(checkonly)
function SubmitForm()
{
// Current form contents as formfields array.
var formfields = {};
var callback = function(json) {
if (!checkonly) {
sup.HideModal("#waitwait-modal");
var submit_callback = function(json) {