overte-thingvellir/domain-server/resources/web/settings/js/settings.js
2024-06-18 21:08:21 -07:00

1283 lines
46 KiB
JavaScript

$(document).ready(function(){
var qs = (function(a) {
if (a == "") return {};
var b = {};
for (var i = 0; i < a.length; ++i)
{
var p=a[i].split('=', 2);
if (p.length == 1) {
b[p[0]] = "";
} else {
b[p[0]] = decodeURIComponent(p[1].replace(/\+/g, " "));
}
}
return b;
})(window.location.search.substr(1).split('&'));
Settings.extraGroupsAtEnd = Settings.extraDomainGroupsAtEnd;
Settings.extraGroupsAtIndex = Settings.extraDomainGroupsAtIndex;
var METAVERSE_URL = URLs.DEFAULT_METAVERSE_URL;
var SSL_PRIVATE_KEY_FILE_ID = 'ssl-private-key-file';
var SSL_PRIVATE_KEY_CONTENTS_ID = 'key-contents';
var SSL_PRIVATE_KEY_CONTENTS_NAME = 'oauth.key-contents';
var SSL_CERT_UPLOAD_ID = 'ssl-cert-button';
var SSL_CERT_FILE_ID = 'ssl-cert-file';
var SSL_CERT_FINGERPRINT_ID = 'cert-fingerprint';
var SSL_CERT_FINGERPRINT_SPAN_ID = 'cert-fingerprint-span-id';
var SSL_CERT_CONTENTS_ID = 'cert-contents';
var SSL_CERT_CONTENTS_NAME = 'oauth.cert-contents';
var SSL_PRIVATE_KEY_PATH = 'oauth.key';
var SSL_CERT_PATH = 'oauth.cert';
Settings.afterReloadActions = function(data) {
getMetaverseUrl(function(metaverse_url) {
METAVERSE_URL = metaverse_url;
// call our method to setup the HF account button
setupHFAccountButton();
// call our method to setup the place names table
setupPlacesTable();
// hide the places table for now because we do not want that interacted with from the domain-server
$('#' + Settings.PLACES_TABLE_ID).hide();
setupDomainNetworkingSettings();
// setupDomainLabelSetting();
setupSettingsOAuth(data);
setupSettingsBackup();
if (domainIDIsSet()) {
// now, ask the API for what places, if any, point to this domain
reloadDomainInfo();
// we need to ask the API what a shareable name for this domain is
getShareName(function(success, shareName) {
if (success) {
var shareLink = "https://hifi.place/" + shareName;
$('#visit-domain-link').attr("href", shareLink).show();
}
});
} else if (accessTokenIsSet()) {
$('#' + Settings.GET_TEMPORARY_NAME_BTN_ID).show();
}
// append the domain selection modal
appendDomainIDButtons();
handleAction();
});
}
Settings.handlePostSettings = function(formJSON) {
if (!verifyAvatarHeights()) {
return false;
}
// check if we've set the basic http password
if (formJSON["security"]) {
var password = formJSON["security"]["http_password"];
var verify_password = formJSON["security"]["verify_http_password"];
// if they've only emptied out the default password field, we should go ahead and acknowledge
// the verify password field
if (password != undefined && verify_password == undefined) {
verify_password = "";
}
// if we have a password and its verification, convert it to sha256 for comparison
if (password != undefined && verify_password != undefined) {
formJSON["security"]["http_password"] = sha256_digest(password);
formJSON["security"]["verify_http_password"] = sha256_digest(verify_password);
if (password == verify_password) {
delete formJSON["security"]["verify_http_password"];
} else {
bootbox.alert({ "message": "Passwords must match!", "title": "Password Error" });
return false;
}
}
}
console.log("----- handlePostSettings() called ------");
console.log(formJSON);
if (formJSON["security"]) {
var username = formJSON["security"]["http_username"];
var password = formJSON["security"]["http_password"];
if ((password == sha256_digest("")) && (username == undefined || (username && username.length != 0))) {
swalAreYouSure(
"You have entered a blank password with a non-blank username. Are you sure you want to require a blank password?",
"Use blank password",
function() {
swal.close();
formJSON["security"]["http_password"] = "";
postSettings(formJSON);
}
);
return;
}
}
if (formJSON["oauth"]) {
var private_key = formJSON["oauth"]["key-contents"];
var cert = formJSON["oauth"]["cert-contents"];
var oauthErrors = "";
if (private_key != undefined) {
var pattern = /-+BEGIN PRIVATE KEY-+[A-Za-z0-9+/\n=]*-+END PRIVATE KEY-+/m;
if (!pattern.test(private_key)) {
oauthErrors += "Private key must be in PEM format<BR/>";
}
}
if (cert != undefined) {
var pattern = /-+BEGIN CERTIFICATE-+[A-Za-z0-9+/\n=]*-+END CERTIFICATE-+/m;
if (!pattern.test(cert)) {
oauthErrors += "Certificate must be in PEM format<BR/>";
}
}
if ($('#oauth.panel').length) {
if (!$('input[name="oauth.client-id"]').val()) {
oauthErrors += "OAuth requires a client Id.<BR/>";
}
if (!$('input[name="oauth.provider"]').val()) {
oauthErrors += "OAuth requires a provider.<BR/>";
}
if (!$('input[name="oauth.hostname"]').val()) {
oauthErrors += "OAuth requires a hostname.<BR/>";
}
if (!$('input[name="' + SSL_PRIVATE_KEY_PATH + '"]').val() && !$('input[name="' + SSL_PRIVATE_KEY_CONTENTS_NAME + '"]').val()) {
oauthErrors += "OAuth requires an SSL Private Key.<BR/>";
}
if (!$('input[name="' + SSL_CERT_PATH + '"]').val() && !$('input[name="' + SSL_CERT_CONTENTS_NAME + '"]').val()) {
oauthErrors += "OAuth requires an SSL Certificate.<BR/>";
}
if (!$("table[name='oauth.admin-users'] tr.value-row").length &&
!$("table[name='oauth.admin-roles'] tr.value-row").length) {
oauthErrors += "OAuth must have at least one admin user or admin role.<BR/>";
}
}
if (oauthErrors) {
bootbox.alert({ "message": oauthErrors, "title": "OAuth Configuration Error" });
return false;
}
}
postSettings(formJSON);
};
$('#' + Settings.FORM_ID).on('click', '#' + Settings.CREATE_DOMAIN_ID_BTN_ID, function(){
$(this).blur();
showDomainCreationAlert(false);
})
$('#' + Settings.FORM_ID).on('click', '#' + Settings.CHOOSE_DOMAIN_ID_BTN_ID, function(){
$(this).blur();
chooseFromHighFidelityDomains($(this))
});
$('#' + Settings.FORM_ID).on('click', '#' + Settings.GET_TEMPORARY_NAME_BTN_ID, function(){
$(this).blur();
createTemporaryDomain();
});
$('#' + Settings.FORM_ID).on('click', '#' + Settings.DISCONNECT_ACCOUNT_BTN_ID, function(e){
$(this).blur();
disonnectHighFidelityAccount();
e.preventDefault();
});
$('#' + Settings.FORM_ID).on('click', '#' + Settings.CONNECT_ACCOUNT_BTN_ID, function(e){
$(this).blur();
prepareAccessTokenPrompt(function(accessToken) {
// we have an access token - set the access token input with this and save settings
$(Settings.ACCESS_TOKEN_SELECTOR).val(accessToken).change();
saveSettings();
});
});
function accessTokenIsSet() {
if (typeof Settings.data.values.metaverse !== 'undefined' &&
typeof Settings.data.values.metaverse.access_token !== 'undefined') {
return Settings.data.values.metaverse.access_token.length > 0;
} else {
return false;
}
}
function getShareName(callback) {
getDomainFromAPI(function(data){
// check if we have owner_places (for a real domain) or a name (for a temporary domain)
if (data && data.status == "success") {
var shareName;
if (data.domain.default_place_name) {
shareName = data.domain.default_place_name;
} else if (data.domain.name) {
shareName = data.domain.name;
} else if (data.domain.network_address) {
shareName = data.domain.network_address;
if (data.domain.network_port !== 40102) {
shareName += ':' + data.domain.network_port;
}
}
callback(true, shareName);
} else {
callback(false);
}
})
}
function handleAction() {
// check if we were passed an action to handle
var action = qs["action"];
if (action == "share") {
// figure out if we already have a stored domain ID
if (domainIDIsSet()) {
// we need to ask the API what a shareable name for this domain is
getShareName(function(success, shareName){
if (success) {
var shareLink = "hifi://" + shareName;
console.log(shareLink);
// show a dialog with a copiable share URL
swal({
title: "Share",
type: "input",
inputPlaceholder: shareLink,
inputValue: shareLink,
text: "Copy this URL to invite friends to your domain.",
closeOnConfirm: true
});
$('.sweet-alert input').select();
} else {
// show an error alert
swal({
title: '',
type: 'error',
text: "There was a problem retrieving domain information from the Directory Services API.",
confirmButtonText: 'Try again',
showCancelButton: true,
closeOnConfirm: false
}, function(isConfirm){
if (isConfirm) {
// they want to try getting domain share info again
showSpinnerAlert("Requesting domain information...")
handleAction();
} else {
swal.close();
}
});
}
});
} else {
// no domain ID present, just show the share dialog
createTemporaryDomain();
}
}
}
function setupHFAccountButton() {
var hasAccessToken = accessTokenIsSet();
var el;
if (hasAccessToken) {
el = "<p>";
el += "<span class='account-connected-header'>Directory Services Account Connected</span>";
el += "<button id='" + Settings.DISCONNECT_ACCOUNT_BTN_ID + "' class='btn'>Disconnect</button>";
el += "</p>";
el = $(el);
} else {
// setup an object for the settings we want our button to have
var buttonSetting = {
type: 'button',
name: 'connected_account',
label: 'Connected Account',
}
buttonSetting.help = "";
buttonSetting.classes = "btn-primary";
buttonSetting.button_label = "Connect Directory Services Account";
buttonSetting.html_id = Settings.CONNECT_ACCOUNT_BTN_ID;
buttonSetting.href = METAVERSE_URL + "/user/tokens/new?for_domain_server=true";
// since we do not have an access token we change hide domain ID and auto networking settings
// without an access token niether of them can do anything
$("[data-keypath='metaverse.id']").hide();
// use the existing getFormGroup helper to ask for a button
el = viewHelpers.getFormGroup('', buttonSetting, Settings.data.values);
}
// add the button group to the top of the Directory Services panel
$('#metaverse .panel-body').prepend(el);
}
function disonnectHighFidelityAccount() {
// the user clicked on the disconnect account btn - give them a sweet alert to make sure this is what they want to do
swal({
title: "Are you sure?",
text: "This will remove your domain-server OAuth access token."
+ "</br></br>This could cause your domain to appear offline and no longer be reachable via any place names.",
type: "warning",
html: true,
showCancelButton: true
}, function(){
// we need to post to settings to clear the access-token
$(Settings.ACCESS_TOKEN_SELECTOR).val('').change();
// reset the domain id to get a new temporary name
$(Settings.DOMAIN_ID_SELECTOR).val('').change();
saveSettings();
});
}
function showDomainCreationAlert(justConnected) {
swal({
title: 'Create new domain ID',
type: 'input',
text: 'Enter a label for this machine.</br></br>This will help you identify which domain ID belongs to which machine.</br></br>',
showCancelButton: true,
confirmButtonText: "Create",
closeOnConfirm: false,
html: true
}, function (inputValue) {
if (inputValue === false) {
swal.close();
// user cancelled domain ID creation - if we're supposed to save after cancel then save here
if (justConnected) {
saveSettings();
}
} else {
// we're going to change the alert to a new one with a spinner while we create this domain
// showSpinnerAlert('Creating domain ID');
createNewDomainID(inputValue, justConnected);
}
});
}
function createNewDomainID(label, justConnected) {
// get the JSON object ready that we'll use to create a new domain
var domainJSON = {
"label": label
}
$.post("/api/domains", domainJSON, function(data) {
if (data.status === "failure") {
failedToCreateDomainID(data, justConnected);
return;
}
// we successfully created a domain ID, set it on that field
var domainID = data.domain.domainId;
console.log("Setting domain id to ", data, domainID);
$(Settings.DOMAIN_ID_SELECTOR).val(domainID).change();
if (justConnected) {
var successText = Strings.CREATE_DOMAIN_SUCCESS_JUST_CONNECTED
} else {
var successText = Strings.CREATE_DOMAIN_SUCCESS;
}
successText += "</br></br>Click the button below to save your new settings and restart your domain-server.";
// show a sweet alert to say we are all finished up and that we need to save
swal({
title: 'Success!',
type: 'success',
text: successText,
html: true,
confirmButtonText: 'Save'
}, function () {
saveSettings();
});
}, 'json').fail(function (data) {
failedToCreateDomainID(data, justConnected);
});
}
function failedToCreateDomainID(data, justConnected) {
var errorText = "There was a problem creating your new domain ID. Do you want to try again or";
if (data && data.status === "failure") {
errorText = "Error: " + data.error + "</br>Do you want to try again or";
console.log("Error: " + data.error);
} else {
console.log("Error: Failed to post to metaverse.");
}
if (justConnected) {
errorText += " just save your new access token?</br></br>You can always create a new domain ID later.";
} else {
errorText += " cancel?"
}
// we failed to create the new domain ID, show a sweet-alert that lets them try again or cancel
swal({
title: '',
type: 'error',
text: errorText,
html: true,
confirmButtonText: 'Try again',
showCancelButton: true,
closeOnConfirm: false
}, function (isConfirm) {
if (isConfirm) {
// they want to try creating a domain ID again
showDomainCreationAlert(justConnected);
} else {
// they want to cancel
if (justConnected) {
// since they just connected we need to save the access token here
saveSettings();
}
}
});
}
function createDomainSpinner() {
var spinner = '<p class="loading-domain-info-spinner text-center" style="display: none">';
spinner += 'Loading <span class="glyphicon glyphicon-refresh glyphicon-refresh-animate"></span>';
spinner += '</p>';
return spinner;
}
function createDomainLoadingError(message) {
var errorEl = $("<div class='domain-loading-error alert alert-warning' style='display: none'></div>");
errorEl.append(message + " ");
var retryLink = $("<a href='#'>Please click here to try again.</a>");
retryLink.click(function(ev) {
ev.preventDefault();
reloadDomainInfo();
});
errorEl.append(retryLink);
return errorEl;
}
function showOrHideLabel() {
var type = getCurrentDomainIDType();
var shouldShow = accessTokenIsSet() && (type === DOMAIN_ID_TYPE_FULL || type === DOMAIN_ID_TYPE_UNKNOWN);
$(".panel#label").toggle(shouldShow);
$("li a[href='#label']").parent().toggle(shouldShow);
return shouldShow;
}
function setupDomainLabelSetting() {
showOrHideLabel();
var html = "<div>"
html += "<label class='control-label'>Specify a label for your domain</label> <a class='domain-loading-hide' href='#'>Edit</a>";
html += "<input id='network-label' type='text' class='form-control domain-loading-hide' disabled></input>";
html += "</div>";
html = $(html);
html.find('a').click(function(ev) {
ev.preventDefault();
var label = DomainInfo.label === null ? "" : DomainInfo.label;
var modal_body = "<div class='form'>";
modal_body += "<label class='control-label'>Label</label>";
modal_body += "<input type='text' id='domain-label-input' class='form-control' value='" + label + "'>";
modal_body += "<div id='domain-label-error' class='error-message' data-property='label'></div>";
modal_body += "</div>";
var dialog = bootbox.dialog({
title: 'Edit Label',
message: modal_body,
closeButton: false,
onEscape: true,
buttons: [
{
label: 'Cancel',
className: 'edit-label-cancel-btn',
callback: function() {
dialog.modal('hide');
}
},
{
label: 'Save',
className: 'edit-label-save-btn btn btn-primary',
callback: function() {
var data = {
label: $('#domain-label-input').val()
};
$('.edit-label-cancel-btn').attr('disabled', 'disabled');
$('.edit-label-save-btn').attr('disabled', 'disabled');
$('.edit-label-save-btn').html("Saving...");
$('.error-message').hide();
$.ajax({
url: '/api/domains',
type: 'PUT',
data: data,
success: function(xhr) {
dialog.modal('hide');
reloadDomainInfo();
},
error: function(xhr) {
var data = parseJSONResponse(xhr);
console.log(data, data.status, data.data);
if (data.status === "fail") {
for (var key in data.data) {
var errorMsg = data.data[key];
var errorEl = $('.error-message[data-property="' + key + '"');
errorEl.html(errorMsg);
errorEl.show();
}
}
$('.edit-label-cancel-btn').removeAttr('disabled');
$('.edit-label-save-btn').removeAttr('disabled');
$('.edit-label-save-btn').html("Save");
}
});
return false;
}
}
],
callback: function(result) {
console.log("result: ", result);
}
});
});
var spinner = createDomainSpinner();
var errorEl = createDomainLoadingError("Error loading label.");
html.append(spinner);
html.append(errorEl);
$('div#label .panel-body').append(html);
}
function showOrHideAutomaticNetworking() {
var type = getCurrentDomainIDType();
if (!accessTokenIsSet() || (type !== DOMAIN_ID_TYPE_FULL && type !== DOMAIN_ID_TYPE_UNKNOWN)) {
$("[data-keypath='metaverse.automatic_networking']").hide();
return false;
}
$("[data-keypath='metaverse.automatic_networking']").show();
return true;
}
function setupDomainNetworkingSettings() {
if (!showOrHideAutomaticNetworking()) {
return;
}
var autoNetworkingSetting = Settings.data.values.metaverse.automatic_networking;
if (autoNetworkingSetting === 'full') {
return;
}
var includeAddress = autoNetworkingSetting === 'disabled';
if (includeAddress) {
var label = "Network Address:Port";
} else {
var label = "Network Port";
}
var lowerName = name.toLowerCase();
var form = '<div id="network-address-port">';
form += '<label class="control-label">' + label + '</label>';
form += ' <a id="edit-network-address-port" class="domain-loading-hide" href="#">Edit</a>';
form += '<input type="text" class="domain-loading-hide form-control" disabled></input>';
form += '<div class="domain-loading-hide help-block">This defines how nodes will connect to your domain. Since the displayed setting is read back from directory server, it takes some time to update after being edited. You can read more about automatic networking <a href="">here</a>.</div>';
form += '</div>';
form = $(form);
form.find('#edit-network-address-port').click(function(ev) {
ev.preventDefault();
var address = DomainInfo.network_address === null ? '' : DomainInfo.network_address;
var port = DomainInfo.network_port === null ? '' : DomainInfo.network_port;
var modal_body = "<div class='form-group'>";
if (isCloudDomain()) {
modal_body += '<div style="color:red;font-weight: bold">Changing the network settings may actually break your domain.</div>';
}
if (includeAddress) {
modal_body += "<label class='control-label'>Address</label>";
modal_body += "<input type='text' id='network-address-input' class='form-control' value='" + address + "'>";
modal_body += "<div id='network-address-error' class='error-message' data-property='network_address'></div>";
}
modal_body += "<label class='control-label'>Port</label>";
modal_body += "<input type='text' id='network-port-input' class='form-control' value='" + port + "'>";
modal_body += "<div id='network-port-error' class='error-message' data-property='network_port'></div>";
modal_body += "</div>";
var dialog = bootbox.dialog({
title: 'Edit Network',
message: modal_body,
closeButton: false,
onEscape: true,
buttons: [
{
label: 'Cancel',
className: 'edit-network-cancel-btn',
callback: function() {
dialog.modal('hide');
}
},
{
label: 'Save',
className: 'edit-network-save-btn btn btn-primary',
callback: function() {
var data = {
network_port: $('#network-port-input').val()
};
if (includeAddress) {
data.network_address = $('#network-address-input').val();
}
$('.edit-network-cancel-btn').attr('disabled', 'disabled');
$('.edit-network-save-btn').attr('disabled', 'disabled');
$('.edit-network-save-btn').html("Saving...");
console.log('data', data);
$('.error-message').hide();
$.ajax({
url: '/api/domains',
type: 'PUT',
data: data,
success: function(xhr) {
console.log(xhr, parseJSONResponse(xhr));
dialog.modal('hide');
reloadDomainInfo(); // TODO: this one doesn't work since directory server still has old data
setTimeout(function() {
reloadDomainInfo();
}, 16000);
setTimeout(function() {
reloadDomainInfo();
}, 64000);
},
error:function(xhr) {
var data = parseJSONResponse(xhr);
console.log(data, data.status, data.data);
if (data.status === "fail") {
for (var key in data.data) {
var errorMsg = data.data[key];
console.log(key, errorMsg);
var errorEl = $('.error-message[data-property="' + key + '"');
console.log(errorEl);
errorEl.html(errorMsg);
errorEl.show();
}
}
$('.edit-network-cancel-btn').removeAttr('disabled');
$('.edit-network-save-btn').removeAttr('disabled');
$('.edit-network-save-btn').html("Save");
}
});
return false;
}
}
],
callback: function(result) {
console.log("result: ", result);
}
});
});
var spinner = createDomainSpinner();
var errorMessage = ''
if (includeAddress) {
errorMessage = "We were unable to load the network address and port.";
} else {
errorMessage = "We were unable to load the network port."
}
var errorEl = createDomainLoadingError(errorMessage);
var autoNetworkingEl = $('div[data-keypath="metaverse.automatic_networking"]');
autoNetworkingEl.after(spinner);
autoNetworkingEl.after(errorEl);
autoNetworkingEl.after(form);
}
function setupPlacesTable() {
// create a dummy table using our view helper
var placesTableSetting = {
type: 'table',
name: 'places',
label: 'Places',
html_id: Settings.PLACES_TABLE_ID,
help: "To point places to this domain, "
+ " go to the <a href='" + METAVERSE_URL + "/user/places'>Places</a> "
+ "page in your Directory Services account.",
read_only: true,
can_add_new_rows: false,
columns: [
{
"name": "name",
"label": "Name"
},
{
"name": "path",
"label": "Viewpoint or Path"
},
{
"name": "remove",
"label": "",
"class": "buttons"
}
]
}
// get a table for the places
var placesTableGroup = viewHelpers.getFormGroup('', placesTableSetting, Settings.data.values);
// append the places table in the right place
$('#places .panel-body').prepend(placesTableGroup);
var spinner = createDomainSpinner();
$('#' + Settings.PLACES_TABLE_ID).after($(spinner));
var errorEl = createDomainLoadingError("There was an error retrieving your places.");
$("#" + Settings.PLACES_TABLE_ID).after(errorEl);
// DISABLE TEMP PLACE NAME BUTTON...
// var temporaryPlaceButton = dynamicButton(Settings.GET_TEMPORARY_NAME_BTN_ID, 'Get a temporary place name');
// temporaryPlaceButton.hide();
// $('#' + Settings.PLACES_TABLE_ID).after(temporaryPlaceButton);
if (accessTokenIsSet()) {
appendAddButtonToPlacesTable();
}
}
function placeTableRow(name, path, isTemporary, placeID) {
var name_link = "<a href='hifi://" + name + "'>" + (isTemporary ? name + " (temporary)" : name) + "</a>";
function placeEditClicked() {
editHighFidelityPlace(placeID, name, path);
}
function placeDeleteClicked() {
var el = $(this);
var confirmString = Strings.REMOVE_PLACE_TITLE.replace("{{place}}", name);
var dialog = bootbox.dialog({
message: confirmString,
closeButton: false,
onEscape: true,
buttons: [
{
label: Strings.REMOVE_PLACE_CANCEL_BUTTON,
className: "delete-place-cancel-btn",
callback: function() {
dialog.modal('hide');
}
},
{
label: Strings.REMOVE_PLACE_DELETE_BUTTON,
className: "delete-place-confirm-btn btn btn-danger",
callback: function() {
$('.delete-place-cancel-btn').attr('disabled', 'disabled');
$('.delete-place-confirm-btn').attr('disabled', 'disabled');
$('.delete-place-confirm-btn').html(Strings.REMOVE_PLACE_DELETE_BUTTON_PENDING);
sendUpdatePlaceRequest(
placeID,
'',
null,
true,
function() {
reloadDomainInfo();
dialog.modal('hide');
}, function() {
$('.delete-place-cancel-btn').removeAttr('disabled');
$('.delete-place-confirm-btn').removeAttr('disabled');
$('.delete-place-confirm-btn').html(Strings.REMOVE_PLACE_DELETE_BUTTON);
bootbox.alert(Strings.REMOVE_PLACE_ERROR);
});
return false;
}
},
]
});
}
if (isTemporary) {
var editLink = "";
var deleteColumn = "<td class='buttons'></td>";
} else {
var editLink = " <a class='place-edit' href='javascript:void(0);'>Edit</a>";
var deleteColumn = "<td class='buttons'><a class='place-delete glyphicon glyphicon-remove'></a></td>";
}
var row = $("<tr><td>" + name_link + "</td><td>" + path + editLink + "</td>" + deleteColumn + "</tr>");
row.find(".place-edit").click(placeEditClicked);
row.find(".place-delete").click(placeDeleteClicked);
return row;
}
function placeTableRowForPlaceObject(place) {
var placePathOrIndex = (place.path ? place.path : "/");
return placeTableRow(place.name, placePathOrIndex, false, place.id);
}
function reloadDomainInfo() {
$('#' + Settings.PLACES_TABLE_ID + " tbody tr").not('.headers').remove();
$('.domain-loading-show').show();
$('.domain-loading-hide').hide();
$('.domain-loading-error').hide();
$('.loading-domain-info-spinner').show();
$('#' + Settings.PLACES_TABLE_ID).append("<tr colspan=3>Hello</tr>");
getDomainFromAPI(function(data){
$('.loading-domain-info-spinner').hide();
$('.domain-loading-show').hide();
// check if we have owner_places (for a real domain) or a name (for a temporary domain)
if (data.status == "success") {
$('#' + Settings.GET_TEMPORARY_NAME_BTN_ID).hide();
$('.domain-loading-hide').show();
if (data.domain.owner_places && data.domain.owner_places.length > 0) {
// add a table row for each of these names
_.each(data.domain.owner_places, function(place){
$('#' + Settings.PLACES_TABLE_ID + " tbody").append(placeTableRowForPlaceObject(place));
});
} else if (data.domain.name) {
// add a table row for this temporary domain name
$('#' + Settings.PLACES_TABLE_ID + " tbody").append(placeTableRow(data.domain.name, '/', true));
} else {
$('#' + Settings.GET_TEMPORARY_NAME_BTN_ID).show();
}
// Update label
if (showOrHideLabel()) {
var label = data.domain.name;
label = label === null ? '' : label;
$('#network-label').val(label);
}
// Update automatic networking
if (showOrHideAutomaticNetworking()) {
var autoNetworkingSetting = Settings.data.values.metaverse.automatic_networking;
var address = data.domain.network_address === null ? "" : data.domain.network_address;
var port = data.domain.network_port === null ? "" : data.domain.network_port;
if (autoNetworkingSetting === 'disabled') {
$('#network-address-port input').val(address + ":" + port);
} else if (autoNetworkingSetting === 'ip') {
$('#network-address-port input').val(port);
}
}
if (getCurrentDomainIDType() === DOMAIN_ID_TYPE_TEMP) {
$(Settings.DOMAIN_ID_SELECTOR).siblings('span').append(" <b>This is a temporary domain and will not be visible in your domain list.</b>");
}
if (accessTokenIsSet()) {
appendAddButtonToPlacesTable();
}
} else {
$('.domain-loading-error').show();
}
})
}
function appendDomainIDButtons() {
var domainIDInput = $(Settings.DOMAIN_ID_SELECTOR);
var createButton = dynamicButton(Settings.CREATE_DOMAIN_ID_BTN_ID, Strings.CREATE_DOMAIN_BUTTON);
createButton.css('margin-top', '10px');
var chooseButton = dynamicButton(Settings.CHOOSE_DOMAIN_ID_BTN_ID, Strings.CHOOSE_DOMAIN_BUTTON);
chooseButton.css('margin', '10px 0px 0px 10px');
domainIDInput.after(chooseButton);
domainIDInput.after(createButton);
}
function editHighFidelityPlace(placeID, name, path) {
var dialog;
var modal_body = "<div class='form-group'>";
modal_body += "<input type='text' id='place-path-input' class='form-control' value='" + path + "'>";
modal_body += "</div>";
var modal_buttons = [
{
label: Strings.EDIT_PLACE_CANCEL_BUTTON,
className: "edit-place-cancel-button",
callback: function() {
dialog.modal('hide');
}
},
{
label: Strings.EDIT_PLACE_CONFIRM_BUTTON,
className: 'edit-place-save-button btn btn-primary',
callback: function() {
var placePath = $('#place-path-input').val();
if (path == placePath) {
return true;
}
$('.edit-place-cancel-button').attr('disabled', 'disabled');
$('.edit-place-save-button').attr('disabled', 'disabled');
$('.edit-place-save-button').html(Strings.EDIT_PLACE_BUTTON_PENDING);
sendUpdatePlaceRequest(
placeID,
placePath,
null,
false,
function() {
dialog.modal('hide')
reloadDomainInfo();
},
function() {
$('.edit-place-cancel-button').removeAttr('disabled');
$('.edit-place-save-button').removeAttr('disabled');
$('.edit-place-save-button').html(Strings.EDIT_PLACE_CONFIRM_BUTTON);
}
);
return false;
}
}
];
dialog = bootbox.dialog({
title: Strings.EDIT_PLACE_TITLE,
closeButton: false,
onEscape: true,
message: modal_body,
buttons: modal_buttons
})
}
function appendAddButtonToPlacesTable() {
var addRow = $("<tr> <td></td> <td></td> <td class='buttons'><a href='#' class='place-add glyphicon glyphicon-plus'></a></td> </tr>");
addRow.find(".place-add").click(function(ev) {
ev.preventDefault();
chooseFromMetaversePlaces(Settings.initialValues.metaverse.access_token, null, function(placeName, newDomainID) {
if (newDomainID) {
Settings.data.values.metaverse.id = newDomainID;
var domainIDEl = $("[data-keypath='metaverse.id']");
domainIDEl.val(newDomainID);
Settings.initialValues.metaverse.id = newDomainID;
badgeForDifferences(domainIDEl);
}
reloadDomainInfo();
});
});
$('#' + Settings.PLACES_TABLE_ID + " tbody").append(addRow);
}
function chooseFromHighFidelityDomains(clickedButton) {
// setup the modal to help user pick their domain
if (Settings.initialValues.metaverse.access_token) {
// add a spinner to the choose button
clickedButton.html("Loading domains...");
clickedButton.attr('disabled', 'disabled');
// get a list of user domains from data-web
$.ajax({
url: "/api/domains",
dataType: 'json',
jsonp: false,
success: function(data){
var modal_buttons = {
cancel: {
label: 'Cancel',
className: 'btn-default'
}
}
if (data.data.domains.length) {
// setup a select box for the returned domains
modal_body = "<p>Choose the Directory Services domain you want this domain-server to represent.<br/>This will set your domain ID on the settings page.</p>";
domain_select = $("<select id='domain-name-select' class='form-control'></select>");
_.each(data.data.domains, function(domain){
var domainString = "";
if (domain.name) {
domainString += '"' + domain.name+ '" - ';
}
domainString += domain.domainId;
domain_select.append("<option value='" + domain.domainId + "'>" + domainString + "</option>");
})
modal_body += "<label for='domain-name-select'>Domains</label>" + domain_select[0].outerHTML
modal_buttons["success"] = {
label: 'Choose domain',
callback: function() {
domainID = $('#domain-name-select').val()
// set the domain ID on the form
$(Settings.DOMAIN_ID_SELECTOR).val(domainID).change();
}
}
} else {
modal_buttons["success"] = {
label: 'Create new domain',
callback: function() {
window.open(METAVERSE_URL + "/user/domains", '_blank');
}
}
modal_body = "<p>You do not have any domains in your Directory Services account." +
"<br/><br/>Go to your domains page to create a new one. Once your domain is created re-open this dialog to select it.</p>"
}
bootbox.dialog({
title: "Choose matching domain",
onEscape: true,
message: modal_body,
buttons: modal_buttons
})
},
error: function() {
bootbox.alert("Failed to retrieve your domains from the Directory Server");
},
complete: function() {
// remove the spinner from the choose button
clickedButton.html("Choose from my domains")
clickedButton.removeAttr('disabled')
}
});
} else {
bootbox.alert({
message: "You must have an access token to query your Directory Services domains.<br><br>" +
"Please follow the instructions on the settings page to add an access token.",
title: "Access token required"
})
}
}
function createTemporaryDomain() {
swal({
title: 'Create temporary place name',
text: "This will create a temporary place name and domain ID"
+ " so other users can easily connect to your domain.</br></br>"
+ "In order to make your domain reachable, this will also enable full automatic networking.",
showCancelButton: true,
confirmButtonText: 'Create',
closeOnConfirm: false,
html: true
}, function(isConfirm){
if (isConfirm) {
showSpinnerAlert('Creating temporary place name');
// make a get request to get a temporary domain
$.post(METAVERSE_URL + '/api/v1/domains/temporary', function(data){
if (data.status == "success") {
var domain = data.data.domain;
// we should have a new domain ID - set it on the domain ID value
$(Settings.DOMAIN_ID_SELECTOR).val(domain.id).change();
// we also need to make sure auto networking is set to full
$('[data-hidden-input="metaverse.automatic_networking"]').val("full").change();
swal({
type: 'success',
title: 'Success!',
text: "We have created a temporary name and domain ID for you.</br></br>"
+ "Your temporary place name is <strong>" + domain.name + "</strong>.</br></br>"
+ "Press the button below to save your new settings and restart your domain-server.",
confirmButtonText: 'Save',
html: true
}, function(){
saveSettings();
});
}
});
}
});
}
function setupSettingsOAuth(data) {
// construct the HTML needed for the settings backup panel
var html = "<div class='form-group undefined'>";
html += "<label class='control-label'>SSL Private Key</label><BR/>";
html += "<label id='key-path-label'class='control-label'>Path</label>";
html += "<input id='" + SSL_PRIVATE_KEY_FILE_ID + "' type='file' accept='.key'/>";
html += "<input id='" + SSL_PRIVATE_KEY_CONTENTS_ID + "' name='" + SSL_PRIVATE_KEY_CONTENTS_NAME + "' type='hidden'/>";
html += "</div>";
html += "<div class='form-group undefined'>";
html += "<label class='control-label'>SSL Cert</label>";
html += "<div id='cert-fingerprint'><b>Fingerprint:</b><span id='" + SSL_CERT_FINGERPRINT_SPAN_ID + "'>" + data.values.oauth["cert-fingerprint"] + "</span></div>";
html += "<label id='cert-path-label' class='control-label'>Path</label>";
html += "<input id='" + SSL_CERT_FILE_ID + "' type='file' accept='.cer,.crt'/>";
html += "<input id='" + SSL_CERT_CONTENTS_ID + "' name='" + SSL_CERT_CONTENTS_NAME + "' type='hidden'/>";
html += "</div>";
$('#oauth-advanced').append(html);
$('#key-path-label').after($('[data-keypath="' + SSL_PRIVATE_KEY_PATH + '"]'));
$('#cert-path-label').after($('[data-keypath="' + SSL_CERT_PATH + '"]'));
$('[name="' + SSL_PRIVATE_KEY_PATH + '"]').val(data.values.oauth.key);
$('[name="' + SSL_CERT_PATH + '"]').val(data.values.oauth.cert);
$('body').on('change input propertychange', '#' + SSL_PRIVATE_KEY_FILE_ID, function(e){
var f = e.target.files[0];
var reader = new FileReader();
reader.onload = function(e) {
$('#' + SSL_PRIVATE_KEY_CONTENTS_ID).val(reader.result);
$('#' + SSL_PRIVATE_KEY_CONTENTS_ID).attr('data-changed', true);
$('[name="' + SSL_PRIVATE_KEY_PATH + '"]').val('');
badgeForDifferences($('#' + SSL_PRIVATE_KEY_CONTENTS_ID));
}
reader.readAsText(f);
});
$('body').on('change input propertychange', '#' + SSL_CERT_FILE_ID, function(e){
var f = e.target.files[0];
var reader = new FileReader();
reader.onload = function(e) {
$('#' + SSL_CERT_CONTENTS_ID).val(reader.result);
$('#' + SSL_CERT_CONTENTS_ID).attr('data-changed', true);
$('[name="' + SSL_CERT_PATH + '"]').val('');
$('#' + SSL_CERT_FINGERPRINT_SPAN_ID).text('');
badgeForDifferences($('#' + SSL_CERT_CONTENTS_ID));
}
reader.readAsText(f);
});
$('body').on('change input propertychange', '[name="' + SSL_PRIVATE_KEY_PATH + '"]', function(e){
$('#' + SSL_PRIVATE_KEY_FILE_ID).val('');
$('#' + SSL_PRIVATE_KEY_CONTENTS_ID).val('');
badgeForDifferences($('[name="' + SSL_PRIVATE_KEY_PATH + '"]').attr('data-changed', true));
});
$('body').on('change input propertychange', '[name="' + SSL_CERT_PATH + '"]', function(e){
$('#' + SSL_CERT_FILE_ID).val('');
$('#' + SSL_CERT_CONTENTS_ID).val('');
$('#' + SSL_CERT_FINGERPRINT_SPAN_ID).text('');
badgeForDifferences($('[name="' + SSL_CERT_PATH + '"]').attr('data-changed', true));
});
}
var RESTORE_SETTINGS_UPLOAD_ID = 'restore-settings-button';
var RESTORE_SETTINGS_FILE_ID = 'restore-settings-file';
// when the restore button is clicked, AJAX send the settings file to DS
// to restore its settings
$('body').on('click', '#' + RESTORE_SETTINGS_UPLOAD_ID, function(e){
e.preventDefault();
swalAreYouSure(
"Your domain settings will be replaced by the uploaded settings",
"Restore settings",
function() {
var files = $('#' + RESTORE_SETTINGS_FILE_ID).prop('files');
var fileFormData = new FormData();
fileFormData.append('restore-file', files[0]);
showSpinnerAlert("Restoring Settings");
$.ajax({
url: '/settings/restore',
type: 'POST',
processData: false,
contentType: false,
dataType: 'json',
data: fileFormData
}).done(function(data, textStatus, jqXHR) {
swal.close();
showRestartModal();
}).fail(function(jqXHR, textStatus, errorThrown) {
showErrorMessage(
"Error",
"There was a problem restoring domain settings.\n"
+ "Please ensure that your current domain settings are valid and try again."
);
reloadSettings();
});
}
);
});
$('body').on('change', '#' + RESTORE_SETTINGS_FILE_ID, function() {
if ($(this).val()) {
$('#' + RESTORE_SETTINGS_UPLOAD_ID).attr('disabled', false);
}
});
function setupSettingsBackup() {
// construct the HTML needed for the settings backup panel
var html = "<div class='form-group'>";
html += "<label class='control-label'>Save Your Settings Configuration</label>";
html += "<span class='help-block'>Download this domain's settings to quickly configure another domain or to restore them later</span>";
html += "<a href='/settings/backup.json' class='btn btn-primary'>Download Domain Settings</a>";
html += "</div>";
html += "<div class='form-group'>";
html += "<label class='control-label'>Upload a Settings Configuration</label>";
html += "<span class='help-block'>Upload a settings configuration to quickly configure this domain";
html += "<br/>Note: Your domain settings will be replaced by the settings you upload</span>";
html += "<input id='restore-settings-file' name='restore-settings' type='file'>";
html += "<button type='button' id='" + RESTORE_SETTINGS_UPLOAD_ID + "' disabled='true' class='btn btn-primary'>Upload Domain Settings</button>";
html += "</div>";
$('#settings_backup .panel-body').html(html);
}
function verifyAvatarHeights() {
var errorString = '';
var minAllowedHeight = 0.009;
var maxAllowedHeight = 1755;
var alertCss = { backgroundColor: '#ffa0a0' };
var minHeightElement = $('input[name="avatars.min_avatar_height"]');
var maxHeightElement = $('input[name="avatars.max_avatar_height"]');
var minHeight = Number(minHeightElement.val());
var maxHeight = Number(maxHeightElement.val());
if (maxHeight < minHeight) {
errorString = 'Maximum avatar height must not be less than minimum avatar height<br>';
minHeightElement.css(alertCss);
maxHeightElement.css(alertCss);
};
if (minHeight < minAllowedHeight) {
errorString += 'Minimum avatar height must not be less than ' + minAllowedHeight + '<br>';
minHeightElement.css(alertCss);
}
if (maxHeight > maxAllowedHeight) {
errorString += 'Maximum avatar height must not be greater than ' + maxAllowedHeight + '<br>';
maxHeightElement.css(alertCss);
}
if (errorString.length > 0) {
swal({
type: 'error',
title: '',
text: errorString,
html: true
});
return false;
} else {
return true;
}
}
});