Merge pull request #9554 from birarda/bug/domain-settings-save-row

make table additions in DS settings more obvious
This commit is contained in:
Brad Hefta-Gaub 2017-02-01 07:10:19 -08:00 committed by GitHub
commit 0f4bc1a7c1
2 changed files with 174 additions and 105 deletions

View file

@ -125,6 +125,10 @@ tr.new-row {
background-color: #dff0d8; background-color: #dff0d8;
} }
tr.invalid-input {
background-color: #f2dede;
}
.graphable-stat { .graphable-stat {
text-align: center; text-align: center;
color: #5286BC; color: #5286BC;

View file

@ -38,7 +38,8 @@ var Settings = {
DOMAIN_ID_SELECTOR: '[name="metaverse.id"]', DOMAIN_ID_SELECTOR: '[name="metaverse.id"]',
ACCESS_TOKEN_SELECTOR: '[name="metaverse.access_token"]', ACCESS_TOKEN_SELECTOR: '[name="metaverse.access_token"]',
PLACES_TABLE_ID: 'places-table', PLACES_TABLE_ID: 'places-table',
FORM_ID: 'settings-form' FORM_ID: 'settings-form',
INVALID_ROW_CLASS: 'invalid-input'
}; };
var viewHelpers = { var viewHelpers = {
@ -215,8 +216,8 @@ $(document).ready(function(){
sibling = sibling.next(); sibling = sibling.next();
} }
if (sibling.hasClass(Settings.ADD_DEL_BUTTONS_CLASS)) { // for tables with categories we add the entry and setup the new row on enter
sibling.find('.' + Settings.ADD_ROW_BUTTON_CLASS).click(); if (sibling.find("." + Settings.ADD_CATEGORY_BUTTON_CLASS).length) {
sibling.find("." + Settings.ADD_CATEGORY_BUTTON_CLASS).click(); sibling.find("." + Settings.ADD_CATEGORY_BUTTON_CLASS).click();
// set focus to the first input in the new row // set focus to the first input in the new row
@ -891,10 +892,102 @@ 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 + ':not([data-category]) 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!"; var SETTINGS_ERROR_MESSAGE = "There was a problem saving domain settings. Please try again!";
function saveSettings() { function saveSettings() {
if (validateInputs()) {
// POST the form JSON to the domain-server settings.json endpoint so the settings are saved
// disable any inputs not changed // disable any inputs not changed
$("input:not([data-changed])").each(function(){ $("input:not([data-changed])").each(function(){
$(this).prop('disabled', true); $(this).prop('disabled', true);
@ -904,10 +997,20 @@ function saveSettings() {
var formJSON = form2js('settings-form', ".", false, cleanupFormValues, true); var formJSON = form2js('settings-form', ".", false, cleanupFormValues, true);
// check if we've set the basic http password - if so convert it to base64 // 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);
}
}
// verify that the password and confirmation match before saving
var canPost = true; var canPost = true;
if (formJSON["security"]) { if (formJSON["security"]) {
var password = formJSON["security"]["http_password"]; var password = formJSON["security"]["http_password"];
var verify_password = formJSON["security"]["verify_http_password"]; var verify_password = formJSON["security"]["verify_http_password"];
if (password && password.length > 0) { if (password && password.length > 0) {
if (password != verify_password) { if (password != verify_password) {
bootbox.alert({"message": "Passwords must match!", "title":"Password Error"}); bootbox.alert({"message": "Passwords must match!", "title":"Password Error"});
@ -930,11 +1033,12 @@ function saveSettings() {
// remove focus from the button // remove focus from the button
$(this).blur(); $(this).blur();
// POST the form JSON to the domain-server settings.json endpoint so the settings are saved
if (canPost) { if (canPost) {
// POST the form JSON to the domain-server settings.json endpoint so the settings are saved
postSettings(formJSON); postSettings(formJSON);
} }
} }
}
$('body').on('click', '.save-button', function(e){ $('body').on('click', '.save-button', function(e){
saveSettings(); saveSettings();
@ -1110,8 +1214,9 @@ function makeTable(setting, keypath, setting_value) {
if (setting.can_add_new_categories) { if (setting.can_add_new_categories) {
html += makeTableCategoryInput(setting, numVisibleColumns); html += makeTableCategoryInput(setting, numVisibleColumns);
} }
if (setting.can_add_new_rows || setting.can_add_new_categories) { if (setting.can_add_new_rows || setting.can_add_new_categories) {
html += makeTableInputs(setting, {}, ""); html += makeTableHiddenInputs(setting, {}, "");
} }
} }
html += "</table>" html += "</table>"
@ -1137,7 +1242,7 @@ function makeTableCategoryHeader(categoryKey, categoryValue, numVisibleColumns,
return html; return html;
} }
function makeTableInputs(setting, initialValues, categoryValue) { function makeTableHiddenInputs(setting, initialValues, categoryValue) {
var html = "<tr class='inputs'" + (setting.can_add_new_categories && !categoryValue ? " hidden" : "") + " " + var html = "<tr class='inputs'" + (setting.can_add_new_categories && !categoryValue ? " hidden" : "") + " " +
(categoryValue ? ("data-category='" + categoryValue + "'") : "") + " " + (categoryValue ? ("data-category='" + categoryValue + "'") : "") + " " +
(setting.categorize_by_key ? ("data-keep-field='" + setting.categorize_by_key + "'") : "") + ">"; (setting.categorize_by_key ? ("data-keep-field='" + setting.categorize_by_key + "'") : "") + ">";
@ -1148,7 +1253,7 @@ function makeTableInputs(setting, initialValues, categoryValue) {
if (setting.key) { if (setting.key) {
html += "<td class='key' name='" + setting.key.name + "'>\ html += "<td class='key' name='" + setting.key.name + "'>\
<input type='text' class='form-control' placeholder='" + (_.has(setting.key, 'placeholder') ? setting.key.placeholder : "") + "' value=''>\ <input type='text' style='display: none;' class='form-control' placeholder='" + (_.has(setting.key, 'placeholder') ? setting.key.placeholder : "") + "' value=''>\
</td>" </td>"
} }
@ -1157,14 +1262,14 @@ function makeTableInputs(setting, initialValues, categoryValue) {
if (col.type === "checkbox") { if (col.type === "checkbox") {
html += html +=
"<td class='" + Settings.DATA_COL_CLASS + "'name='" + col.name + "'>" + "<td class='" + Settings.DATA_COL_CLASS + "'name='" + col.name + "'>" +
"<input type='checkbox' class='form-control table-checkbox' " + "<input type='checkbox' style='display: none;' class='form-control table-checkbox' " +
"name='" + col.name + "'" + (defaultValue ? " checked" : "") + "/>" + "name='" + col.name + "'" + (defaultValue ? " checked" : "") + "/>" +
"</td>"; "</td>";
} else { } else {
html += html +=
"<td " + (col.hidden ? "style='display: none;'" : "") + " class='" + Settings.DATA_COL_CLASS + "' " + "<td " + (col.hidden ? "style='display: none;'" : "") + " class='" + Settings.DATA_COL_CLASS + "' " +
"name='" + col.name + "'>" + "name='" + col.name + "'>" +
"<input type='text' class='form-control' placeholder='" + (col.placeholder ? col.placeholder : "") + "' " + "<input type='text' style='display: none;' class='form-control' placeholder='" + (col.placeholder ? col.placeholder : "") + "' " +
"value='" + (defaultValue || "") + "' data-default='" + (defaultValue || "") + "'" + "value='" + (defaultValue || "") + "' data-default='" + (defaultValue || "") + "'" +
(col.readonly ? " readonly" : "") + ">" + (col.readonly ? " readonly" : "") + ">" +
"</td>"; "</td>";
@ -1244,49 +1349,17 @@ function addTableRow(row) {
var columns = row.parent().children('.' + Settings.DATA_ROW_CLASS); var columns = row.parent().children('.' + Settings.DATA_ROW_CLASS);
var input_clone = row.clone();
if (!isArray) { if (!isArray) {
// Check key spaces // show the key input
var key = row.children(".key").children("input").val() var keyInput = row.children(".key").children("input");
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
}
}
// 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 // Change input row to data row
var table = row.parents("table") var table = row.parents("table");
var setting_name = table.attr("name") var setting_name = table.attr("name");
var full_name = setting_name + "." + key row.addClass(Settings.DATA_ROW_CLASS + " " + Settings.NEW_ROW_CLASS);
row.addClass(Settings.DATA_ROW_CLASS + " " + Settings.NEW_ROW_CLASS)
row.removeClass("inputs")
_.each(row.children(), function(element) { _.each(row.children(), function(element) {
if ($(element).hasClass("numbered")) { if ($(element).hasClass("numbered")) {
@ -1308,56 +1381,43 @@ function addTableRow(row) {
anchor.addClass(Settings.DEL_ROW_SPAN_CLASSES) anchor.addClass(Settings.DEL_ROW_SPAN_CLASSES)
} else if ($(element).hasClass("key")) { } else if ($(element).hasClass("key")) {
var input = $(element).children("input") var input = $(element).children("input")
$(element).html(input.val()) input.show();
input.remove()
} else if ($(element).hasClass(Settings.DATA_COL_CLASS)) { } else if ($(element).hasClass(Settings.DATA_COL_CLASS)) {
// Hide inputs // show inputs
var input = $(element).find("input") var input = $(element).find("input");
var isCheckbox = false; input.show();
var isTime = false;
if (input.hasClass("table-checkbox")) {
input = $(input).parent();
isCheckbox = true;
} else if (input.hasClass("table-time")) {
input = $(input).parent();
isTime = true;
}
var val = input.val(); var isCheckbox = input.hasClass("table-checkbox");
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")
}
if (isArray) { if (isArray) {
var row_index = row.siblings('.' + Settings.DATA_ROW_CLASS).length 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? // 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 // with multiple we have an array of Objects, with one we have an array of whatever the value type is
var num_columns = row.children('.' + Settings.DATA_COL_CLASS).length var num_columns = row.children('.' + Settings.DATA_COL_CLASS).length
if (isCheckbox) { if (isCheckbox) {
$(input).find("input").attr("name", setting_name + "[" + row_index + "]" + (num_columns > 1 ? "." + key : "")) input.attr("name", setting_name + "[" + row_index + "]" + (num_columns > 1 ? "." + key : ""))
} else { } else {
input.attr("name", setting_name + "[" + row_index + "]" + (num_columns > 1 ? "." + key : "")) input.attr("name", setting_name + "[" + row_index + "]" + (num_columns > 1 ? "." + key : ""))
} }
} else { } 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) { if (isCheckbox) {
$(input).find("input").attr("data-changed", "true"); $(input).find("input").attr("data-changed", "true");
} else { } else {
input.attr("data-changed", "true"); input.attr("data-changed", "true");
$(element).append(val);
} }
} else { } else {
console.log("Unknown table element") console.log("Unknown table element");
} }
}); });
@ -1387,7 +1447,12 @@ function deleteTableRow($row) {
$row.empty(); $row.empty();
if (!isArray) { if (!isArray) {
if ($row.attr('name')) {
$row.html("<input type='hidden' class='form-control' name='" + $row.attr('name') + "' data-changed='true' value=''>"); $row.html("<input type='hidden' class='form-control' name='" + $row.attr('name') + "' data-changed='true' value=''>");
} else {
// for rows that didn't have a key, simply remove the row
$row.remove();
}
} else { } else {
if ($table.find('.' + Settings.DATA_ROW_CLASS + "[data-category='" + categoryName + "']").length <= 1) { if ($table.find('.' + Settings.DATA_ROW_CLASS + "[data-category='" + categoryName + "']").length <= 1) {
// This is the last row of the category, so delete the header // This is the last row of the category, so delete the header