diff --git a/domain-server/resources/web/css/style.css b/domain-server/resources/web/css/style.css
index ad426671a4..553f408e15 100644
--- a/domain-server/resources/web/css/style.css
+++ b/domain-server/resources/web/css/style.css
@@ -125,6 +125,10 @@ tr.new-row {
background-color: #dff0d8;
}
+tr.invalid-input {
+ background-color: #f2dede;
+}
+
.graphable-stat {
text-align: center;
color: #5286BC;
diff --git a/domain-server/resources/web/settings/js/settings.js b/domain-server/resources/web/settings/js/settings.js
index 659372267c..fbc2aefceb 100644
--- a/domain-server/resources/web/settings/js/settings.js
+++ b/domain-server/resources/web/settings/js/settings.js
@@ -38,14 +38,15 @@ var Settings = {
DOMAIN_ID_SELECTOR: '[name="metaverse.id"]',
ACCESS_TOKEN_SELECTOR: '[name="metaverse.access_token"]',
PLACES_TABLE_ID: 'places-table',
- FORM_ID: 'settings-form'
+ FORM_ID: 'settings-form',
+ INVALID_ROW_CLASS: 'invalid-input'
};
var viewHelpers = {
getFormGroup: function(keypath, setting, values, isAdvanced) {
form_group = "
";
setting_value = _(values).valueForKeyPath(keypath);
@@ -891,23 +892,105 @@ function reloadSettings(callback) {
});
}
+function validateInputs() {
+ // check if any new values are bad
+ var tables = $('table');
+
+ var inputsValid = true;
+
+ var tables = $('table');
+
+ // clear any current invalid rows
+ $('tr.' + Settings.INVALID_ROW_CLASS).removeClass(Settings.INVALID_ROW_CLASS);
+
+ function markParentRowInvalid(rowChild) {
+ $(rowChild).closest('tr').addClass(Settings.INVALID_ROW_CLASS);
+ }
+
+ _.each(tables, function(table) {
+ var inputs = $(table).find('tr.' + Settings.NEW_ROW_CLASS + ' input[data-changed="true"]');
+
+ var empty = false;
+
+ _.each(inputs, function(input){
+ var inputVal = $(input).val();
+
+ if (inputVal.length === 0) {
+ empty = true
+ markParentRowInvalid(input);
+ return;
+ }
+ });
+
+ if (empty) {
+ showErrorMessage("Error", "Empty field(s)");
+ inputsValid = false;
+ return
+ }
+
+ // validate keys specificially for spaces and equality to an existing key
+ var newKeys = $(table).find('tr.' + Settings.NEW_ROW_CLASS + ' td.key');
+
+ var keyWithSpaces = false;
+ var duplicateKey = false;
+
+ _.each(newKeys, function(keyCell) {
+ var keyVal = $(keyCell).children('input').val();
+
+ if (keyVal.indexOf(' ') !== -1) {
+ keyWithSpaces = true;
+ markParentRowInvalid(keyCell);
+ return;
+ }
+
+ // make sure we don't have duplicate keys in the table
+ var otherKeys = $(table).find('td.key').not(keyCell);
+ _.each(otherKeys, function(otherKeyCell) {
+ var keyInput = $(otherKeyCell).children('input');
+
+ if (keyInput.length) {
+ if ($(keyInput).val() == keyVal) {
+ duplicateKey = true;
+ }
+ } else if ($(otherKeyCell).html() == keyVal) {
+ duplicateKey = true;
+ }
+
+ if (duplicateKey) {
+ markParentRowInvalid(keyCell);
+ return;
+ }
+ });
+
+ });
+
+ if (keyWithSpaces) {
+ showErrorMessage("Error", "Key contains spaces");
+ inputsValid = false;
+ return
+ }
+
+ if (duplicateKey) {
+ showErrorMessage("Error", "Two keys cannot be identical");
+ inputsValid = false;
+ return;
+ }
+ });
+
+ return inputsValid;
+}
var SETTINGS_ERROR_MESSAGE = "There was a problem saving domain settings. Please try again!";
function saveSettings() {
- // disable any inputs not changed
- $("input:not([data-changed])").each(function(){
- $(this).prop('disabled', true);
- });
- // grab a JSON representation of the form via form2js
- var formJSON = form2js('settings-form', ".", false, cleanupFormValues, true);
-
- // check if we've set the basic http password - if so convert it to base64
+ // verify that the password and confirmation match before saving
var canPost = true;
+
if (formJSON["security"]) {
var password = formJSON["security"]["http_password"];
var verify_password = formJSON["security"]["verify_http_password"];
+
if (password && password.length > 0) {
if (password != verify_password) {
bootbox.alert({"message": "Passwords must match!", "title":"Password Error"});
@@ -919,23 +1002,46 @@ function saveSettings() {
}
}
- console.log("----- SAVING ------");
- console.log(formJSON);
+ if (canPost && validateInputs()) {
+ // POST the form JSON to the domain-server settings.json endpoint so the settings are saved
- // re-enable all inputs
- $("input").each(function(){
- $(this).prop('disabled', false);
- });
+ // disable any inputs not changed
+ $("input:not([data-changed])").each(function(){
+ $(this).prop('disabled', true);
+ });
- // remove focus from the button
- $(this).blur();
+ // grab a JSON representation of the form via form2js
+ var formJSON = form2js('settings-form', ".", false, cleanupFormValues, true);
- // POST the form JSON to the domain-server settings.json endpoint so the settings are saved
- if (canPost) {
+ // check if we've set the basic http password - if so convert it to base64
+ if (formJSON["security"]) {
+ var password = formJSON["security"]["http_password"];
+ if (password && password.length > 0) {
+ formJSON["security"]["http_password"] = sha256_digest(password);
+ }
+ }
+
+ console.log("----- SAVING ------");
+ console.log(formJSON);
+
+ // re-enable all inputs
+ $("input").each(function(){
+ $(this).prop('disabled', false);
+ });
+
+ // remove focus from the button
+ $(this).blur();
+
+ // POST the form JSON to the domain-server settings.json endpoint so the settings are saved
postSettings(formJSON);
}
}
+// disable any inputs not changed
+$("input:not([data-changed])").each(function(){
+ $(this).prop('disabled', true);
+});
+
$('body').on('click', '.save-button', function(e){
saveSettings();
return false;
@@ -1110,8 +1216,9 @@ function makeTable(setting, keypath, setting_value) {
if (setting.can_add_new_categories) {
html += makeTableCategoryInput(setting, numVisibleColumns);
}
+
if (setting.can_add_new_rows || setting.can_add_new_categories) {
- html += makeTableInputs(setting, {}, "");
+ html += makeTableHiddenInputs(setting, {}, "");
}
}
html += ""
@@ -1137,7 +1244,7 @@ function makeTableCategoryHeader(categoryKey, categoryValue, numVisibleColumns,
return html;
}
-function makeTableInputs(setting, initialValues, categoryValue) {
+function makeTableHiddenInputs(setting, initialValues, categoryValue) {
var html = "
";
@@ -1148,7 +1255,7 @@ function makeTableInputs(setting, initialValues, categoryValue) {
if (setting.key) {
html += "\
- \
+ \
| "
}
@@ -1157,14 +1264,14 @@ function makeTableInputs(setting, initialValues, categoryValue) {
if (col.type === "checkbox") {
html +=
"" +
- "" +
" | ";
} else {
html +=
"" +
- "" +
" | ";
@@ -1244,49 +1351,17 @@ function addTableRow(row) {
var columns = row.parent().children('.' + Settings.DATA_ROW_CLASS);
+ var input_clone = row.clone();
+
if (!isArray) {
- // Check key spaces
- var key = row.children(".key").children("input").val()
- if (key.indexOf(' ') !== -1) {
- showErrorMessage("Error", "Key contains spaces")
- return
- }
- // Check keys with the same name
- var equals = false;
- _.each(columns.children(".key"), function(element) {
- if ($(element).text() === key) {
- equals = true
- return
- }
- })
- if (equals) {
- showErrorMessage("Error", "Two keys cannot be identical")
- return
- }
+ // show the key input
+ var keyInput = row.children(".key").children("input");
}
- // Check empty fields
- var empty = false;
- _.each(row.children('.' + Settings.DATA_COL_CLASS + ' input'), function(element) {
- if ($(element).val().length === 0) {
- empty = true
- return
- }
- })
-
- if (empty) {
- showErrorMessage("Error", "Empty field(s)")
- return
- }
-
- var input_clone = row.clone()
-
// Change input row to data row
- var table = row.parents("table")
- var setting_name = table.attr("name")
- var full_name = setting_name + "." + key
- row.addClass(Settings.DATA_ROW_CLASS + " " + Settings.NEW_ROW_CLASS)
- row.removeClass("inputs")
+ var table = row.parents("table");
+ var setting_name = table.attr("name");
+ row.addClass(Settings.DATA_ROW_CLASS + " " + Settings.NEW_ROW_CLASS);
_.each(row.children(), function(element) {
if ($(element).hasClass("numbered")) {
@@ -1308,34 +1383,17 @@ function addTableRow(row) {
anchor.addClass(Settings.DEL_ROW_SPAN_CLASSES)
} else if ($(element).hasClass("key")) {
var input = $(element).children("input")
- $(element).html(input.val())
- input.remove()
+ input.show();
} else if ($(element).hasClass(Settings.DATA_COL_CLASS)) {
- // Hide inputs
- var input = $(element).find("input")
- var isCheckbox = false;
- var isTime = false;
- if (input.hasClass("table-checkbox")) {
- input = $(input).parent();
- isCheckbox = true;
- } else if (input.hasClass("table-time")) {
- input = $(input).parent();
- isTime = true;
- }
+ // show inputs
+ var input = $(element).find("input");
+ input.show();
- var val = input.val();
- if (isCheckbox) {
- // don't hide the checkbox
- val = $(input).find("input").is(':checked');
- } else if (isTime) {
- // don't hide the time
- } else {
- input.attr("type", "hidden")
- }
+ var isCheckbox = input.hasClass("table-checkbox");
if (isArray) {
var row_index = row.siblings('.' + Settings.DATA_ROW_CLASS).length
- var key = $(element).attr('name')
+ var key = $(element).attr('name');
// are there multiple columns or just one?
// with multiple we have an array of Objects, with one we have an array of whatever the value type is
@@ -1347,17 +1405,21 @@ function addTableRow(row) {
input.attr("name", setting_name + "[" + row_index + "]" + (num_columns > 1 ? "." + key : ""))
}
} else {
- input.attr("name", full_name + "." + $(element).attr("name"))
+ // because the name of the setting in question requires the key
+ // setup a hook to change the HTML name of the element whenever the key changes
+ var colName = $(element).attr("name");
+ keyInput.on('change', function(){
+ input.attr("name", setting_name + "." + $(this).val() + "." + colName);
+ });
}
if (isCheckbox) {
$(input).find("input").attr("data-changed", "true");
} else {
input.attr("data-changed", "true");
- $(element).append(val);
}
} else {
- console.log("Unknown table element")
+ console.log("Unknown table element");
}
});
@@ -1387,7 +1449,12 @@ function deleteTableRow($row) {
$row.empty();
if (!isArray) {
- $row.html("");
+ if ($row.attr('name')) {
+ $row.html("");
+ } else {
+ // for rows that didn't have a key, simply remove the row
+ $row.remove();
+ }
} else {
if ($table.find('.' + Settings.DATA_ROW_CLASS + "[data-category='" + categoryName + "']").length <= 1) {
// This is the last row of the category, so delete the header