mirror of
https://github.com/google/nomulus.git
synced 2025-05-12 22:38:16 +02:00
mv com/google/domain/registry google/registry
This change renames directories in preparation for the great package rename. The repository is now in a broken state because the code itself hasn't been updated. However this should ensure that git correctly preserves history for each file.
This commit is contained in:
parent
a41677aea1
commit
5012893c1d
2396 changed files with 0 additions and 0 deletions
19
java/google/registry/ui/js/BUILD
Normal file
19
java/google/registry/ui/js/BUILD
Normal file
|
@ -0,0 +1,19 @@
|
|||
package(default_visibility = ["//java/com/google/domain/registry:registry_project"])
|
||||
|
||||
load("//third_party/closure/compiler:closure_js_library.bzl", "closure_js_library")
|
||||
|
||||
|
||||
filegroup(
|
||||
name = "js_files",
|
||||
srcs = glob(["*.js"]),
|
||||
)
|
||||
|
||||
closure_js_library(
|
||||
name = "js",
|
||||
srcs = [":js_files"],
|
||||
deps = [
|
||||
"//java/com/google/domain/registry/ui/soy:Console",
|
||||
"//java/com/google/domain/registry/ui/soy:Forms",
|
||||
"//javascript/closure",
|
||||
],
|
||||
)
|
210
java/google/registry/ui/js/component.js
Normal file
210
java/google/registry/ui/js/component.js
Normal file
|
@ -0,0 +1,210 @@
|
|||
// Copyright 2016 The Domain Registry Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
goog.provide('registry.Component');
|
||||
|
||||
goog.require('goog.dom');
|
||||
goog.require('goog.dom.TagName');
|
||||
goog.require('goog.dom.classlist');
|
||||
goog.require('goog.events');
|
||||
goog.require('goog.events.EventHandler');
|
||||
goog.require('goog.events.EventType');
|
||||
goog.require('registry.soy.forms');
|
||||
goog.require('registry.util');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Base component for UI.
|
||||
*
|
||||
* <pre>
|
||||
* ui/js/component.js - Base UI class.
|
||||
* ^
|
||||
* |
|
||||
* edit_item.js - Common controls for editable items.
|
||||
* ^
|
||||
* \
|
||||
* |-ui/js/resource_component.js - JSON resources
|
||||
* | ^
|
||||
* | \
|
||||
* | |- ui/js/registrar/settings.js
|
||||
* |
|
||||
* |-ui/js/registrar/xml_resource_component.js - EPP resources
|
||||
* ^
|
||||
* \
|
||||
* |- ui/js/registrar/contact.js
|
||||
* |- ui/js/registrar/domain.js
|
||||
* |- ui/js/registrar/host.js
|
||||
* </pre>
|
||||
*
|
||||
* @param {!registry.Console} cons the console singleton.
|
||||
* @constructor
|
||||
* @extends {goog.events.EventHandler}
|
||||
*/
|
||||
registry.Component = function(cons) {
|
||||
registry.Component.base(this, 'constructor');
|
||||
|
||||
/** @type {!registry.Console} */
|
||||
this.console = cons;
|
||||
|
||||
/**
|
||||
* The hashPath this component is mapped by. This is set by the
|
||||
* console after construction.
|
||||
* @type {string}
|
||||
*/
|
||||
this.basePath = '';
|
||||
|
||||
/**
|
||||
* Bean counter that's used by {@code addRemBtnHandlers},
|
||||
* e.g. {@code typeCounts['host']++} when user adds or removes.
|
||||
* @type {!Object.<string, number>}
|
||||
* @protected
|
||||
*/
|
||||
this.typeCounts = {};
|
||||
|
||||
/**
|
||||
* Stateful UI/server session model.
|
||||
* @type {?Object.<string, ?>}
|
||||
*/
|
||||
this.model = null;
|
||||
};
|
||||
goog.inherits(registry.Component, goog.events.EventHandler);
|
||||
|
||||
|
||||
/**
|
||||
* Subclasses shold override this to implement panel display.
|
||||
* @param {string} id The target resource id.
|
||||
*/
|
||||
registry.Component.prototype.bindToDom = function(id) {
|
||||
registry.util.unbutter();
|
||||
};
|
||||
|
||||
|
||||
// XXX: Should clean up the many remove button handler setup funcs.
|
||||
/**
|
||||
* Shared functionality for contact and host add button click events
|
||||
* to add form elements for inputing a new item name. Requires the
|
||||
* template to have:
|
||||
*
|
||||
* <ul>
|
||||
* <li>an add button with id="domain-[type]-add-button".
|
||||
* <li>a table row with id="domain-[type]s-footer".
|
||||
* </ul>
|
||||
*
|
||||
* @param {string} type e.g. 'contact', 'host'.
|
||||
* @param {function(): string} newFieldNameFn generates new form field's name.
|
||||
* @param {function(string): (?Element)=} opt_onAddFn currying further setup.
|
||||
* @param {function()=} opt_tmpl input element template.
|
||||
* @param {!Object=} opt_tmplArgs input element template parameter object.
|
||||
* @param {boolean=} opt_disable optionally disable the add button.
|
||||
* @protected
|
||||
*/
|
||||
registry.Component.prototype.addRemBtnHandlers = function(
|
||||
type,
|
||||
newFieldNameFn,
|
||||
opt_onAddFn,
|
||||
opt_tmpl,
|
||||
opt_tmplArgs,
|
||||
opt_disable) {
|
||||
var addBtnId = 'domain-' + type + '-add-button';
|
||||
var addBtn = goog.dom.getRequiredElement(addBtnId);
|
||||
if (!opt_disable) {
|
||||
addBtn.removeAttribute('disabled');
|
||||
}
|
||||
var addButtonClickCallback = function() {
|
||||
var fieldElts = [];
|
||||
var newFieldName = newFieldNameFn();
|
||||
var tmpl =
|
||||
opt_tmpl ? opt_tmpl : registry.soy.forms.inputFieldRow;
|
||||
var tmplArgs = opt_tmplArgs ? opt_tmplArgs : {
|
||||
label: 'New ' + type,
|
||||
name: newFieldName + '.value'
|
||||
};
|
||||
var newFieldInputRow = registry.util.renderBeforeRow(
|
||||
'domain-' + type + 's-footer', tmpl, tmplArgs);
|
||||
fieldElts.push(newFieldInputRow);
|
||||
// Save the add/rem op type as a hidden elt for use by
|
||||
// determine EPP add/remove semantics in subclasses.
|
||||
// e.g. domain.js#saveItem()
|
||||
var opElt = goog.dom.createDom(goog.dom.TagName.INPUT, {
|
||||
'type': 'hidden',
|
||||
'name': newFieldName + '.op',
|
||||
'value': 'add'
|
||||
});
|
||||
newFieldInputRow.lastChild.appendChild(opElt);
|
||||
if (opt_onAddFn) {
|
||||
var elt = opt_onAddFn(newFieldName);
|
||||
if (elt) {
|
||||
fieldElts.push(elt);
|
||||
}
|
||||
}
|
||||
this.appendRemoveBtn(
|
||||
goog.dom.getLastElementChild(newFieldInputRow),
|
||||
fieldElts,
|
||||
goog.bind(function() {
|
||||
this.typeCounts[type]--;
|
||||
}, this));
|
||||
this.typeCounts[type]++;
|
||||
};
|
||||
goog.events.listen(goog.dom.getRequiredElement(addBtnId),
|
||||
goog.events.EventType.CLICK,
|
||||
goog.bind(addButtonClickCallback, this));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Helper for making an element removable.
|
||||
* @param {Element} parent The element to append the remove button to.
|
||||
* @param {(Array.<Element>|function())=} opt_eltsToRemove
|
||||
* Elements to remove when the button is clicked or Function to do
|
||||
* the removing for full customization.
|
||||
* @param {function()=} opt_cb callback will be called if no
|
||||
* customized function is given.
|
||||
*/
|
||||
registry.Component.prototype.appendRemoveBtn =
|
||||
function(parent, opt_eltsToRemove, opt_cb) {
|
||||
var rmBtn = goog.dom.createDom(goog.dom.TagName.BUTTON,
|
||||
goog.getCssName('kd-button'),
|
||||
'Remove');
|
||||
goog.dom.appendChild(parent, rmBtn);
|
||||
var clickCb;
|
||||
if (opt_eltsToRemove instanceof Function) {
|
||||
clickCb = opt_eltsToRemove;
|
||||
} else {
|
||||
var eltsToRemove = opt_eltsToRemove ? opt_eltsToRemove : [parent];
|
||||
clickCb = function() {
|
||||
for (var i = 0; i < eltsToRemove.length; i++) {
|
||||
goog.dom.removeNode(eltsToRemove[i]);
|
||||
}
|
||||
if (opt_cb) {
|
||||
opt_cb();
|
||||
}
|
||||
};
|
||||
}
|
||||
goog.events.listen(rmBtn, goog.events.EventType.CLICK, clickCb, true);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Bind the remove button action for the given container element.
|
||||
* @param {!Element} containerElt
|
||||
*/
|
||||
registry.Component.prototype.enableRemoveButton = function(containerElt) {
|
||||
var rmBtn = goog.dom.getElementByClass(
|
||||
goog.getCssName('remove'), containerElt);
|
||||
goog.dom.classlist.toggle(rmBtn, goog.getCssName('hidden'));
|
||||
goog.events.listen(rmBtn, goog.events.EventType.CLICK, function() {
|
||||
goog.dom.removeNode(goog.dom.getParentElement(rmBtn));
|
||||
});
|
||||
};
|
123
java/google/registry/ui/js/console.js
Normal file
123
java/google/registry/ui/js/console.js
Normal file
|
@ -0,0 +1,123 @@
|
|||
// Copyright 2016 The Domain Registry Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
goog.provide('registry.Console');
|
||||
|
||||
goog.require('goog.Disposable');
|
||||
goog.require('goog.History');
|
||||
goog.require('goog.dom');
|
||||
goog.require('goog.events');
|
||||
goog.require('goog.events.EventType');
|
||||
goog.require('goog.events.KeyCodes');
|
||||
goog.require('goog.history.EventType');
|
||||
goog.require('registry.util');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Abstract console for both admin and registrar console UIs.
|
||||
* @param {?registry.Session} session server request session.
|
||||
* @constructor
|
||||
* @extends {goog.Disposable}
|
||||
*/
|
||||
registry.Console = function(session) {
|
||||
registry.Console.base(this, 'constructor');
|
||||
|
||||
/**
|
||||
* @type {!goog.History}
|
||||
* @protected
|
||||
*/
|
||||
this.history = new goog.History();
|
||||
goog.events.listen(
|
||||
this.history,
|
||||
goog.history.EventType.NAVIGATE,
|
||||
goog.bind(this.handleHashChange, this));
|
||||
|
||||
/**
|
||||
* @type {?registry.Session} The server session.
|
||||
*/
|
||||
this.session = session;
|
||||
|
||||
this.bindToDom();
|
||||
};
|
||||
goog.inherits(registry.Console, goog.Disposable);
|
||||
|
||||
|
||||
/**
|
||||
* Helper to setup permanent page elements.
|
||||
*/
|
||||
registry.Console.prototype.bindToDom = function() {
|
||||
registry.util.unbutter();
|
||||
goog.events.listen(goog.dom.getRequiredElement('kd-searchbutton'),
|
||||
goog.events.EventType.CLICK,
|
||||
goog.bind(this.onSearch_, this));
|
||||
goog.events.listen(goog.dom.getRequiredElement('kd-searchfield'),
|
||||
goog.events.EventType.KEYUP,
|
||||
goog.bind(this.onSearchFieldKeyUp_, this));
|
||||
goog.events.listen(
|
||||
goog.dom.getElementByClass(goog.getCssName('kd-butterbar-dismiss')),
|
||||
goog.events.EventType.CLICK,
|
||||
registry.util.unbutter);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Subclasses should override to visit the hash token given by
|
||||
* {@code goog.History.getToken()}.
|
||||
*/
|
||||
registry.Console.prototype.handleHashChange = goog.abstractMethod;
|
||||
|
||||
|
||||
/**
|
||||
* @param {string} resourcePath Resource description path.
|
||||
*/
|
||||
registry.Console.prototype.view = function(resourcePath) {
|
||||
this.history.setToken(resourcePath);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Handler for search bar.
|
||||
* @private
|
||||
*/
|
||||
registry.Console.prototype.onSearch_ = function() {
|
||||
var qElt = goog.dom.getRequiredElement('kd-searchfield');
|
||||
if (qElt.getAttribute('disabled')) {
|
||||
return;
|
||||
}
|
||||
var query = qElt.value;
|
||||
if (query == '') {
|
||||
return;
|
||||
}
|
||||
// Filtering this value change event.
|
||||
qElt.setAttribute('disabled', true);
|
||||
qElt.value = '';
|
||||
this.view(query);
|
||||
qElt.removeAttribute('disabled');
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Handler for key press in the search input field.
|
||||
* @param {!goog.events.KeyEvent} e Key event to handle.
|
||||
* @return {boolean} Whether the event should be continued or cancelled.
|
||||
* @private
|
||||
*/
|
||||
registry.Console.prototype.onSearchFieldKeyUp_ = function(e) {
|
||||
if (e.keyCode == goog.events.KeyCodes.ENTER) {
|
||||
this.onSearch_();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
294
java/google/registry/ui/js/edit_item.js
Normal file
294
java/google/registry/ui/js/edit_item.js
Normal file
|
@ -0,0 +1,294 @@
|
|||
// Copyright 2016 The Domain Registry Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
goog.provide('registry.EditItem');
|
||||
|
||||
goog.require('goog.dom');
|
||||
goog.require('goog.dom.classlist');
|
||||
goog.require('goog.events');
|
||||
goog.require('goog.events.EventType');
|
||||
goog.require('goog.soy');
|
||||
goog.require('registry.Component');
|
||||
goog.require('registry.soy.console');
|
||||
goog.require('registry.util');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* An editable item, with Edit and Save/Cancel buttons in the appbar.
|
||||
* @param {!registry.Console} cons
|
||||
* @param {function()} itemTmpl
|
||||
* @constructor
|
||||
* @extends {registry.Component}
|
||||
*/
|
||||
registry.EditItem = function(cons, itemTmpl) {
|
||||
registry.EditItem.base(this, 'constructor', cons);
|
||||
|
||||
/**
|
||||
* @type {!Function}
|
||||
*/
|
||||
this.itemTmpl = itemTmpl;
|
||||
|
||||
/**
|
||||
* Optional current target resource id.
|
||||
* @type {?string}
|
||||
*/
|
||||
this.id = null;
|
||||
|
||||
/**
|
||||
* Transitional id for next resource during create.
|
||||
* @type {?string}
|
||||
*/
|
||||
this.nextId = null;
|
||||
};
|
||||
goog.inherits(registry.EditItem, registry.Component);
|
||||
|
||||
|
||||
/** @override */
|
||||
registry.EditItem.prototype.bindToDom = function(id) {
|
||||
registry.EditItem.base(this, 'bindToDom', id);
|
||||
this.id = id;
|
||||
this.nextId = null;
|
||||
this.setupAppbar();
|
||||
};
|
||||
|
||||
|
||||
/** Setup appbar save/edit buttons. */
|
||||
registry.EditItem.prototype.setupAppbar = function() {
|
||||
goog.soy.renderElement(goog.dom.getRequiredElement('reg-appbar'),
|
||||
registry.soy.console.appbarButtons);
|
||||
goog.events.listen(goog.dom.getRequiredElement('reg-app-btn-add'),
|
||||
goog.events.EventType.CLICK,
|
||||
goog.bind(this.add, this));
|
||||
goog.events.listen(goog.dom.getRequiredElement('reg-app-btn-edit'),
|
||||
goog.events.EventType.CLICK,
|
||||
goog.bind(this.edit, this));
|
||||
goog.events.listen(goog.dom.getRequiredElement('reg-app-btn-save'),
|
||||
goog.events.EventType.CLICK,
|
||||
goog.bind(this.save, this));
|
||||
goog.events.listen(goog.dom.getRequiredElement('reg-app-btn-cancel'),
|
||||
goog.events.EventType.CLICK,
|
||||
goog.bind(this.cancel, this));
|
||||
goog.events.listen(goog.dom.getRequiredElement('reg-app-btn-back'),
|
||||
goog.events.EventType.CLICK,
|
||||
goog.bind(this.back, this));
|
||||
if (this.id) {
|
||||
registry.util.setVisible('reg-app-btns-edit', true);
|
||||
} else {
|
||||
registry.util.setVisible('reg-app-btn-add', true);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Retrieve item from server. Overrides should callback to {@code
|
||||
* #handleFetchItem(string, !Object)}.
|
||||
* @param {string} id item id.
|
||||
*/
|
||||
registry.EditItem.prototype.fetchItem = goog.abstractMethod;
|
||||
|
||||
|
||||
/**
|
||||
* Handle result decoding and display.
|
||||
* @param {string} id The requested ID/name, for error message.
|
||||
* @param {!Object} rsp The requested object.
|
||||
*/
|
||||
registry.EditItem.prototype.handleFetchItem = goog.abstractMethod;
|
||||
|
||||
|
||||
/** Subclasses should override to continue processing after fetch. */
|
||||
registry.EditItem.prototype.processItem = function() {};
|
||||
|
||||
|
||||
/**
|
||||
* Show the item.
|
||||
* @param {!Object} objArgs
|
||||
*/
|
||||
registry.EditItem.prototype.renderItem = function(objArgs) {
|
||||
goog.soy.renderElement(goog.dom.getRequiredElement('reg-content'),
|
||||
this.itemTmpl,
|
||||
objArgs);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {boolean} if the component is currently being edited.
|
||||
*/
|
||||
registry.EditItem.prototype.isEditing = function() {
|
||||
return goog.dom.classlist.contains(
|
||||
goog.dom.getElement('reg-app'), goog.getCssName('editing'));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Toggles the editing state of a component. This will first hide the
|
||||
* elements of the {@code shown} class, then adds the {@code editing}
|
||||
* style to the component, then shows the elements that had the {@code
|
||||
* hidden} class.
|
||||
*/
|
||||
registry.EditItem.prototype.toggleEdit = function() {
|
||||
// Toggle appbar buttons.
|
||||
var addBtn = goog.dom.getRequiredElement('reg-app-btn-add');
|
||||
var editBtns = goog.dom.getRequiredElement('reg-app-btns-edit');
|
||||
var saveBtns = goog.dom.getRequiredElement('reg-app-btns-save');
|
||||
var editing = goog.dom.classlist.contains(saveBtns,
|
||||
registry.util.cssShown);
|
||||
if (editing) {
|
||||
registry.util.setVisible(saveBtns, false);
|
||||
if (this.id) {
|
||||
registry.util.setVisible(editBtns, true);
|
||||
} else {
|
||||
registry.util.setVisible(addBtn, true);
|
||||
}
|
||||
} else {
|
||||
if (this.id) {
|
||||
registry.util.setVisible(editBtns, false);
|
||||
} else {
|
||||
registry.util.setVisible(addBtn, false);
|
||||
}
|
||||
registry.util.setVisible(saveBtns, true);
|
||||
}
|
||||
// Then page contents.
|
||||
var parentElt = goog.dom.getElement('reg-content');
|
||||
var shownCssName = goog.getCssName('shown');
|
||||
var hiddenCssName = goog.getCssName('hidden');
|
||||
var shown = goog.dom.getElementsByClass(shownCssName, parentElt);
|
||||
var hidden = goog.dom.getElementsByClass(hiddenCssName, parentElt);
|
||||
|
||||
for (var i = 0; i < shown.length; i++) {
|
||||
goog.dom.classlist.addRemove(shown[i], shownCssName, hiddenCssName);
|
||||
}
|
||||
|
||||
// Then add editing styles.
|
||||
var editingCssName = goog.getCssName('editing');
|
||||
var startingEdit = !goog.dom.classlist.contains(parentElt, editingCssName);
|
||||
if (startingEdit) {
|
||||
goog.dom.classlist.remove(parentElt, editingCssName);
|
||||
} else {
|
||||
goog.dom.classlist.add(parentElt, editingCssName);
|
||||
}
|
||||
|
||||
// The show hiddens.
|
||||
for (var i = 0; i < hidden.length; i++) {
|
||||
goog.dom.classlist.addRemove(hidden[i], hiddenCssName, shownCssName);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Subclasses should override to enhance the default model.
|
||||
* @return {!Object.<string, ?>}
|
||||
*/
|
||||
registry.EditItem.prototype.newModel = function() {
|
||||
return {item: {}};
|
||||
};
|
||||
|
||||
|
||||
// N.B. setting these as abstract precludes their correct binding in
|
||||
// setupAppbar.
|
||||
/** Show add item panel. */
|
||||
registry.EditItem.prototype.add = function() {};
|
||||
|
||||
|
||||
/** Go back from item to collection view. */
|
||||
registry.EditItem.prototype.back = function() {};
|
||||
|
||||
|
||||
/** Sets up initial edit model state and then called edit(objArgs). */
|
||||
registry.EditItem.prototype.edit = function() {
|
||||
var objArgs = this.model;
|
||||
if (objArgs == null) {
|
||||
objArgs = this.newModel();
|
||||
}
|
||||
// XXX: This is vestigial. In the new msg format, this pollutes the
|
||||
// server-model state. Should be carried elsewhere.
|
||||
objArgs.readonly = false;
|
||||
this.renderItem(objArgs);
|
||||
this.setupEditor(objArgs);
|
||||
this.toggleEdit();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Save the current item. Calls either create if creating a new
|
||||
* object or update otherwise.
|
||||
*/
|
||||
registry.EditItem.prototype.save = function() {
|
||||
if (this.model == null) {
|
||||
this.sendCreate();
|
||||
} else {
|
||||
this.sendUpdate();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Resets to non-editing, readonly state, or visits home screen if the
|
||||
* page was in on a create panel.
|
||||
*/
|
||||
registry.EditItem.prototype.cancel = function() {
|
||||
this.toggleEdit();
|
||||
// XXX: The presence of a model is sufficent for non-collection pages, but an
|
||||
// empty id also means go to the collection in collection pages. Should
|
||||
// be simplified.
|
||||
if (this.model && this.id != '') {
|
||||
this.model.readonly = true;
|
||||
this.renderItem(this.model);
|
||||
} else {
|
||||
this.bindToDom('');
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Called after this.renderItem(), to allow for further setup of
|
||||
* editing.
|
||||
* @param {!Object} objArgs
|
||||
*/
|
||||
registry.EditItem.prototype.setupEditor = function(objArgs) {};
|
||||
|
||||
|
||||
// XXX: These should really take @param {object} which is the form. Alas the
|
||||
// only override which doesn't work this way, ResourceComponent.sendCreate
|
||||
// breaks this opportunity. Hmmm...
|
||||
/** Subclasses should extract form values and send them to the server. */
|
||||
registry.EditItem.prototype.sendCreate = goog.abstractMethod;
|
||||
|
||||
|
||||
/** Subclasses should extract form values and send them to the server. */
|
||||
registry.EditItem.prototype.sendUpdate = goog.abstractMethod;
|
||||
|
||||
|
||||
/** Subclasses should extract form values and send them to the server. */
|
||||
registry.EditItem.prototype.sendDelete = goog.abstractMethod;
|
||||
|
||||
|
||||
/**
|
||||
* Sublcasses should override to populate update queryParams with form
|
||||
* fields as needed. {@code queryParams.nextId} MUST be set to the
|
||||
* new object's ID.
|
||||
* @param {!Object} queryParams
|
||||
*/
|
||||
registry.EditItem.prototype.prepareUpdate = goog.abstractMethod;
|
||||
|
||||
|
||||
/**
|
||||
* Subclasses should provide a funtion to parse either XML or JSON response
|
||||
* from server and return a result object as described below.
|
||||
* @param {!Object} rsp Decoded XML/JSON response from the server.
|
||||
* @return {!Object} a result object describing next steps. On
|
||||
* success, if next is defined, visit(ret.next) is called, otherwise
|
||||
* if err is set, the butterbar message is set to it.
|
||||
*/
|
||||
registry.EditItem.prototype.handleUpdateResponse = goog.abstractMethod;
|
135
java/google/registry/ui/js/forms.js
Normal file
135
java/google/registry/ui/js/forms.js
Normal file
|
@ -0,0 +1,135 @@
|
|||
// Copyright 2016 The Domain Registry Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
goog.provide('registry.forms');
|
||||
|
||||
goog.require('goog.array');
|
||||
goog.require('goog.dom');
|
||||
goog.require('goog.dom.TagName');
|
||||
goog.require('goog.dom.classlist');
|
||||
goog.require('goog.dom.forms');
|
||||
goog.require('goog.events');
|
||||
goog.require('goog.events.EventType');
|
||||
goog.require('goog.events.KeyCodes');
|
||||
goog.require('registry.util');
|
||||
|
||||
|
||||
/**
|
||||
* Sets the focus on a form field (if it exists).
|
||||
* @param {Element|string} field Form field (or ID) to focus.
|
||||
*/
|
||||
registry.forms.focus = function(field) {
|
||||
field = goog.dom.getElement(field);
|
||||
if (!goog.isNull(field) && goog.dom.isFocusable(field)) {
|
||||
goog.dom.forms.focusAndSelect(field);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Displays a form field error, or butters if no field is specified.
|
||||
* @param {string} message Human-readable explanation of why this field is evil.
|
||||
* @param {string=} opt_field Erroneous field name.
|
||||
*/
|
||||
registry.forms.displayError = function(message, opt_field) {
|
||||
if (!goog.isDef(opt_field)) {
|
||||
registry.util.butter(message);
|
||||
return;
|
||||
}
|
||||
var input = goog.dom.getElement(opt_field) ||
|
||||
goog.dom.getElement(opt_field + '[0]');
|
||||
// XXX: Transitioning to use of form.eltId instead of DOM id. If DOM id
|
||||
// lookup fails, then search forms for the named field.
|
||||
if (goog.isDefAndNotNull(opt_field) && goog.isNull(input)) {
|
||||
for (var fNdx in document.forms) {
|
||||
var form = document.forms[fNdx];
|
||||
if (form[opt_field]) {
|
||||
input = form[opt_field];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!goog.isNull(input)) {
|
||||
goog.dom.classlist.add(input, goog.getCssName('kd-formerror'));
|
||||
goog.dom.insertSiblingAfter(
|
||||
goog.dom.createDom(goog.dom.TagName.DIV,
|
||||
goog.getCssName('kd-errormessage'),
|
||||
message),
|
||||
input);
|
||||
registry.forms.focus(input);
|
||||
} else {
|
||||
registry.util.butter(opt_field + ': ' + message);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/** Removes error markup from whois settings form. */
|
||||
registry.forms.resetErrors = function() {
|
||||
registry.util.unbutter();
|
||||
goog.array.forEach(
|
||||
goog.dom.getElementsByClass(goog.getCssName('kd-formerror')),
|
||||
function(field) {
|
||||
goog.dom.classlist.remove(field, goog.getCssName('kd-formerror'));
|
||||
});
|
||||
goog.array.forEach(
|
||||
goog.dom.getElementsByClass(goog.getCssName('kd-errormessage')),
|
||||
goog.dom.removeNode);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Adds enter key listeners to all form fields.
|
||||
* @param {!Element} container Parent element containing INPUT fields.
|
||||
* @param {function()} callback Called when enter is pressed in a field.
|
||||
*/
|
||||
registry.forms.listenFieldsOnEnter = function(container, callback) {
|
||||
var handler = goog.partial(registry.forms.onFieldKeyUp_, callback);
|
||||
goog.array.forEach(
|
||||
goog.dom.getElementsByTagNameAndClass(
|
||||
goog.dom.TagName.INPUT, undefined, container),
|
||||
function(field) {
|
||||
goog.events.listen(field, goog.events.EventType.KEYUP, handler);
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Event handler that saves form when enter is pressed in a form field.
|
||||
* @param {function()} callback Called when key pressed is ENTER.
|
||||
* @param {!goog.events.KeyEvent} e Key event to handle.
|
||||
* @return {boolean} Whether the event should be continued or cancelled.
|
||||
* @private
|
||||
*/
|
||||
registry.forms.onFieldKeyUp_ = function(callback, e) {
|
||||
if (e.keyCode == goog.events.KeyCodes.ENTER) {
|
||||
callback();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Toggles disabled state of a button or form element.
|
||||
* @param {!Element} element Form element to disable.
|
||||
* @param {boolean} enabled Enables element if true, or else disables it.
|
||||
*/
|
||||
registry.forms.setEnabled = function(element, enabled) {
|
||||
if (enabled) {
|
||||
goog.dom.classlist.remove(element, goog.getCssName('disabled'));
|
||||
} else {
|
||||
goog.dom.classlist.add(element, goog.getCssName('disabled'));
|
||||
element.blur();
|
||||
}
|
||||
};
|
135
java/google/registry/ui/js/menu_button.js
Normal file
135
java/google/registry/ui/js/menu_button.js
Normal file
|
@ -0,0 +1,135 @@
|
|||
// Copyright 2016 The Domain Registry Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
goog.provide('registry.MenuButton');
|
||||
|
||||
goog.require('goog.array');
|
||||
goog.require('goog.asserts');
|
||||
goog.require('goog.dom');
|
||||
goog.require('goog.dom.classlist');
|
||||
goog.require('goog.events');
|
||||
goog.require('goog.events.EventHandler');
|
||||
goog.require('goog.events.EventType');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Kennedy style menu button.
|
||||
* @param {!Element} button Menu button element.
|
||||
* @constructor
|
||||
* @extends {goog.events.EventHandler}
|
||||
* @final
|
||||
*/
|
||||
registry.MenuButton = function(button) {
|
||||
registry.MenuButton.base(this, 'constructor');
|
||||
|
||||
/**
|
||||
* Outer menu button element.
|
||||
* @private {!Element}
|
||||
* @const
|
||||
*/
|
||||
this.button_ = button;
|
||||
this.listen(button, goog.events.EventType.CLICK, this.onButtonClick_);
|
||||
|
||||
/**
|
||||
* Label that displays currently selected item.
|
||||
* @private {!Element}
|
||||
* @const
|
||||
*/
|
||||
this.label_ =
|
||||
goog.dom.getRequiredElementByClass(goog.getCssName('label'), button);
|
||||
|
||||
/**
|
||||
* List of selectable items.
|
||||
* @private {!Element}
|
||||
* @const
|
||||
*/
|
||||
this.menu_ =
|
||||
goog.dom.getRequiredElementByClass(goog.getCssName('kd-menulist'),
|
||||
button);
|
||||
|
||||
goog.array.forEach(
|
||||
goog.dom.getElementsByClass(goog.getCssName('kd-menulistitem'), button),
|
||||
function(item) {
|
||||
this.listen(item, goog.events.EventType.CLICK, this.onItemClick_);
|
||||
},
|
||||
this);
|
||||
};
|
||||
goog.inherits(registry.MenuButton, goog.events.EventHandler);
|
||||
|
||||
|
||||
/**
|
||||
* Returns selected value in menu.
|
||||
* @return {string}
|
||||
*/
|
||||
registry.MenuButton.prototype.getValue = function() {
|
||||
return goog.dom.getTextContent(
|
||||
goog.dom.getRequiredElementByClass(
|
||||
goog.getCssName('selected'),
|
||||
this.button_));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Main menu button handler.
|
||||
* @param {!goog.events.BrowserEvent} e
|
||||
* @private
|
||||
*/
|
||||
registry.MenuButton.prototype.onButtonClick_ = function(e) {
|
||||
if (goog.dom.classlist.contains(this.button_, goog.getCssName('selected'))) {
|
||||
return;
|
||||
}
|
||||
e.stopPropagation();
|
||||
goog.dom.classlist.add(this.button_, goog.getCssName('selected'));
|
||||
goog.dom.classlist.add(this.menu_, goog.getCssName('shown'));
|
||||
this.listenOnce(goog.dom.getDocument().body, goog.events.EventType.CLICK,
|
||||
this.hideMenu_);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Menu item selection handler.
|
||||
* @param {!goog.events.BrowserEvent} e
|
||||
* @private
|
||||
*/
|
||||
registry.MenuButton.prototype.onItemClick_ = function(e) {
|
||||
e.stopPropagation();
|
||||
if (goog.dom.classlist.contains(this.button_, goog.getCssName('disabled'))) {
|
||||
return;
|
||||
}
|
||||
goog.array.forEach(
|
||||
goog.dom.getElementsByClass(goog.getCssName('kd-menulistitem'),
|
||||
this.button_),
|
||||
function(item) {
|
||||
goog.dom.classlist.remove(item, goog.getCssName('selected'));
|
||||
},
|
||||
this);
|
||||
goog.asserts.assert(e.target instanceof Element);
|
||||
goog.dom.classlist.add(e.target, goog.getCssName('selected'));
|
||||
var text = goog.dom.getTextContent(e.target);
|
||||
goog.dom.setTextContent(this.label_, text);
|
||||
goog.events.fireListeners(this.button_, goog.events.EventType.CHANGE,
|
||||
false, {newValue: text});
|
||||
this.hideMenu_();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Hide the menu.
|
||||
* @private
|
||||
*/
|
||||
registry.MenuButton.prototype.hideMenu_ = function() {
|
||||
goog.dom.classlist.remove(this.menu_, goog.getCssName('shown'));
|
||||
goog.dom.classlist.remove(this.button_, goog.getCssName('selected'));
|
||||
};
|
31
java/google/registry/ui/js/registrar/BUILD
Normal file
31
java/google/registry/ui/js/registrar/BUILD
Normal file
|
@ -0,0 +1,31 @@
|
|||
package(default_visibility = ["//java/com/google/domain/registry:registry_project"])
|
||||
|
||||
load("//third_party/closure/compiler:closure_js_library.bzl", "closure_js_library")
|
||||
|
||||
|
||||
filegroup(
|
||||
name = "js_files",
|
||||
srcs = glob(["*.js"]),
|
||||
)
|
||||
|
||||
closure_js_library(
|
||||
name = "registrar",
|
||||
srcs = [":js_files"],
|
||||
deps = [
|
||||
"//java/com/google/domain/registry/ui/js",
|
||||
"//java/com/google/domain/registry/ui/soy:Forms",
|
||||
"//java/com/google/domain/registry/ui/soy/registrar:Console",
|
||||
"//java/com/google/domain/registry/ui/soy/registrar:Contact",
|
||||
"//java/com/google/domain/registry/ui/soy/registrar:ContactEpp",
|
||||
"//java/com/google/domain/registry/ui/soy/registrar:ContactSettings",
|
||||
"//java/com/google/domain/registry/ui/soy/registrar:Domain",
|
||||
"//java/com/google/domain/registry/ui/soy/registrar:DomainEpp",
|
||||
"//java/com/google/domain/registry/ui/soy/registrar:Epp",
|
||||
"//java/com/google/domain/registry/ui/soy/registrar:Host",
|
||||
"//java/com/google/domain/registry/ui/soy/registrar:HostEpp",
|
||||
"//java/com/google/domain/registry/ui/soy/registrar:Payment",
|
||||
"//java/com/google/domain/registry/ui/soy/registrar:SecuritySettings",
|
||||
"//java/com/google/domain/registry/ui/soy/registrar:WhoisSettings",
|
||||
"//javascript/closure",
|
||||
],
|
||||
)
|
265
java/google/registry/ui/js/registrar/brainframe.js
Normal file
265
java/google/registry/ui/js/registrar/brainframe.js
Normal file
|
@ -0,0 +1,265 @@
|
|||
// Copyright 2016 The Domain Registry Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
goog.provide('registry.registrar.BrainFrame');
|
||||
goog.provide('registry.registrar.BrainFrame.main');
|
||||
|
||||
goog.require('goog.Timer');
|
||||
goog.require('goog.asserts');
|
||||
goog.require('goog.dom');
|
||||
goog.require('goog.dom.TagName');
|
||||
goog.require('goog.events.EventHandler');
|
||||
goog.require('goog.events.EventType');
|
||||
goog.require('goog.json');
|
||||
goog.require('goog.object');
|
||||
goog.require('goog.style');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Sandboxed iframe for Braintree JS SDK v2 iframe.
|
||||
*
|
||||
* <p>This class adds an additional layer of security between the Registrar
|
||||
* Console and JavaScript loaded from Braintree's web server.
|
||||
*
|
||||
* <p>The main function for this class is compiled into a separate binary,
|
||||
* which is loaded within an iframe that's hosted on a different domain than
|
||||
* the production environment. This ensures that cross origin browser security
|
||||
* policies take effect.
|
||||
*
|
||||
* @param {string} origin FQDN of production environment.
|
||||
* @param {string} containerId ID of Braintree container element.
|
||||
* @constructor
|
||||
* @extends {goog.events.EventHandler}
|
||||
* @final
|
||||
*/
|
||||
registry.registrar.BrainFrame = function(origin, containerId) {
|
||||
registry.registrar.BrainFrame.base(this, 'constructor');
|
||||
|
||||
/**
|
||||
* Hostname of production registry, e.g. domain-registry.appspot.com.
|
||||
* @private {string}
|
||||
* @const
|
||||
*/
|
||||
this.origin_ = origin;
|
||||
|
||||
/**
|
||||
* Div that wraps Braintree iframe.
|
||||
* @private {!Element}
|
||||
* @const
|
||||
*/
|
||||
this.container_ = goog.dom.getRequiredElement(containerId);
|
||||
|
||||
/**
|
||||
* Last known height of {@code container_}.
|
||||
* @private {number}
|
||||
*/
|
||||
this.containerHeight_ = 0;
|
||||
|
||||
/**
|
||||
* Timer polling for changes in Braintree iframe height.
|
||||
* @private {!goog.Timer}
|
||||
* @const
|
||||
*/
|
||||
this.resizeTimer_ = new goog.Timer(1000 / 30);
|
||||
this.registerDisposable(this.resizeTimer_);
|
||||
this.listen(this.resizeTimer_, goog.Timer.TICK, this.onResizeTimer_);
|
||||
|
||||
/**
|
||||
* Form that wraps {@code container_}.
|
||||
* @private {?Element}
|
||||
* @const
|
||||
*/
|
||||
this.form_ = goog.dom.getAncestorByTagNameAndClass(this.container_,
|
||||
goog.dom.TagName.FORM);
|
||||
goog.asserts.assert(this.form_ != null);
|
||||
|
||||
/**
|
||||
* State indicating if we're submitting at behest of parent.
|
||||
* @private {boolean}
|
||||
*/
|
||||
this.isSubmitting_ = false;
|
||||
|
||||
this.listen(goog.global.window,
|
||||
goog.events.EventType.MESSAGE,
|
||||
this.onMessage_);
|
||||
};
|
||||
goog.inherits(registry.registrar.BrainFrame, goog.events.EventHandler);
|
||||
|
||||
|
||||
/**
|
||||
* Runs Braintree sandbox environment.
|
||||
*/
|
||||
registry.registrar.BrainFrame.prototype.run = function() {
|
||||
this.send_(
|
||||
'type', registry.registrar.BrainFrame.MessageType.TOKEN_REQUEST);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Handles message from parent iframe which sends Braintree token.
|
||||
* @param {!goog.events.BrowserEvent} e
|
||||
* @private
|
||||
*/
|
||||
registry.registrar.BrainFrame.prototype.onMessage_ = function(e) {
|
||||
var msg = /** @type {!MessageEvent.<string>} */ (e.getBrowserEvent());
|
||||
if (msg.source != goog.global.window.parent) {
|
||||
return;
|
||||
}
|
||||
if (this.origin_ != '*' && this.origin_ != msg.origin) {
|
||||
throw new Error(
|
||||
'Message origin is "' + msg.origin + '" but wanted: ' + this.origin_);
|
||||
}
|
||||
var data = goog.json.parse(msg.data);
|
||||
switch (goog.object.get(data, 'type')) {
|
||||
case registry.registrar.BrainFrame.MessageType.TOKEN_RESPONSE:
|
||||
goog.global.braintree.setup(goog.object.get(data, 'token'), 'dropin', {
|
||||
container: this.container_,
|
||||
onPaymentMethodReceived: goog.bind(this.onPaymentMethod_, this),
|
||||
onReady: goog.bind(this.onReady_, this),
|
||||
onError: goog.bind(this.onError_, this)
|
||||
});
|
||||
this.resizeTimer_.start();
|
||||
break;
|
||||
case registry.registrar.BrainFrame.MessageType.SUBMIT_REQUEST:
|
||||
this.isSubmitting_ = true;
|
||||
// Trigger Braintree JS SDK submit event listener. It does not appear to
|
||||
// be possible to do this using the Closure Library. This is IE 9+ only.
|
||||
this.form_.dispatchEvent(new Event(goog.events.EventType.SUBMIT));
|
||||
break;
|
||||
default:
|
||||
throw Error('Unexpected message: ' + msg.data);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Polls for resizes of Braintree iframe and propagates them to the parent
|
||||
* frame which will then use it to resize this iframe.
|
||||
* @private
|
||||
*/
|
||||
registry.registrar.BrainFrame.prototype.onResizeTimer_ = function() {
|
||||
var height = goog.style.getSize(this.container_).height;
|
||||
if (height != this.containerHeight_) {
|
||||
this.send_(
|
||||
'type', registry.registrar.BrainFrame.MessageType.RESIZE_REQUEST,
|
||||
'height', height);
|
||||
this.containerHeight_ = height;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Callback Braintree iframe has fully loaded.
|
||||
* @private
|
||||
*/
|
||||
registry.registrar.BrainFrame.prototype.onReady_ = function() {
|
||||
this.send_('type', registry.registrar.BrainFrame.MessageType.READY);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Callback Braintree says an error happened.
|
||||
* @param {!braintreepayments.Error} error
|
||||
* @private
|
||||
*/
|
||||
registry.registrar.BrainFrame.prototype.onError_ = function(error) {
|
||||
this.isSubmitting_ = false;
|
||||
this.send_('type', registry.registrar.BrainFrame.MessageType.SUBMIT_ERROR,
|
||||
'message', error.message);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Callback when user successfully gave Braintree payment details.
|
||||
* @param {!braintreepayments.PaymentMethod} pm
|
||||
* @private
|
||||
*/
|
||||
registry.registrar.BrainFrame.prototype.onPaymentMethod_ = function(pm) {
|
||||
// TODO(b/26829319): The Braintree JS SDK does not seem to recognize the
|
||||
// enter key while embedded inside our sandbox iframe. So
|
||||
// at this time, this callback will only be invoked after
|
||||
// we've submitted the form manually at the behest of
|
||||
// payment.js, which means isSubmitting_ will be true.
|
||||
this.send_(
|
||||
'type', registry.registrar.BrainFrame.MessageType.PAYMENT_METHOD,
|
||||
'submit', this.isSubmitting_,
|
||||
'method', pm);
|
||||
this.isSubmitting_ = false;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sends message to parent iframe.
|
||||
* @param {...*} var_args Passed along to {@code goog.object.create}.
|
||||
* @private
|
||||
*/
|
||||
registry.registrar.BrainFrame.prototype.send_ = function(var_args) {
|
||||
goog.asserts.assert(arguments[0] == 'type');
|
||||
registry.registrar.BrainFrame.postMessage_(
|
||||
goog.json.serialize(goog.object.create.apply(null, arguments)),
|
||||
this.origin_);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Delegates to {@code window.parent.postMessage}. This method exists because
|
||||
* IE will not allow us to mock methods on the window object.
|
||||
* @param {string} message
|
||||
* @param {string} origin
|
||||
* @private
|
||||
*/
|
||||
registry.registrar.BrainFrame.postMessage_ = function(message, origin) {
|
||||
goog.global.window.parent.postMessage(message, origin);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Message types passed between brainframe and payment page.
|
||||
* @enum {string}
|
||||
*/
|
||||
registry.registrar.BrainFrame.MessageType = {
|
||||
|
||||
/** Brainframe asks payment page for Braintree token. */
|
||||
TOKEN_REQUEST: 'token_request',
|
||||
|
||||
/** Payment page sends brainframe Braintree token. */
|
||||
TOKEN_RESPONSE: 'token_response',
|
||||
|
||||
/** Brainframe asks payment page to be resized. */
|
||||
RESIZE_REQUEST: 'resize_request',
|
||||
|
||||
/** Brainframe tells payment page it finished loading. */
|
||||
READY: 'ready',
|
||||
|
||||
/** Payment page asks brainframe to submit Braintree payment method form. */
|
||||
SUBMIT_REQUEST: 'submit_request',
|
||||
|
||||
/** Brainframe tells payment page it failed to submit. */
|
||||
SUBMIT_ERROR: 'submit_error',
|
||||
|
||||
/** Brainframe gives payment method info and nonce to payment page. */
|
||||
PAYMENT_METHOD: 'payment_method'
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Entrypoint for {@link registry.registrar.BrainFrame}.
|
||||
* @param {string} origin
|
||||
* @param {string} containerId
|
||||
* @export
|
||||
*/
|
||||
registry.registrar.BrainFrame.main = function(origin, containerId) {
|
||||
new registry.registrar.BrainFrame(origin, containerId).run();
|
||||
};
|
167
java/google/registry/ui/js/registrar/console.js
Normal file
167
java/google/registry/ui/js/registrar/console.js
Normal file
|
@ -0,0 +1,167 @@
|
|||
// Copyright 2016 The Domain Registry Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
goog.provide('registry.registrar.Console');
|
||||
|
||||
goog.require('goog.dispose');
|
||||
goog.require('goog.dom');
|
||||
goog.require('goog.dom.classlist');
|
||||
goog.require('goog.net.XhrIo');
|
||||
goog.require('registry.Console');
|
||||
goog.require('registry.registrar.Contact');
|
||||
goog.require('registry.registrar.ContactSettings');
|
||||
goog.require('registry.registrar.ContactUs');
|
||||
goog.require('registry.registrar.Dashboard');
|
||||
goog.require('registry.registrar.Domain');
|
||||
goog.require('registry.registrar.EppSession');
|
||||
goog.require('registry.registrar.Host');
|
||||
goog.require('registry.registrar.Payment');
|
||||
goog.require('registry.registrar.Resources');
|
||||
goog.require('registry.registrar.SecuritySettings');
|
||||
goog.require('registry.registrar.WhoisSettings');
|
||||
goog.require('registry.util');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* The Registrar Console.
|
||||
* @param {string} xsrfToken Populated by server-side soy template.
|
||||
* @param {string} clientId The logged in GAE user.
|
||||
* @constructor
|
||||
* @extends {registry.Console}
|
||||
* @final
|
||||
*/
|
||||
registry.registrar.Console = function(xsrfToken, clientId) {
|
||||
registry.registrar.Console.base(
|
||||
this, 'constructor',
|
||||
new registry.registrar.EppSession(this, xsrfToken, clientId));
|
||||
|
||||
/**
|
||||
* Component that's currently embedded in the page.
|
||||
* @type {?registry.Component}
|
||||
* @private
|
||||
*/
|
||||
this.component_ = null;
|
||||
|
||||
// XXX: This was in parent ctor but was triggering event dispatching before
|
||||
// ready here.
|
||||
this.history.setEnabled(true);
|
||||
|
||||
/**
|
||||
* @type {!string}
|
||||
* @private
|
||||
*/
|
||||
this.xsrfToken_ = xsrfToken;
|
||||
|
||||
/**
|
||||
* Last active nav element.
|
||||
* @type {Element}
|
||||
*/
|
||||
this.lastActiveNavElt;
|
||||
|
||||
/**
|
||||
* @type {!Object.<string, function(new:registry.Component,
|
||||
* !registry.registrar.Console,
|
||||
* string)>}
|
||||
*/
|
||||
this.pageMap = {};
|
||||
this.pageMap['security-settings'] = registry.registrar.SecuritySettings;
|
||||
this.pageMap['contact-settings'] = registry.registrar.ContactSettings;
|
||||
this.pageMap['whois-settings'] = registry.registrar.WhoisSettings;
|
||||
this.pageMap['contact-us'] = registry.registrar.ContactUs;
|
||||
this.pageMap['resources'] = registry.registrar.Resources;
|
||||
this.pageMap['contact'] = registry.registrar.Contact;
|
||||
this.pageMap['payment'] = registry.registrar.Payment;
|
||||
this.pageMap['domain'] = registry.registrar.Domain;
|
||||
this.pageMap['host'] = registry.registrar.Host;
|
||||
this.pageMap[''] = registry.registrar.Dashboard;
|
||||
};
|
||||
goog.inherits(registry.registrar.Console, registry.Console);
|
||||
|
||||
|
||||
/**
|
||||
* Changes the content area depending on hash path.
|
||||
*
|
||||
* <p>Hash path is expected to be of the form:
|
||||
*
|
||||
* <pre>
|
||||
* #type/id
|
||||
* </pre>
|
||||
*
|
||||
* <p>The {@code id} part may be appended by {@code ()} to specify the target
|
||||
* should be a resource create page.
|
||||
*
|
||||
* @override
|
||||
*/
|
||||
registry.registrar.Console.prototype.handleHashChange = function() {
|
||||
var hashToken = this.history.getToken();
|
||||
// On page reloads, opening a new tab, etc. it's possible that the
|
||||
// session cookie for a logged-in session exists, but the
|
||||
// this.session is not yet aware, so come back here after syncing.
|
||||
//
|
||||
// XXX: Method should be refactored to avoid this 2-stage behavior.
|
||||
if (!this.session.isEppLoggedIn()) {
|
||||
this.session.login(goog.bind(this.handleHashChange, this));
|
||||
return;
|
||||
}
|
||||
|
||||
// Otherwise, a resource operation.
|
||||
var parts = hashToken.split('/');
|
||||
var type = '';
|
||||
var id = '';
|
||||
if (parts.length >= 1) {
|
||||
type = parts[0];
|
||||
}
|
||||
if (parts.length == 2) {
|
||||
id = parts[1];
|
||||
}
|
||||
|
||||
goog.net.XhrIo.cleanup();
|
||||
|
||||
var componentCtor = this.pageMap[type];
|
||||
if (componentCtor == undefined) {
|
||||
componentCtor = this.pageMap[''];
|
||||
}
|
||||
var oldComponent = this.component_;
|
||||
this.component_ = new componentCtor(this, this.xsrfToken_);
|
||||
this.registerDisposable(this.component_);
|
||||
this.component_.basePath = type;
|
||||
this.component_.bindToDom(id);
|
||||
|
||||
this.changeNavStyle();
|
||||
|
||||
goog.dispose(oldComponent);
|
||||
};
|
||||
|
||||
|
||||
/** Change nav style. */
|
||||
registry.registrar.Console.prototype.changeNavStyle = function() {
|
||||
var hashToken = this.history.getToken();
|
||||
// Path except id
|
||||
var slashNdx = hashToken.lastIndexOf('/');
|
||||
slashNdx = slashNdx == -1 ? hashToken.length : slashNdx;
|
||||
var regNavlist = goog.dom.getRequiredElement('reg-navlist');
|
||||
var path = hashToken.substring(0, slashNdx);
|
||||
var active = regNavlist.querySelector('a[href="/registrar#' + path + '"]');
|
||||
if (goog.isNull(active)) {
|
||||
registry.util.log('Unknown path or path form in changeNavStyle.');
|
||||
return;
|
||||
}
|
||||
if (this.lastActiveNavElt) {
|
||||
goog.dom.classlist.remove(
|
||||
this.lastActiveNavElt, goog.getCssName('domain-active-nav'));
|
||||
}
|
||||
goog.dom.classlist.add(active, goog.getCssName('domain-active-nav'));
|
||||
this.lastActiveNavElt = active;
|
||||
};
|
125
java/google/registry/ui/js/registrar/contact.js
Normal file
125
java/google/registry/ui/js/registrar/contact.js
Normal file
|
@ -0,0 +1,125 @@
|
|||
// Copyright 2016 The Domain Registry Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
goog.provide('registry.registrar.Contact');
|
||||
|
||||
goog.require('goog.dom');
|
||||
goog.require('registry.registrar.XmlResourceComponent');
|
||||
goog.require('registry.soy.registrar.contact');
|
||||
goog.require('registry.soy.registrar.contactepp');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* The {@code Contact} class respresents a registry contact object and
|
||||
* binds UI CRUD operations to it.
|
||||
* @param {!registry.registrar.Console} console the
|
||||
* console singleton.
|
||||
* @constructor
|
||||
* @extends {registry.registrar.XmlResourceComponent}
|
||||
* @final
|
||||
*/
|
||||
registry.registrar.Contact = function(console) {
|
||||
registry.registrar.Contact.base(
|
||||
this, 'constructor',
|
||||
registry.soy.registrar.contact.item,
|
||||
registry.soy.registrar.contactepp,
|
||||
console);
|
||||
};
|
||||
goog.inherits(registry.registrar.Contact,
|
||||
registry.registrar.XmlResourceComponent);
|
||||
|
||||
|
||||
/** @override */
|
||||
registry.registrar.Contact.prototype.processItem = function() {
|
||||
this.model.item = this.model['epp']['response']['resData']['contact:infData'];
|
||||
if (!goog.isArray(this.model.item['contact:postalInfo'])) {
|
||||
this.model.item['contact:postalInfo'] =
|
||||
[this.model.item['contact:postalInfo']];
|
||||
}
|
||||
|
||||
// XXX: Is this code necessary?
|
||||
var fixPlus = function(val) {
|
||||
var str = (val || '') + '';
|
||||
if (str == '' || str.match(/\+.*/)) {
|
||||
return str;
|
||||
} else {
|
||||
return '+' + str;
|
||||
}
|
||||
};
|
||||
|
||||
// Both of these are optional.
|
||||
if (this.model.item['contact:voice']) {
|
||||
this.model.item['contact:voice']['keyValue'] =
|
||||
fixPlus(this.model.item['contact:voice']['keyValue']);
|
||||
}
|
||||
if (this.model.item['contact:voice']) {
|
||||
this.model.item['contact:fax']['keyValue'] =
|
||||
fixPlus(this.model.item['contact:fax']['keyValue']);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
registry.registrar.Contact.prototype.setupEditor = function(objArgs) {
|
||||
// For now always keep the first contact and make it i18n. Toggle button
|
||||
// disables to enforce state.
|
||||
//
|
||||
// XXX: Should be simplified to make more modular.
|
||||
var postalElt = goog.dom.getRequiredElement('contact-postalInfo');
|
||||
var addPostalInfoBtn = goog.dom.getRequiredElement(
|
||||
'domain-contact-postalInfo-add-button');
|
||||
this.typeCounts['contact-postalInfo'] = postalElt.childNodes.length;
|
||||
// 4 child nodes means both addresses are present:
|
||||
// 2 data tables, the footer id elt and a hidden input.
|
||||
var setupRemoveBtns = this.typeCounts['contact-postalInfo'] == 4;
|
||||
if (setupRemoveBtns) {
|
||||
this.appendRemoveBtn(/** @type {!Element} */ (postalElt.childNodes[0]));
|
||||
this.appendRemoveBtn(/** @type {!Element} */ (postalElt.childNodes[1]));
|
||||
} else {
|
||||
addPostalInfoBtn.removeAttribute('disabled');
|
||||
}
|
||||
|
||||
this.addRemBtnHandlers(
|
||||
'contact-postalInfo',
|
||||
function() {
|
||||
return 'contact:postalInfo[1].contact:';
|
||||
},
|
||||
function() {
|
||||
addPostalInfoBtn.setAttribute('disabled', true);
|
||||
return null;
|
||||
},
|
||||
registry.soy.registrar.contact.postalInfo,
|
||||
{
|
||||
item: {},
|
||||
localized: true,
|
||||
itemPrefix: 'contact:',
|
||||
namePrefix: 'contact:postalInfo[1].contact:'
|
||||
},
|
||||
setupRemoveBtns);
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
registry.registrar.Contact.prototype.prepareCreate = function(params) {
|
||||
params.nextId = params.item['contact:id'];
|
||||
return registry.soy.registrar.contactepp.create(params).toString();
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
registry.registrar.Contact.prototype.prepareUpdate = function(params) {
|
||||
params.nextId = params.item['contact:id'];
|
||||
return registry.soy.registrar.contactepp.update(params).toString();
|
||||
};
|
240
java/google/registry/ui/js/registrar/contact_settings.js
Normal file
240
java/google/registry/ui/js/registrar/contact_settings.js
Normal file
|
@ -0,0 +1,240 @@
|
|||
// Copyright 2016 The Domain Registry Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
goog.provide('registry.registrar.ContactSettings');
|
||||
|
||||
goog.require('goog.Uri');
|
||||
goog.require('goog.array');
|
||||
goog.require('goog.dom');
|
||||
goog.require('goog.events');
|
||||
goog.require('goog.events.EventType');
|
||||
goog.require('goog.json');
|
||||
goog.require('goog.soy');
|
||||
goog.require('registry.Resource');
|
||||
goog.require('registry.ResourceComponent');
|
||||
goog.require('registry.soy.registrar.contacts');
|
||||
goog.require('registry.util');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Contact Settings page. Registrar Contacts are not really a proper
|
||||
* REST resource as they're still passed from the server as a member
|
||||
* field of Registrar, so this class behaves like an item page,
|
||||
* updating only that field of the Registrar object and not
|
||||
* implementing the create action from edit_item.
|
||||
* @param {!registry.registrar.Console} console
|
||||
* @param {string} xsrfToken Security token to pass back to the server.
|
||||
* @constructor
|
||||
* @extends {registry.ResourceComponent}
|
||||
* @final
|
||||
*/
|
||||
registry.registrar.ContactSettings = function(console, xsrfToken) {
|
||||
registry.registrar.ContactSettings.base(
|
||||
this, 'constructor',
|
||||
console,
|
||||
new registry.Resource(new goog.Uri('/registrar-settings'), xsrfToken),
|
||||
registry.soy.registrar.contacts.contact,
|
||||
null);
|
||||
};
|
||||
goog.inherits(registry.registrar.ContactSettings, registry.ResourceComponent);
|
||||
|
||||
|
||||
/** @override */
|
||||
registry.registrar.ContactSettings.prototype.setupAppbar = function() {
|
||||
registry.registrar.ContactSettings.base(this, 'setupAppbar');
|
||||
// Setup delete only on existing items, not on creates.
|
||||
if (goog.isDefAndNotNull(this.model)) {
|
||||
var deleteBtn = goog.dom.createDom('button', {
|
||||
type: 'button',
|
||||
id: 'reg-app-btn-delete',
|
||||
className: goog.getCssName('kd-button')
|
||||
},
|
||||
'Delete');
|
||||
goog.events.listen(deleteBtn, goog.events.EventType.CLICK,
|
||||
goog.bind(this.sendDelete, this));
|
||||
goog.dom.insertSiblingBefore(deleteBtn,
|
||||
goog.dom.getRequiredElement('reg-app-btn-cancel'));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
registry.registrar.ContactSettings.prototype.renderItem = function(rspObj) {
|
||||
var contentElt = goog.dom.getRequiredElement('reg-content');
|
||||
/** @type {!registry.json.RegistrarContact} */
|
||||
var contacts = rspObj.contacts;
|
||||
if (this.id) {
|
||||
var targetContactNdx;
|
||||
var targetContact;
|
||||
for (var c in contacts) {
|
||||
var ct = contacts[c];
|
||||
if (ct.emailAddress == this.id) {
|
||||
targetContactNdx = c;
|
||||
targetContact = ct;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!targetContact) {
|
||||
registry.util.butter('No contact with the given email.');
|
||||
return;
|
||||
}
|
||||
var typesList = targetContact.types.toLowerCase().split(',');
|
||||
var actualTypesLookup = {};
|
||||
for (var t in typesList) {
|
||||
actualTypesLookup[typesList[t]] = true;
|
||||
}
|
||||
goog.soy.renderElement(
|
||||
contentElt,
|
||||
registry.soy.registrar.contacts.contact,
|
||||
{
|
||||
item: targetContact,
|
||||
namePrefix: 'contacts[' + targetContactNdx + '].',
|
||||
actualTypesLookup: actualTypesLookup,
|
||||
readonly: (rspObj.readonly || false)
|
||||
});
|
||||
this.setupAppbar();
|
||||
} else {
|
||||
var contactsByType = {};
|
||||
for (var c in contacts) {
|
||||
var contact = contacts[c];
|
||||
var types = contact.types;
|
||||
if (!types) {
|
||||
continue;
|
||||
}
|
||||
types = types.split(',');
|
||||
for (var t in types) {
|
||||
var type = types[t].toLowerCase();
|
||||
var contactsList = contactsByType[type];
|
||||
if (!contactsList) {
|
||||
contactsByType[type] = contactsList = [];
|
||||
}
|
||||
contactsList.push(contact);
|
||||
}
|
||||
}
|
||||
goog.soy.renderElement(
|
||||
contentElt,
|
||||
registry.soy.registrar.contacts.set,
|
||||
{contactsByType: contactsByType });
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
registry.registrar.ContactSettings.prototype.add = function() {
|
||||
var newContactNdx = this.model.contacts.length;
|
||||
goog.soy.renderElement(goog.dom.getRequiredElement('reg-content'),
|
||||
registry.soy.registrar.contacts.contact,
|
||||
{
|
||||
item: {},
|
||||
namePrefix: 'contacts[' + newContactNdx + '].',
|
||||
actualTypesLookup: {},
|
||||
readonly: false
|
||||
});
|
||||
this.toggleEdit();
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
registry.registrar.ContactSettings.prototype.sendDelete = function() {
|
||||
var ndxToDel = null;
|
||||
for (var i = 0; i < this.model.contacts.length; i++) {
|
||||
var contact = this.model.contacts[i];
|
||||
if (contact.emailAddress == this.id) {
|
||||
ndxToDel = i;
|
||||
}
|
||||
}
|
||||
if (goog.isNull(ndxToDel)) {
|
||||
throw new Error('Email to delete does not match model.');
|
||||
}
|
||||
var modelCopy = /** @type {!Object}
|
||||
*/ (goog.json.parse(goog.json.serialize(this.model)));
|
||||
goog.array.removeAt(modelCopy.contacts, ndxToDel);
|
||||
this.resource.update(modelCopy, goog.bind(this.handleDeleteResponse, this));
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
registry.registrar.ContactSettings.prototype.prepareUpdate =
|
||||
function(modelCopy) {
|
||||
var form = registry.util.parseForm('item');
|
||||
var contact;
|
||||
// Handle update/create.
|
||||
if (this.id) {
|
||||
// Update contact, so overwrite it in the model before sending
|
||||
// back to server.
|
||||
var once = false;
|
||||
for (var c in form.contacts) {
|
||||
if (once) {
|
||||
throw new Error('More than one contact parsed from form: ' + c);
|
||||
}
|
||||
contact = form.contacts[c];
|
||||
modelCopy.contacts[c] = contact;
|
||||
once = true;
|
||||
}
|
||||
} else {
|
||||
// Add contact.
|
||||
contact = form.contacts.pop();
|
||||
modelCopy.contacts.push(contact);
|
||||
}
|
||||
contact.visibleInWhoisAsAdmin = contact.visibleInWhoisAsAdmin == 'true';
|
||||
contact.visibleInWhoisAsTech = contact.visibleInWhoisAsTech == 'true';
|
||||
contact.types = '';
|
||||
for (var tNdx in contact.type) {
|
||||
if (contact.type[tNdx]) {
|
||||
if (contact.types.length > 0) {
|
||||
contact.types += ',';
|
||||
}
|
||||
contact.types += ('' + tNdx).toUpperCase();
|
||||
}
|
||||
}
|
||||
delete contact['type'];
|
||||
this.nextId = contact.emailAddress;
|
||||
};
|
||||
|
||||
|
||||
// XXX: Should be hoisted up.
|
||||
/**
|
||||
* Handler for contact save that navigates to that item on success.
|
||||
* Does nothing on failure as UI will be left with error messages for
|
||||
* the user to resolve.
|
||||
* @param {!Object} rsp Decoded XML/JSON response from the server.
|
||||
* @override
|
||||
*/
|
||||
registry.registrar.ContactSettings.prototype.handleCreateResponse =
|
||||
function(rsp) {
|
||||
this.handleUpdateResponse(rsp);
|
||||
if (rsp.status == 'SUCCESS') {
|
||||
this.console.view('contact-settings/' + this.nextId);
|
||||
}
|
||||
return rsp;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Handler for contact delete that navigates back to the collection on success.
|
||||
* Does nothing on failure as UI will be left with error messages for
|
||||
* the user to resolve.
|
||||
* @param {!Object} rsp Decoded XML/JSON response from the server.
|
||||
* @override
|
||||
*/
|
||||
registry.registrar.ContactSettings.prototype.handleDeleteResponse =
|
||||
function(rsp) {
|
||||
this.handleUpdateResponse(rsp);
|
||||
if (rsp.status == 'SUCCESS') {
|
||||
this.id = null;
|
||||
this.console.view('contact-settings');
|
||||
}
|
||||
return rsp;
|
||||
};
|
49
java/google/registry/ui/js/registrar/contact_us.js
Normal file
49
java/google/registry/ui/js/registrar/contact_us.js
Normal file
|
@ -0,0 +1,49 @@
|
|||
// Copyright 2016 The Domain Registry Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
goog.provide('registry.registrar.ContactUs');
|
||||
|
||||
goog.require('goog.Uri');
|
||||
goog.require('goog.dom');
|
||||
goog.require('registry.Resource');
|
||||
goog.require('registry.ResourceComponent');
|
||||
goog.require('registry.soy.registrar.console');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Contact Us page.
|
||||
* @param {!registry.registrar.Console} console
|
||||
* @param {string} xsrfToken Security token to pass back to the server.
|
||||
* @constructor
|
||||
* @extends {registry.ResourceComponent}
|
||||
* @final
|
||||
*/
|
||||
registry.registrar.ContactUs = function(console, xsrfToken) {
|
||||
registry.registrar.ContactUs.base(
|
||||
this,
|
||||
'constructor',
|
||||
console,
|
||||
new registry.Resource(new goog.Uri('/registrar-settings'), xsrfToken),
|
||||
registry.soy.registrar.console.contactUs,
|
||||
null);
|
||||
};
|
||||
goog.inherits(registry.registrar.ContactUs, registry.ResourceComponent);
|
||||
|
||||
|
||||
/** @override */
|
||||
registry.registrar.ContactUs.prototype.bindToDom = function(id) {
|
||||
registry.registrar.ContactUs.base(this, 'bindToDom', '');
|
||||
goog.dom.removeChildren(goog.dom.getRequiredElement('reg-app-buttons'));
|
||||
};
|
94
java/google/registry/ui/js/registrar/dashboard.js
Normal file
94
java/google/registry/ui/js/registrar/dashboard.js
Normal file
|
@ -0,0 +1,94 @@
|
|||
// Copyright 2016 The Domain Registry Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
goog.provide('registry.registrar.Dashboard');
|
||||
|
||||
goog.require('goog.Timer');
|
||||
goog.require('goog.dom');
|
||||
goog.require('goog.events');
|
||||
goog.require('goog.events.EventType');
|
||||
goog.require('goog.soy');
|
||||
goog.require('goog.style');
|
||||
goog.require('registry.Component');
|
||||
goog.require('registry.soy.registrar.console');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Dashboard for Registrar Console.
|
||||
* @param {!registry.registrar.Console} console
|
||||
* @constructor
|
||||
* @extends {registry.Component}
|
||||
* @final
|
||||
*/
|
||||
registry.registrar.Dashboard = function(console) {
|
||||
registry.registrar.Dashboard.base(this, 'constructor', console);
|
||||
|
||||
/** @private {number} */
|
||||
this.x_ = 0;
|
||||
|
||||
/** @private {Element} */
|
||||
this.gear_ = null;
|
||||
|
||||
/** @private {goog.Timer} */
|
||||
this.timer_ = null;
|
||||
};
|
||||
goog.inherits(registry.registrar.Dashboard, registry.Component);
|
||||
|
||||
|
||||
/** @override */
|
||||
registry.registrar.Dashboard.prototype.bindToDom = function(id) {
|
||||
registry.registrar.Dashboard.base(this, 'bindToDom', '');
|
||||
goog.dom.removeChildren(goog.dom.getRequiredElement('reg-appbar'));
|
||||
goog.soy.renderElement(goog.dom.getElement('reg-content'),
|
||||
registry.soy.registrar.console.dashboard);
|
||||
goog.events.listen(goog.dom.getElement('rotate'),
|
||||
goog.events.EventType.CLICK,
|
||||
goog.bind(this.rotate_, this));
|
||||
this.gear_ = goog.dom.getRequiredElement('gear');
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Do EPP logout.
|
||||
*/
|
||||
registry.registrar.Dashboard.prototype.doEppLogout = function() {
|
||||
this.console.session.logout();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Let's do the twist.
|
||||
* @private
|
||||
*/
|
||||
registry.registrar.Dashboard.prototype.rotate_ = function() {
|
||||
this.timer_ = new goog.Timer(10);
|
||||
this.registerDisposable(this.timer_);
|
||||
goog.events.listen(this.timer_, goog.Timer.TICK,
|
||||
goog.bind(this.rotateCall_, this));
|
||||
this.timer_.start();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* No really this time!
|
||||
* @private
|
||||
*/
|
||||
registry.registrar.Dashboard.prototype.rotateCall_ = function() {
|
||||
this.x_++;
|
||||
goog.style.setStyle(this.gear_, 'transform', 'rotate(' + this.x_ + 'deg)');
|
||||
if (this.x_ == 360) {
|
||||
this.timer_.stop();
|
||||
}
|
||||
};
|
202
java/google/registry/ui/js/registrar/domain.js
Normal file
202
java/google/registry/ui/js/registrar/domain.js
Normal file
|
@ -0,0 +1,202 @@
|
|||
// Copyright 2016 The Domain Registry Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
goog.provide('registry.registrar.Domain');
|
||||
|
||||
goog.require('goog.json');
|
||||
goog.require('registry.registrar.XmlResourceComponent');
|
||||
goog.require('registry.soy.forms');
|
||||
goog.require('registry.soy.registrar.domain');
|
||||
goog.require('registry.soy.registrar.domainepp');
|
||||
goog.require('registry.util');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* CRUD for EPP domain objects.
|
||||
* @param {!registry.registrar.Console} console
|
||||
* @constructor
|
||||
* @extends {registry.registrar.XmlResourceComponent}
|
||||
* @final
|
||||
*/
|
||||
registry.registrar.Domain = function(console) {
|
||||
registry.registrar.Domain.base(
|
||||
this, 'constructor',
|
||||
registry.soy.registrar.domain.item,
|
||||
registry.soy.registrar.domainepp,
|
||||
console);
|
||||
};
|
||||
goog.inherits(registry.registrar.Domain,
|
||||
registry.registrar.XmlResourceComponent);
|
||||
|
||||
|
||||
/**
|
||||
* @define {boolean} Launch phase flag. If not SUNRUSH then GA.
|
||||
*/
|
||||
goog.define('registry.registrar.Domain.SUNRUSH', false);
|
||||
|
||||
|
||||
/** @override */
|
||||
registry.registrar.Domain.prototype.newModel = function() {
|
||||
var newModel = {item: {'domain:period': ''}};
|
||||
if (registry.registrar.Domain.SUNRUSH) {
|
||||
newModel.allowSmd = true;
|
||||
}
|
||||
return newModel;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Prepare a fetch query for the domain by its domain application id.
|
||||
* @param {!Object} params should have a name field with a
|
||||
* possibly extended domain name id of the form "example.tld:1234"
|
||||
* where the 1234 is the domain application id assigned by the
|
||||
* backend flows.
|
||||
* @override
|
||||
*/
|
||||
registry.registrar.Domain.prototype.prepareFetch = function(params) {
|
||||
var xml;
|
||||
if (registry.registrar.Domain.SUNRUSH) {
|
||||
var idParts = params.id.split(':');
|
||||
if (idParts.length != 2) {
|
||||
registry.util.butter(
|
||||
'Domain queries during sunrush have the form: ' +
|
||||
'"example.tld:1234", where 1234 is the application ID.');
|
||||
throw Error('Invalid domain name for SUNRUSH, lacking application ID');
|
||||
}
|
||||
params.name = idParts[0];
|
||||
params.applicationID = idParts[1];
|
||||
xml = registry.soy.registrar.domainepp.infoSunrush(params);
|
||||
} else {
|
||||
xml = registry.soy.registrar.domainepp.info(params);
|
||||
}
|
||||
return xml.toString();
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
registry.registrar.Domain.prototype.processItem = function() {
|
||||
this.model.item = this.model['epp']['response']['resData']['domain:infData'];
|
||||
// Hoist extensions for easy soy access.
|
||||
var extension = this.model['epp']['response']['extension'];
|
||||
if (extension && extension['launch:infData']) {
|
||||
var extendedInfData = extension['launch:infData'];
|
||||
|
||||
var applicationID = extendedInfData['launch:applicationID'];
|
||||
if (applicationID) {
|
||||
this.model.item['launch:applicationID'] = applicationID;
|
||||
}
|
||||
|
||||
var mark = extendedInfData['mark:mark'];
|
||||
if (mark) {
|
||||
this.model.item['mark:mark'] = {'keyValue': goog.json.serialize(mark)};
|
||||
}
|
||||
}
|
||||
|
||||
// Wrap single item into an array.
|
||||
if (this.model.item['domain:ns'] &&
|
||||
this.model.item['domain:ns']['domain:hostObj']) {
|
||||
var hostObj = this.model.item['domain:ns']['domain:hostObj'];
|
||||
if (!goog.isArray(hostObj)) {
|
||||
this.model.item['domain:ns']['domain:hostObj'] = [hostObj];
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
registry.registrar.Domain.prototype.setupEditor = function(objArgs) {
|
||||
this.typeCounts['contact'] = objArgs.item['domain:contact'] ?
|
||||
objArgs.item['domain:contact'].length : 0;
|
||||
|
||||
var ji = objArgs.item['domain:ns'];
|
||||
this.typeCounts['host'] =
|
||||
ji && ji['domain:hostObj'] ? ji['domain:hostObj'].length : 0;
|
||||
|
||||
this.formInputRowRemovable(
|
||||
document.querySelectorAll('input.domain-hostObj[readonly]'));
|
||||
|
||||
this.formInputRowRemovable(
|
||||
document.querySelectorAll('input.domain-contact[readonly]'));
|
||||
|
||||
this.addRemBtnHandlers(
|
||||
'contact',
|
||||
goog.bind(function() {
|
||||
return 'domain:contact[' + this.typeCounts['contact'] + ']';
|
||||
}, this),
|
||||
goog.bind(function(newFieldName) {
|
||||
return registry.util.renderBeforeRow(
|
||||
'domain-contacts-footer',
|
||||
registry.soy.forms.selectRow, {
|
||||
label: 'Type',
|
||||
name: newFieldName + '.@type',
|
||||
options: ['admin', 'tech', 'billing']
|
||||
});
|
||||
}, this));
|
||||
|
||||
this.addRemBtnHandlers('host', goog.bind(function() {
|
||||
return 'domain:ns.domain:hostObj[' + this.typeCounts['host'] + ']';
|
||||
}, this));
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
registry.registrar.Domain.prototype.prepareCreate = function(params) {
|
||||
var form = params.item;
|
||||
params.nextId = form['domain:name'];
|
||||
|
||||
// The presence of this field is used to signal extended template, so remove
|
||||
// if not used.
|
||||
if (form['smd:encodedSignedMark'] == '') {
|
||||
delete form['smd:encodedSignedMark'];
|
||||
}
|
||||
var xml;
|
||||
if (registry.registrar.Domain.SUNRUSH) {
|
||||
xml = registry.soy.registrar.domainepp.createSunrush(params);
|
||||
} else {
|
||||
xml = registry.soy.registrar.domainepp.create(params);
|
||||
}
|
||||
return xml.toString();
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
registry.registrar.Domain.prototype.prepareUpdate =
|
||||
function(params) {
|
||||
var form = params.item;
|
||||
var nextId = form['domain:name'];
|
||||
params.nextId = nextId;
|
||||
|
||||
if (form['domain:contact']) {
|
||||
this.addRem(form['domain:contact'], 'Contacts', params);
|
||||
}
|
||||
|
||||
if (form['domain:ns'] && form['domain:ns']['domain:hostObj']) {
|
||||
this.addRem(form['domain:ns']['domain:hostObj'], 'Hosts', params);
|
||||
}
|
||||
|
||||
var xml;
|
||||
if (registry.registrar.Domain.SUNRUSH) {
|
||||
xml = registry.soy.registrar.domainepp.updateSunrush(params);
|
||||
nextId += ':' + form['launch:applicationID'];
|
||||
} else {
|
||||
if (form['domain:ns'] && form['domain:ns']['domain:hostObj']) {
|
||||
this.addRem(form['domain:ns']['domain:hostObj'], 'Hosts', params);
|
||||
}
|
||||
this.addRem(form['domain:contact'], 'Contacts', params);
|
||||
xml = registry.soy.registrar.domainepp.update(params);
|
||||
}
|
||||
|
||||
return xml.toString();
|
||||
};
|
131
java/google/registry/ui/js/registrar/epp_session.js
Normal file
131
java/google/registry/ui/js/registrar/epp_session.js
Normal file
|
@ -0,0 +1,131 @@
|
|||
// Copyright 2016 The Domain Registry Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
goog.provide('registry.registrar.EppSession');
|
||||
|
||||
goog.require('goog.Uri');
|
||||
goog.require('registry.Session');
|
||||
goog.require('registry.soy.registrar.epp');
|
||||
goog.require('registry.util');
|
||||
goog.require('registry.xml');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Session state for console.
|
||||
* @param {!registry.registrar.Console} console
|
||||
* @param {string} xsrfToken Populated by server-side soy template.
|
||||
* @param {string} clientId The logged in GAE user.
|
||||
* @constructor
|
||||
* @extends {registry.Session}
|
||||
* @final
|
||||
*/
|
||||
registry.registrar.EppSession = function(console, xsrfToken, clientId) {
|
||||
registry.registrar.EppSession.base(
|
||||
this, 'constructor', new goog.Uri('/registrar-xhr'), xsrfToken,
|
||||
registry.Session.ContentType.EPP);
|
||||
|
||||
/**
|
||||
* @type {!registry.registrar.Console}
|
||||
*/
|
||||
this.console = console;
|
||||
|
||||
/**
|
||||
* @type {!boolean}
|
||||
* @private
|
||||
*/
|
||||
this.isEppLoggedIn_ = false;
|
||||
|
||||
/**
|
||||
* @type {string}
|
||||
* @private
|
||||
*/
|
||||
this.clientId_ = clientId;
|
||||
};
|
||||
goog.inherits(registry.registrar.EppSession, registry.Session);
|
||||
|
||||
|
||||
/**
|
||||
* Whether the session has received an EPP success response to an EPP
|
||||
* login attempt.
|
||||
* @return {boolean} Whether the user is logged into an EPP session.
|
||||
*/
|
||||
registry.registrar.EppSession.prototype.isEppLoggedIn = function() {
|
||||
return this.isEppLoggedIn_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Get the clientId if the user is logged in, or throw Error.
|
||||
* @throws Error if the user is not logged in.
|
||||
* @return {string} the clientId.
|
||||
*/
|
||||
registry.registrar.EppSession.prototype.getClientId = function() {
|
||||
return this.clientId_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Login or display butterbar info about error.
|
||||
* @param {function()} successCb to be called on success.
|
||||
*/
|
||||
registry.registrar.EppSession.prototype.login = function(successCb) {
|
||||
var eppArgs = {clId: this.clientId_, clTrid: 'asdf-1235'};
|
||||
this.send(
|
||||
registry.soy.registrar.epp.login(eppArgs).getContent(),
|
||||
goog.bind(function(xml) {
|
||||
var result = xml['epp']['response']['result'];
|
||||
var eppCode = result['@code'];
|
||||
if (eppCode == '1000' || eppCode == '2002') {
|
||||
// Success || Already logged in.
|
||||
this.isEppLoggedIn_ = true;
|
||||
successCb();
|
||||
} else {
|
||||
// Failure.
|
||||
this.isEppLoggedIn_ = false;
|
||||
registry.util.butter('login error: ' + eppCode);
|
||||
}
|
||||
}, this));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Logout or display butterbar info about error.
|
||||
*/
|
||||
registry.registrar.EppSession.prototype.logout = function() {
|
||||
this.send(
|
||||
registry.soy.registrar.epp.logout(
|
||||
{clTrid: 'asdf-1235'}).getContent(),
|
||||
goog.bind(function(xml) {
|
||||
var result = xml['epp']['response']['result'];
|
||||
var eppCode = result['@code'];
|
||||
registry.util.butter(
|
||||
'logout ' + eppCode + ': ' + result['msg']['keyValue']);
|
||||
// Going to be safe here and force a login either way.
|
||||
this.isEppLoggedIn_ = false;
|
||||
}, this));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Send xml to the server.
|
||||
* @param {string} xml Request document.
|
||||
* @param {function(!Object)} callback For XhrIo result throws.
|
||||
*/
|
||||
registry.registrar.EppSession.prototype.send = function(xml, callback) {
|
||||
var toXmlJsonCb = function(rspXml) {
|
||||
callback(registry.xml.convertToJson(rspXml));
|
||||
};
|
||||
this.sendXhrIo(xml, toXmlJsonCb);
|
||||
};
|
103
java/google/registry/ui/js/registrar/host.js
Normal file
103
java/google/registry/ui/js/registrar/host.js
Normal file
|
@ -0,0 +1,103 @@
|
|||
// Copyright 2016 The Domain Registry Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
goog.provide('registry.registrar.Host');
|
||||
|
||||
goog.require('registry.registrar.XmlResourceComponent');
|
||||
goog.require('registry.soy.registrar.host');
|
||||
goog.require('registry.soy.registrar.hostepp');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* CRUD for EPP host objects.
|
||||
* @param {!registry.registrar.Console} console
|
||||
* @constructor
|
||||
* @extends {registry.registrar.XmlResourceComponent}
|
||||
* @final
|
||||
*/
|
||||
registry.registrar.Host = function(console) {
|
||||
registry.registrar.Host.base(
|
||||
this, 'constructor',
|
||||
registry.soy.registrar.host.item,
|
||||
registry.soy.registrar.hostepp,
|
||||
console);
|
||||
};
|
||||
goog.inherits(registry.registrar.Host,
|
||||
registry.registrar.XmlResourceComponent);
|
||||
|
||||
|
||||
/** @override */
|
||||
registry.registrar.Host.prototype.processItem = function() {
|
||||
this.model.item = this.model['epp']['response']['resData']['host:infData'];
|
||||
if (this.model.item['host:addr']) {
|
||||
if (!goog.isArray(this.model.item['host:addr'])) {
|
||||
this.model.item['host:addr'] = [this.model.item['host:addr']];
|
||||
}
|
||||
} else {
|
||||
this.model.item['host:addr'] = [];
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
registry.registrar.Host.prototype.setupEditor = function(objArgs) {
|
||||
this.typeCounts['host-addr'] =
|
||||
objArgs.item['host:addr'] ? objArgs.item['host:addr'].length : 0;
|
||||
this.addRemBtnHandlers('host-addr', goog.bind(function() {
|
||||
return 'host:addr[' + this.typeCounts['host-addr'] + ']';
|
||||
}, this));
|
||||
|
||||
this.formInputRowRemovable(document.querySelectorAll('input[readonly]'));
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
registry.registrar.Host.prototype.prepareCreate = function(params) {
|
||||
params.nextId = params.item['host:name'];
|
||||
return registry.soy.registrar.hostepp.create(params).toString();
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
registry.registrar.Host.prototype.prepareUpdate = function(params) {
|
||||
var form = params.item;
|
||||
var addAddrs = [];
|
||||
var remAddrs = [];
|
||||
if (form['host:addr']) {
|
||||
var oldAddrs = form['host:oldAddr'] || [];
|
||||
var newAddrs = form['host:addr'];
|
||||
var length = Math.max(oldAddrs.length, newAddrs.length);
|
||||
for (var i = 0; i < length; i++) {
|
||||
if (i >= oldAddrs.length) {
|
||||
addAddrs.push(newAddrs[i]['value']);
|
||||
} else if (i >= newAddrs.length) {
|
||||
remAddrs.push(oldAddrs[i]['value']);
|
||||
} else {
|
||||
if (newAddrs[i]['value'] == oldAddrs[i]['value']) {
|
||||
// Do nothing.
|
||||
} else if (newAddrs[i]['value'] == '') {
|
||||
remAddrs.push(oldAddrs[i]['value']);
|
||||
} else {
|
||||
remAddrs.push(oldAddrs[i]['value']);
|
||||
addAddrs.push(newAddrs[i]['value']);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
params.addAddrs = addAddrs;
|
||||
params.remAddrs = remAddrs;
|
||||
params.nextId = form['host:chgName'];
|
||||
return registry.soy.registrar.hostepp.update(params).toString();
|
||||
};
|
35
java/google/registry/ui/js/registrar/main.js
Normal file
35
java/google/registry/ui/js/registrar/main.js
Normal file
|
@ -0,0 +1,35 @@
|
|||
// Copyright 2016 The Domain Registry Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
/**
|
||||
* @fileoverview Entry point for the registrar console.
|
||||
*/
|
||||
|
||||
goog.provide('registry.registrar.main');
|
||||
|
||||
goog.require('registry.registrar.Console');
|
||||
|
||||
|
||||
/**
|
||||
* Instantiates a registry object, which syncs with the server.
|
||||
* Sub-templates are invoked based on location/pathname, to choose
|
||||
* either Registry or Registrar pages.
|
||||
*
|
||||
* @param {string} xsrfToken populated by server-side soy template.
|
||||
* @param {string} clientId The registrar clientId.
|
||||
* @export
|
||||
*/
|
||||
registry.registrar.main = function(xsrfToken, clientId) {
|
||||
new registry.registrar.Console(xsrfToken, clientId);
|
||||
};
|
412
java/google/registry/ui/js/registrar/payment.js
Normal file
412
java/google/registry/ui/js/registrar/payment.js
Normal file
|
@ -0,0 +1,412 @@
|
|||
// Copyright 2016 The Domain Registry Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
goog.provide('registry.registrar.Payment');
|
||||
|
||||
goog.require('goog.Uri');
|
||||
goog.require('goog.asserts');
|
||||
goog.require('goog.dom');
|
||||
goog.require('goog.dom.TagName');
|
||||
goog.require('goog.events.EventType');
|
||||
goog.require('goog.json');
|
||||
goog.require('goog.object');
|
||||
goog.require('goog.soy');
|
||||
goog.require('registry.Component');
|
||||
goog.require('registry.MenuButton');
|
||||
goog.require('registry.Session');
|
||||
goog.require('registry.forms');
|
||||
goog.require('registry.registrar.BrainFrame');
|
||||
goog.require('registry.soy.registrar.console');
|
||||
goog.require('registry.soy.registrar.payment');
|
||||
goog.require('registry.util');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Page allowing registrar to send money to registry.
|
||||
*
|
||||
* <p>This page contains a form that asks the user to enter an arbitrary amount
|
||||
* and a payment method, which can be credit card or PayPal. Multiple
|
||||
* currencies are supported.
|
||||
*
|
||||
* <h3>PCI Compliance</h3>
|
||||
*
|
||||
* <p>We don't have any access whatsoever to the credit card information. We
|
||||
* embed an iframe run by Braintree Payments. The user can then provide his
|
||||
* credit card (or PayPal) details directly to Braintree. Braintree then gives
|
||||
* us a nonce value representing the payment method, which we can use to issue
|
||||
* the transaction.
|
||||
*
|
||||
* <h3>Bidirectional Protection</h3>
|
||||
*
|
||||
* <p>To use Braintree's iframe, we need to load a script from their server. We
|
||||
* don't want that script to have access to the Registrar Console. If Braintree
|
||||
* got pwnd, the attacker would be able to issue EPP commands as a registrar.
|
||||
*
|
||||
* <p>We fix this problem by embedding the Braintree iframe inside another
|
||||
* sandbox iframe that's hosted from a Cloud Storage bucket. This frame is
|
||||
* defined by {@code brainframe.html}. It's basically an empty shell that sends
|
||||
* a request back to the production environment for {@code brainframe.js}.
|
||||
*
|
||||
* <p>The importance of the Cloud Storage bucket is that it is served from a
|
||||
* separate domain. This causes the browser to forbid the iframe from accessing
|
||||
* the contents of the parent frame. The HTML5 {@code sandbox} attribute does
|
||||
* this too, but we can't use it, because the Venmo functionality in the
|
||||
* Braintree JS SDK needs to be able to access {@code document.cookie}, which
|
||||
* is forbidden in a sandbox environment. This HTML5 {@code sandbox} feature is
|
||||
* also not available in older versions of Internet Explorer.
|
||||
*
|
||||
* <h3>Business Logic</h3>
|
||||
*
|
||||
* <p>This page starts off as a loading glyph, while we issue an RPC to the
|
||||
* backend. We ask for a Braintree token, which currencies are available, and
|
||||
* the location of the brainframe HTML file. Once we get that data, we render
|
||||
* the form.
|
||||
*
|
||||
* <p>Once the sandbox iframe inside that form has loaded, it'll send us a
|
||||
* message asking for the token. We give it the token, which it uses to load
|
||||
* the Braintree iframe.
|
||||
*
|
||||
* <p>To make sure the sandbox iframe is the same size as the Braintree iframe,
|
||||
* the sandbox iframe will send us messages on occasion asking to be resized.
|
||||
*
|
||||
* <p>The disabled state of the submit button is managed judiciously. It's
|
||||
* disabled initially, until we get a READY message from the sandbox iframe,
|
||||
* indicating that the Braintree iframe is fully loaded. We also disable the
|
||||
* submit button during the submit process.
|
||||
*
|
||||
* <p>When the user presses the submit button, we send a message to the sandbox
|
||||
* iframe asking it to submit the Braintree iframe. When the Braintree iframe
|
||||
* is submitted, it gives the sandbox iframe the the payment method nonce,
|
||||
* which it passes along to us. Then we pass the form data to the backend via
|
||||
* the payment RPC, which invokes the Braintree Java API to issue the
|
||||
* transaction.
|
||||
*
|
||||
* <p>If the payment RPC fails, we'll either show an error on a field, or in a
|
||||
* bloody butterbar. If it succeeds, then the backend will give us the
|
||||
* transaction ID assigned by Braintree, which we then render on a success
|
||||
* page.
|
||||
*
|
||||
* <p>The success page contains a "Make Another Payment" button which, if
|
||||
* clicked, will reset the state of this page back to the beginning.
|
||||
*
|
||||
* @param {!registry.registrar.Console} console
|
||||
* @param {string} xsrfToken Security token to pass back to the server.
|
||||
* @constructor
|
||||
* @extends {registry.Component}
|
||||
* @final
|
||||
*/
|
||||
registry.registrar.Payment = function(console, xsrfToken) {
|
||||
registry.registrar.Payment.base(this, 'constructor', console);
|
||||
|
||||
/**
|
||||
* Element in which this page is rendered.
|
||||
* @private {!Element}
|
||||
* @const
|
||||
*/
|
||||
this.content_ = goog.dom.getRequiredElement('reg-content');
|
||||
|
||||
/**
|
||||
* Braintree API nonce token generated by the backend. This value is a
|
||||
* prerequisite to rendering the Braintree iframe.
|
||||
* @private {string}
|
||||
*/
|
||||
this.token_ = '';
|
||||
|
||||
/**
|
||||
* Braintree API nonce value for payment method selected by user.
|
||||
* @private {string}
|
||||
*/
|
||||
this.paymentMethodNonce_ = '';
|
||||
|
||||
/**
|
||||
* Currency drop-down widget in form.
|
||||
* @private {?registry.MenuButton}
|
||||
*/
|
||||
this.currencyMenu_ = null;
|
||||
|
||||
/**
|
||||
* XHR client to {@code RegistrarPaymentSetupAction}.
|
||||
* @private {!registry.Session.<!registry.rpc.PaymentSetup.Request,
|
||||
* !registry.rpc.PaymentSetup.Response>}
|
||||
* @const
|
||||
*/
|
||||
this.setupRpc_ =
|
||||
new registry.Session(new goog.Uri('/registrar-payment-setup'),
|
||||
xsrfToken,
|
||||
registry.Session.ContentType.JSON);
|
||||
|
||||
/**
|
||||
* XHR client to {@code RegistrarPaymentAction}.
|
||||
* @private {!registry.Session.<!registry.rpc.Payment.Request,
|
||||
* !registry.rpc.Payment.Response>}
|
||||
* @const
|
||||
*/
|
||||
this.paymentRpc_ =
|
||||
new registry.Session(new goog.Uri('/registrar-payment'),
|
||||
xsrfToken,
|
||||
registry.Session.ContentType.JSON);
|
||||
|
||||
this.listen(goog.global.window,
|
||||
goog.events.EventType.MESSAGE,
|
||||
this.onMessage_);
|
||||
};
|
||||
goog.inherits(registry.registrar.Payment, registry.Component);
|
||||
|
||||
|
||||
/** @override */
|
||||
registry.registrar.Payment.prototype.bindToDom = function(id) {
|
||||
registry.registrar.Payment.base(this, 'bindToDom', id);
|
||||
if (!goog.isNull(goog.dom.getElement('reg-app-buttons'))) {
|
||||
goog.dom.removeChildren(goog.dom.getElement('reg-app-buttons'));
|
||||
}
|
||||
if (!registry.registrar.Payment.isBrowserSupported_()) {
|
||||
goog.soy.renderElement(this.content_,
|
||||
registry.soy.registrar.payment.unsupported);
|
||||
return;
|
||||
}
|
||||
goog.soy.renderElement(this.content_, registry.soy.registrar.console.loading);
|
||||
this.setupRpc_.sendXhrIo({}, goog.bind(this.onSetup_, this));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Handler invoked when we receive information from our backend, such as a
|
||||
* Braintree token, which is necessary for us to render the payment form.
|
||||
* @param {!registry.rpc.PaymentSetup.Response} response
|
||||
* @private
|
||||
*/
|
||||
registry.registrar.Payment.prototype.onSetup_ = function(response) {
|
||||
if (response.status != 'SUCCESS') {
|
||||
if (response.message == 'not-using-cc-billing') {
|
||||
goog.soy.renderElement(this.content_,
|
||||
registry.soy.registrar.payment.notUsingCcBilling);
|
||||
} else {
|
||||
registry.forms.displayError(response.message);
|
||||
}
|
||||
return;
|
||||
}
|
||||
var result = response.results[0];
|
||||
this.token_ = result.token;
|
||||
this.paymentMethodNonce_ = '';
|
||||
goog.soy.renderElement(this.content_,
|
||||
registry.soy.registrar.payment.form,
|
||||
result);
|
||||
this.listen(
|
||||
goog.dom.getRequiredElementByClass(goog.getCssName('reg-payment-form')),
|
||||
goog.events.EventType.SUBMIT,
|
||||
this.onSubmit_);
|
||||
this.currencyMenu_ =
|
||||
new registry.MenuButton(goog.dom.getRequiredElement('currency'));
|
||||
this.registerDisposable(this.currencyMenu_);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Handler invoked when payment form is submitted.
|
||||
* @param {!goog.events.BrowserEvent} e
|
||||
* @private
|
||||
*/
|
||||
registry.registrar.Payment.prototype.onSubmit_ = function(e) {
|
||||
e.preventDefault();
|
||||
this.submit_();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Submits payment form.
|
||||
* @private
|
||||
*/
|
||||
registry.registrar.Payment.prototype.submit_ = function() {
|
||||
registry.forms.resetErrors();
|
||||
registry.registrar.Payment.setEnabled_(false);
|
||||
if (this.paymentMethodNonce_ == '') {
|
||||
this.send_(
|
||||
'type', registry.registrar.BrainFrame.MessageType.SUBMIT_REQUEST);
|
||||
return;
|
||||
}
|
||||
this.paymentRpc_.sendXhrIo(
|
||||
{
|
||||
amount: goog.dom.getRequiredElement('amount').value,
|
||||
currency: this.currencyMenu_.getValue(),
|
||||
paymentMethodNonce: this.paymentMethodNonce_
|
||||
},
|
||||
goog.bind(this.onPayment_, this));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Callback for backend payment RPC that issues the transaction.
|
||||
* @param {!registry.rpc.Payment.Response} response
|
||||
* @private
|
||||
*/
|
||||
registry.registrar.Payment.prototype.onPayment_ = function(response) {
|
||||
registry.registrar.Payment.setEnabled_(true);
|
||||
if (response.status != 'SUCCESS') {
|
||||
registry.forms.displayError(response.message, response.field);
|
||||
return;
|
||||
}
|
||||
goog.soy.renderElement(this.content_,
|
||||
registry.soy.registrar.payment.success,
|
||||
response.results[0]);
|
||||
this.listenOnce(
|
||||
goog.dom.getRequiredElementByClass(goog.getCssName('reg-payment-again')),
|
||||
goog.events.EventType.CLICK,
|
||||
this.bindToDom);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Handler invoked when {@code brainframe.js} sends us a message.
|
||||
* @param {!goog.events.BrowserEvent} e
|
||||
* @private
|
||||
*/
|
||||
registry.registrar.Payment.prototype.onMessage_ = function(e) {
|
||||
var msg = /** @type {!MessageEvent.<string>} */ (e.getBrowserEvent());
|
||||
var brainframe =
|
||||
goog.dom.getElementByClass(goog.getCssName('reg-payment-form-method'));
|
||||
if (brainframe == null ||
|
||||
msg.source != goog.dom.getFrameContentWindow(brainframe)) {
|
||||
return;
|
||||
}
|
||||
var data;
|
||||
try {
|
||||
data = goog.json.parse(msg.data);
|
||||
} catch (ex) {
|
||||
// TODO(b/26876003): Figure out why it's possible that the Braintree iframe
|
||||
// is able to propagate messages up to our level.
|
||||
registry.util.log(ex, msg.source, msg.data);
|
||||
return;
|
||||
}
|
||||
switch (goog.object.get(data, 'type')) {
|
||||
case registry.registrar.BrainFrame.MessageType.TOKEN_REQUEST:
|
||||
goog.asserts.assert(this.token_ != '');
|
||||
this.send_(
|
||||
'type', registry.registrar.BrainFrame.MessageType.TOKEN_RESPONSE,
|
||||
'token', this.token_);
|
||||
break;
|
||||
case registry.registrar.BrainFrame.MessageType.RESIZE_REQUEST:
|
||||
brainframe.height = goog.object.get(data, 'height');
|
||||
break;
|
||||
case registry.registrar.BrainFrame.MessageType.READY:
|
||||
registry.registrar.Payment.setEnabled_(true);
|
||||
break;
|
||||
case registry.registrar.BrainFrame.MessageType.SUBMIT_ERROR:
|
||||
registry.registrar.Payment.setEnabled_(true);
|
||||
registry.forms.displayError(goog.object.get(data, 'message'), 'method');
|
||||
break;
|
||||
case registry.registrar.BrainFrame.MessageType.PAYMENT_METHOD:
|
||||
registry.registrar.Payment.setEnabled_(true);
|
||||
this.setPaymentMethod_(
|
||||
/** @type {!braintreepayments.PaymentMethod} */ (
|
||||
goog.object.get(data, 'method')));
|
||||
if (goog.object.get(data, 'submit')) {
|
||||
this.submit_();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw Error('Unexpected message: ' + msg.data);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Updates UI to display selected payment method.
|
||||
*
|
||||
* <p>We remove the iframe from the page as soon as this happens, because the
|
||||
* UI would be busted otherwise. The Braintree UI for changing the payment
|
||||
* method (after it's been entered) does not appear to stop respond to submit
|
||||
* events. It also causes ugly scroll bars to appear inside the iframe.
|
||||
*
|
||||
* <p>This approach is also advantageous for screenshot testing. We do not want
|
||||
* our continuous integration testing system to talk to Braintree's servers. So
|
||||
* we mock out the brainframe with {@code integration-test-brainframe.html}
|
||||
* which <i>only</i> sends us a METHOD message, which we then render ourselves.
|
||||
*
|
||||
* @param {!braintreepayments.PaymentMethod} pm
|
||||
* @private
|
||||
*/
|
||||
registry.registrar.Payment.prototype.setPaymentMethod_ = function(pm) {
|
||||
registry.forms.resetErrors();
|
||||
goog.dom.removeNode(
|
||||
goog.dom.getElementByClass(goog.getCssName('reg-payment-form-method')));
|
||||
var paymentMethodInfoBox =
|
||||
goog.dom.getRequiredElementByClass(
|
||||
goog.getCssName('reg-payment-form-method-info'));
|
||||
switch (pm.type) {
|
||||
case 'CreditCard':
|
||||
goog.soy.renderElement(
|
||||
paymentMethodInfoBox,
|
||||
registry.soy.registrar.payment.methodInfoCard,
|
||||
{cardType: pm.details.cardType, lastTwo: pm.details.lastTwo});
|
||||
break;
|
||||
case 'PayPalAccount':
|
||||
goog.soy.renderElement(
|
||||
paymentMethodInfoBox,
|
||||
registry.soy.registrar.payment.methodInfoPaypal,
|
||||
{email: pm.details.email});
|
||||
break;
|
||||
default:
|
||||
throw Error('Unknown payment method: ' + pm.type);
|
||||
}
|
||||
registry.util.setVisible(paymentMethodInfoBox, true);
|
||||
this.paymentMethodNonce_ = pm.nonce;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sends message to brainframe.
|
||||
* @param {...*} var_args Passed along to {@code goog.object.create}.
|
||||
* @private
|
||||
*/
|
||||
registry.registrar.Payment.prototype.send_ = function(var_args) {
|
||||
goog.asserts.assert(arguments[0] == 'type');
|
||||
var brainframeWindow =
|
||||
goog.dom.getFrameContentWindow(
|
||||
goog.dom.getRequiredElementByClass(
|
||||
goog.getCssName('reg-payment-form-method')));
|
||||
// We send a string value to support older versions of IE.
|
||||
brainframeWindow.postMessage(
|
||||
goog.json.serialize(goog.object.create.apply(null, arguments)),
|
||||
'*');
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Enables submit button and hides mini loading glyph.
|
||||
* @param {boolean} enabled
|
||||
* @private
|
||||
*/
|
||||
registry.registrar.Payment.setEnabled_ = function(enabled) {
|
||||
registry.forms.setEnabled(
|
||||
goog.dom.getRequiredElementByClass(
|
||||
goog.getCssName('reg-payment-form-submit')), enabled);
|
||||
registry.util.setVisible(
|
||||
goog.dom.getRequiredElementByClass(
|
||||
goog.getCssName('reg-payment-form-loader')), !enabled);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns {@code true} if browser has all the features we need.
|
||||
* @return {boolean}
|
||||
* @private
|
||||
* @see "http://caniuse.com/#feat=dispatchevent"
|
||||
*/
|
||||
registry.registrar.Payment.isBrowserSupported_ = function() {
|
||||
// dispatchEvent is used by brainframe.js and is IE 9+.
|
||||
return goog.object.containsKey(
|
||||
goog.dom.createElement(goog.dom.TagName.FORM),
|
||||
'dispatchEvent');
|
||||
};
|
50
java/google/registry/ui/js/registrar/resources.js
Normal file
50
java/google/registry/ui/js/registrar/resources.js
Normal file
|
@ -0,0 +1,50 @@
|
|||
// Copyright 2016 The Domain Registry Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
goog.provide('registry.registrar.Resources');
|
||||
|
||||
goog.require('goog.Uri');
|
||||
goog.require('goog.dom');
|
||||
goog.require('registry.Resource');
|
||||
goog.require('registry.ResourceComponent');
|
||||
goog.require('registry.soy.registrar.console');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Resources and billing page.
|
||||
* @param {!registry.registrar.Console} console
|
||||
* @param {string} xsrfToken Security token to pass back to the server.
|
||||
* @constructor
|
||||
* @extends {registry.ResourceComponent}
|
||||
* @final
|
||||
*/
|
||||
registry.registrar.Resources = function(console, xsrfToken) {
|
||||
registry.registrar.Resources.base(
|
||||
this,
|
||||
'constructor',
|
||||
console,
|
||||
new registry.Resource(new goog.Uri('/registrar-settings'), xsrfToken),
|
||||
registry.soy.registrar.console.resources,
|
||||
null);
|
||||
};
|
||||
goog.inherits(registry.registrar.Resources,
|
||||
registry.ResourceComponent);
|
||||
|
||||
|
||||
/** @override */
|
||||
registry.registrar.Resources.prototype.bindToDom = function(id) {
|
||||
registry.registrar.Resources.base(this, 'bindToDom', '');
|
||||
goog.dom.removeChildren(goog.dom.getRequiredElement('reg-app-buttons'));
|
||||
};
|
110
java/google/registry/ui/js/registrar/security_settings.js
Normal file
110
java/google/registry/ui/js/registrar/security_settings.js
Normal file
|
@ -0,0 +1,110 @@
|
|||
// Copyright 2016 The Domain Registry Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
goog.provide('registry.registrar.SecuritySettings');
|
||||
|
||||
goog.require('goog.Uri');
|
||||
goog.require('goog.array');
|
||||
goog.require('goog.dom');
|
||||
goog.require('goog.dom.classlist');
|
||||
goog.require('goog.events');
|
||||
goog.require('goog.events.EventType');
|
||||
goog.require('goog.soy');
|
||||
goog.require('registry.Resource');
|
||||
goog.require('registry.ResourceComponent');
|
||||
goog.require('registry.soy.registrar.security');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Security Settings page.
|
||||
* @param {!registry.registrar.Console} console
|
||||
* @param {string} xsrfToken Security token to pass back to the server.
|
||||
* @constructor
|
||||
* @extends {registry.ResourceComponent}
|
||||
* @final
|
||||
*/
|
||||
registry.registrar.SecuritySettings = function(console, xsrfToken) {
|
||||
registry.registrar.SecuritySettings.base(
|
||||
this,
|
||||
'constructor',
|
||||
console,
|
||||
new registry.Resource(new goog.Uri('/registrar-settings'), xsrfToken),
|
||||
registry.soy.registrar.security.settings,
|
||||
null);
|
||||
};
|
||||
goog.inherits(registry.registrar.SecuritySettings, registry.ResourceComponent);
|
||||
|
||||
|
||||
/** @override */
|
||||
registry.registrar.SecuritySettings.prototype.bindToDom = function(id) {
|
||||
registry.registrar.SecuritySettings.base(this, 'bindToDom', 'fake');
|
||||
goog.dom.removeNode(goog.dom.getRequiredElement('reg-app-btn-back'));
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
registry.registrar.SecuritySettings.prototype.setupEditor =
|
||||
function(objArgs) {
|
||||
goog.dom.classlist.add(goog.dom.getRequiredElement('ips'),
|
||||
goog.getCssName('editing'));
|
||||
var ips = goog.dom.getElementsByClass(goog.getCssName('ip'),
|
||||
goog.dom.getRequiredElement('ips'));
|
||||
goog.array.forEach(ips, function(ip) {
|
||||
var remBtn = goog.dom.getChildren(ip)[0];
|
||||
goog.events.listen(remBtn,
|
||||
goog.events.EventType.CLICK,
|
||||
goog.bind(this.onIpRemove_, this, remBtn));
|
||||
}, this);
|
||||
this.typeCounts['reg-ips'] = objArgs.ipAddressWhitelist ?
|
||||
objArgs.ipAddressWhitelist.length : 0;
|
||||
|
||||
goog.events.listen(goog.dom.getRequiredElement('btn-add-ip'),
|
||||
goog.events.EventType.CLICK,
|
||||
this.onIpAdd_,
|
||||
false,
|
||||
this);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Click handler for IP add button.
|
||||
* @private
|
||||
*/
|
||||
registry.registrar.SecuritySettings.prototype.onIpAdd_ = function() {
|
||||
var ipInputElt = goog.dom.getRequiredElement('newIp');
|
||||
var ipElt = goog.soy.renderAsFragment(registry.soy.registrar.security.ip, {
|
||||
name: 'ipAddressWhitelist[' + this.typeCounts['reg-ips'] + ']',
|
||||
ip: ipInputElt.value
|
||||
});
|
||||
goog.dom.appendChild(goog.dom.getRequiredElement('ips'), ipElt);
|
||||
var remBtn = goog.dom.getFirstElementChild(ipElt);
|
||||
goog.dom.classlist.remove(remBtn, goog.getCssName('hidden'));
|
||||
goog.events.listen(remBtn, goog.events.EventType.CLICK,
|
||||
goog.bind(this.onIpRemove_, this, remBtn));
|
||||
this.typeCounts['reg-ips']++;
|
||||
ipInputElt.value = '';
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Click handler for IP remove button.
|
||||
* @param {!Element} remBtn The remove button.
|
||||
* @private
|
||||
*/
|
||||
registry.registrar.SecuritySettings.prototype.onIpRemove_ =
|
||||
function(remBtn) {
|
||||
goog.dom.removeNode(goog.dom.getParentElement(remBtn));
|
||||
this.typeCounts['reg-ips']--;
|
||||
};
|
49
java/google/registry/ui/js/registrar/whois_settings.js
Normal file
49
java/google/registry/ui/js/registrar/whois_settings.js
Normal file
|
@ -0,0 +1,49 @@
|
|||
// Copyright 2016 The Domain Registry Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
goog.provide('registry.registrar.WhoisSettings');
|
||||
|
||||
goog.require('goog.Uri');
|
||||
goog.require('goog.dom');
|
||||
goog.require('registry.Resource');
|
||||
goog.require('registry.ResourceComponent');
|
||||
goog.require('registry.soy.registrar.whois');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* WHOIS Settings page.
|
||||
* @param {!registry.registrar.Console} console
|
||||
* @param {string} xsrfToken Cross-site request forgery protection token.
|
||||
* @constructor
|
||||
* @extends {registry.ResourceComponent}
|
||||
* @final
|
||||
*/
|
||||
registry.registrar.WhoisSettings = function(console, xsrfToken) {
|
||||
registry.registrar.WhoisSettings.base(
|
||||
this,
|
||||
'constructor',
|
||||
console,
|
||||
new registry.Resource(new goog.Uri('/registrar-settings'), xsrfToken),
|
||||
registry.soy.registrar.whois.settings,
|
||||
null);
|
||||
};
|
||||
goog.inherits(registry.registrar.WhoisSettings, registry.ResourceComponent);
|
||||
|
||||
|
||||
/** @override */
|
||||
registry.registrar.WhoisSettings.prototype.bindToDom = function(id) {
|
||||
registry.registrar.WhoisSettings.base(this, 'bindToDom', 'fake');
|
||||
goog.dom.removeNode(goog.dom.getRequiredElement('reg-app-btn-back'));
|
||||
};
|
217
java/google/registry/ui/js/registrar/xml_resource_component.js
Normal file
217
java/google/registry/ui/js/registrar/xml_resource_component.js
Normal file
|
@ -0,0 +1,217 @@
|
|||
// Copyright 2016 The Domain Registry Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
goog.provide('registry.registrar.XmlResourceComponent');
|
||||
|
||||
goog.require('goog.dom');
|
||||
goog.require('goog.dom.TagName');
|
||||
goog.require('goog.dom.classlist');
|
||||
goog.require('registry.EditItem');
|
||||
goog.require('registry.util');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* The ResourceComponent class respresents server state for a named
|
||||
* resource and binds UI CRUD operations on it, or its constituent
|
||||
* collection.
|
||||
* @param {function()} itemTmpl
|
||||
* @param {!Object} eppTmpls Epp xml templates for info requests.
|
||||
* @param {!registry.registrar.Console} console
|
||||
* @constructor
|
||||
* @extends {registry.EditItem}
|
||||
*/
|
||||
registry.registrar.XmlResourceComponent = function(
|
||||
itemTmpl, eppTmpls, console) {
|
||||
registry.registrar.XmlResourceComponent.base(
|
||||
this, 'constructor', console, itemTmpl);
|
||||
|
||||
/** @type {!Object} */
|
||||
this.eppTmpls = eppTmpls;
|
||||
};
|
||||
goog.inherits(registry.registrar.XmlResourceComponent, registry.EditItem);
|
||||
|
||||
|
||||
/** @override */
|
||||
registry.registrar.XmlResourceComponent.prototype.bindToDom =
|
||||
function(id) {
|
||||
// XXX: EPP resources still use null state.
|
||||
registry.registrar.XmlResourceComponent.base(
|
||||
this, 'bindToDom', (id || ''));
|
||||
if (id) {
|
||||
this.fetchItem(id);
|
||||
} else {
|
||||
// Start edit of empty object.
|
||||
this.edit();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
registry.registrar.XmlResourceComponent.prototype.fetchItem = function(id) {
|
||||
var queryParams = {id: id, clTrid: 'abc-1234'};
|
||||
var xml = this.prepareFetch(queryParams);
|
||||
this.console.session.send(xml, goog.bind(this.handleFetchItem, this, id));
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
registry.registrar.XmlResourceComponent.prototype.handleFetchItem =
|
||||
function(id, rsp) {
|
||||
this.model = rsp;
|
||||
var result = rsp['epp']['response']['result'];
|
||||
var resCode = result['@code'];
|
||||
// XXX: Should use enum.
|
||||
if (resCode == 1000) { // OK
|
||||
this.model.readonly = true;
|
||||
this.processItem();
|
||||
this.renderItem(this.model);
|
||||
} else if (resCode == 2303) { // Missing
|
||||
registry.util.butter(
|
||||
'Could not find: "' + id + '". Please enter as "type/id"');
|
||||
} else {
|
||||
// includes general failure.
|
||||
registry.util.butter(resCode + ':' + result['msg']['keyValue']);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sublcasses should override to populate create queryParams with form
|
||||
* fields as needed. {@code queryParams.nextId} MUST be set to the
|
||||
* new object's ID.
|
||||
* @param {!Object} queryParams
|
||||
*/
|
||||
registry.registrar.XmlResourceComponent.prototype.prepareCreate =
|
||||
goog.abstractMethod;
|
||||
|
||||
|
||||
/**
|
||||
* Calls prepareCreate with template params and then send the returned
|
||||
* XML to the server
|
||||
* @override
|
||||
*/
|
||||
registry.registrar.XmlResourceComponent.prototype.sendCreate = function() {
|
||||
var form = registry.util.parseForm('item');
|
||||
var queryParams = {item: form, clTrid: 'abc-1234'};
|
||||
var xml = this.prepareCreate(queryParams);
|
||||
this.nextId = queryParams.nextId;
|
||||
this.console.session.send(xml, goog.bind(this.handleUpdateResponse, this));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Calls prepareUpdate with template params and then send the returned
|
||||
* XML to the server.
|
||||
* @override
|
||||
*/
|
||||
registry.registrar.XmlResourceComponent.prototype.sendUpdate = function() {
|
||||
var form = registry.util.parseForm('item');
|
||||
var queryParams = {item: form, clTrid: 'abc-1234'};
|
||||
var xml = this.prepareUpdate(queryParams);
|
||||
this.nextId = queryParams.nextId;
|
||||
this.console.session.send(xml, goog.bind(this.handleUpdateResponse, this));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sublcasses should override to populate fetch queryParams with form
|
||||
* fields as needed.
|
||||
* @param {!Object} queryParams
|
||||
* @return {string} EPP xml string
|
||||
*/
|
||||
registry.registrar.XmlResourceComponent.prototype.prepareFetch =
|
||||
function(queryParams) {
|
||||
return this.eppTmpls.info(queryParams).toString();
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
registry.registrar.XmlResourceComponent.prototype.handleUpdateResponse =
|
||||
function(rsp) {
|
||||
var result = rsp['epp']['response']['result'];
|
||||
if (result['@code'] == 1000) {
|
||||
// XXX: Consider timer, probably just as a seconds arg with impl in butter.
|
||||
registry.util.butter('Saved.');
|
||||
this.fetchItem(this.nextId || '');
|
||||
this.nextId = null;
|
||||
this.toggleEdit();
|
||||
} else {
|
||||
registry.util.butter(result['msg']['keyValue']);
|
||||
}
|
||||
return rsp;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Helper to add add/remove hosts/contacts on queryParams:
|
||||
*
|
||||
* queryParams[op + fieldType] = formFields.
|
||||
*
|
||||
* @param {Array.<Object>} formFields named form item representations
|
||||
* that have an associated {op: (add|rem)} attribute.
|
||||
* @param {string} fieldType capitalized pluralized type name, e.g. "Contacts".
|
||||
* @param {!Object} queryParams
|
||||
*/
|
||||
registry.registrar.XmlResourceComponent.prototype.addRem =
|
||||
function(formFields, fieldType, queryParams) {
|
||||
var add = [];
|
||||
var rem = [];
|
||||
for (var i = 0; i < formFields.length; i++) {
|
||||
var formField = formFields[i];
|
||||
var op = formField['op'];
|
||||
switch (op) {
|
||||
case 'add':
|
||||
add.push(formField);
|
||||
break;
|
||||
case 'rem':
|
||||
rem.push(formField);
|
||||
break;
|
||||
default:
|
||||
registry.util.log(
|
||||
'Unknown op attribute ' + op + ' on form field:',
|
||||
formField);
|
||||
}
|
||||
}
|
||||
if (add.length > 0) {
|
||||
queryParams['add' + fieldType] = add;
|
||||
}
|
||||
if (rem.length > 0) {
|
||||
queryParams['rem' + fieldType] = rem;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Make the given form input rows removable.
|
||||
* @param {NodeList} elts array of input elements.
|
||||
* @param {function()=} opt_onclickCb called when remove button is clicked.
|
||||
*/
|
||||
registry.registrar.XmlResourceComponent.prototype.formInputRowRemovable =
|
||||
function(elts, opt_onclickCb) {
|
||||
for (var i = 0; i < elts.length; i++) {
|
||||
var parent = goog.dom.getParentElement(elts[i]);
|
||||
this.appendRemoveBtn(parent, goog.partial(function(elt) {
|
||||
var eppRemInput = goog.dom.createDom(goog.dom.TagName.INPUT, {
|
||||
'name': elt.name.replace(/\.value/, '.op'),
|
||||
'value': 'rem',
|
||||
'type': 'hidden'
|
||||
});
|
||||
goog.dom.appendChild(goog.dom.getParentElement(elt), eppRemInput);
|
||||
goog.dom.classlist.add(
|
||||
goog.dom.getParentElement(
|
||||
goog.dom.getParentElement(elt)), goog.getCssName('hidden'));
|
||||
}, elts[i]));
|
||||
}
|
||||
};
|
92
java/google/registry/ui/js/resource.js
Normal file
92
java/google/registry/ui/js/resource.js
Normal file
|
@ -0,0 +1,92 @@
|
|||
// Copyright 2016 The Domain Registry Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
goog.provide('registry.Resource');
|
||||
|
||||
goog.require('goog.json');
|
||||
goog.require('registry.Session');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Provide a CRUD view of a server resource.
|
||||
*
|
||||
* @param {!goog.Uri} baseUri Target RESTful resource.
|
||||
* @param {string} xsrfToken Security token to pass back to the server.
|
||||
* @extends {registry.Session}
|
||||
* @constructor
|
||||
*/
|
||||
registry.Resource = function(baseUri, xsrfToken) {
|
||||
registry.Resource.base(this, 'constructor', baseUri, xsrfToken,
|
||||
registry.Session.ContentType.JSON);
|
||||
};
|
||||
goog.inherits(registry.Resource, registry.Session);
|
||||
|
||||
|
||||
/**
|
||||
* Get the resource from the server.
|
||||
*
|
||||
* @param {!Object} args Params for server. Do not set the 'op' field on args.
|
||||
* @param {!Function} callback For retrieved resource.
|
||||
*/
|
||||
registry.Resource.prototype.read = function(args, callback) {
|
||||
this.send_('read', args, callback);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Create the resource on the server.
|
||||
*
|
||||
* @param {!Object} args params for server. Do not set the 'op' field on args.
|
||||
* @param {!Function} callback on success.
|
||||
* @param {string} newId name for the new resource.
|
||||
* @throws {!Exception} if the 'op' field is set on args.
|
||||
*/
|
||||
registry.Resource.prototype.create = function(args, callback, newId) {
|
||||
this.send_('create', args, callback, newId);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Create the resource on the server.
|
||||
*
|
||||
* @param {!Object} args params for server. Do not set the 'op' field on args.
|
||||
* @param {!Function} callback on success.
|
||||
* @throws {!Exception} if the 'op' field is set on args.
|
||||
*/
|
||||
registry.Resource.prototype.update = function(args, callback) {
|
||||
this.send_('update', args, callback);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* RESTful access to resources on the server.
|
||||
*
|
||||
* @param {string} opCode One of (create|read|update)
|
||||
* @param {!Object} argsObj arguments for the operation.
|
||||
* @param {!Function} callback For XhrIo result throws.
|
||||
* @param {string=} opt_newId name for the new resource.
|
||||
* @private
|
||||
*/
|
||||
registry.Resource.prototype.send_ =
|
||||
function(opCode, argsObj, callback, opt_newId) {
|
||||
// NB: must be declared this way in order to avoid compiler renaming
|
||||
var req = {};
|
||||
req['op'] = opCode;
|
||||
req['args'] = argsObj;
|
||||
if (opt_newId) {
|
||||
this.uri.setPath(this.uri.getPath() + '/' + opt_newId);
|
||||
}
|
||||
this.sendXhrIo(goog.json.serialize(req), callback);
|
||||
};
|
175
java/google/registry/ui/js/resource_component.js
Normal file
175
java/google/registry/ui/js/resource_component.js
Normal file
|
@ -0,0 +1,175 @@
|
|||
// Copyright 2016 The Domain Registry Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
goog.provide('registry.ResourceComponent');
|
||||
|
||||
goog.require('goog.dom');
|
||||
goog.require('goog.json');
|
||||
goog.require('registry.EditItem');
|
||||
goog.require('registry.forms');
|
||||
goog.require('registry.util');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* The ResourceComponent class respresents server state for a named
|
||||
* resource and binds UI CRUD operations on it, or its constituent
|
||||
* collection.
|
||||
* @param {!registry.Console} console console singleton.
|
||||
* @param {!registry.Resource} resource the RESTful resource.
|
||||
* @param {!Function} itemTmpl
|
||||
* @param {Function} renderSetCb may be null if this resource is only
|
||||
* ever an item.
|
||||
* @constructor
|
||||
* @extends {registry.EditItem}
|
||||
*/
|
||||
registry.ResourceComponent = function(
|
||||
console,
|
||||
resource,
|
||||
itemTmpl,
|
||||
renderSetCb) {
|
||||
registry.ResourceComponent.base(this, 'constructor', console, itemTmpl);
|
||||
|
||||
/** @type {!registry.Resource} */
|
||||
this.resource = resource;
|
||||
|
||||
/** @type {Function} */
|
||||
this.renderSetCb = renderSetCb;
|
||||
};
|
||||
goog.inherits(registry.ResourceComponent, registry.EditItem);
|
||||
|
||||
|
||||
/** @override */
|
||||
registry.ResourceComponent.prototype.bindToDom = function(id) {
|
||||
registry.ResourceComponent.base(this, 'bindToDom', id);
|
||||
this.fetchItem(id);
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
registry.ResourceComponent.prototype.back = function() {
|
||||
this.console.view(this.basePath);
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
registry.ResourceComponent.prototype.fetchItem = function(id) {
|
||||
this.resource.read({}, goog.bind(this.handleFetchItem, this, id));
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
registry.ResourceComponent.prototype.handleFetchItem = function(id, rsp) {
|
||||
// XXX: Two different protocols are supported here. The new style used in the
|
||||
// registrar console is first, followed by the item/set style used by
|
||||
// the admin console.
|
||||
if ('status' in rsp) {
|
||||
// New style.
|
||||
if (rsp.status == 'SUCCESS') {
|
||||
this.model = rsp.results[0];
|
||||
this.model.readonly = true;
|
||||
this.processItem();
|
||||
this.renderItem(this.model);
|
||||
} else {
|
||||
// XXX: Happens if the server restarts while the client has an
|
||||
// open-session. This should retry.
|
||||
registry.forms.resetErrors();
|
||||
registry.forms.displayError(rsp['message'], rsp['field']);
|
||||
}
|
||||
} else if ('item' in rsp) {
|
||||
this.model = rsp.item;
|
||||
this.model.readonly = true;
|
||||
this.processItem();
|
||||
this.renderItem(this.model);
|
||||
} else if ('set' in rsp && this.renderSetCb != null) {
|
||||
// XXX: This conditional logic should be hoisted to edit_item when
|
||||
// collection support is improved.
|
||||
goog.dom.removeChildren(goog.dom.getRequiredElement('reg-appbar'));
|
||||
this.renderSetCb(goog.dom.getRequiredElement('reg-content'), rsp);
|
||||
} else {
|
||||
registry.util.log('unknown message type in handleFetchItem');
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
registry.ResourceComponent.prototype.sendUpdate = function() {
|
||||
var modelCopy = /** @type {!Object}
|
||||
*/ (goog.json.parse(goog.json.serialize(this.model)));
|
||||
this.prepareUpdate(modelCopy);
|
||||
if (this.id) {
|
||||
this.resource.update(modelCopy, goog.bind(this.handleUpdateResponse, this));
|
||||
} else {
|
||||
this.resource.update(modelCopy, goog.bind(this.handleCreateResponse, this));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
registry.ResourceComponent.prototype.prepareUpdate = function(modelCopy) {
|
||||
var form = registry.util.parseForm('item');
|
||||
for (var ndx in form) {
|
||||
modelCopy[ndx] = form[ndx];
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
registry.ResourceComponent.prototype.handleUpdateResponse = function(rsp) {
|
||||
if (rsp.status) {
|
||||
if (rsp.status != 'SUCCESS') {
|
||||
registry.forms.resetErrors();
|
||||
registry.forms.displayError(rsp['message'], rsp['field']);
|
||||
return rsp;
|
||||
}
|
||||
// XXX: Vestigial state from admin console. Shouldn't be possible to be
|
||||
// null.
|
||||
if (this.id) {
|
||||
this.fetchItem(this.id || '');
|
||||
this.toggleEdit();
|
||||
}
|
||||
return rsp;
|
||||
}
|
||||
// XXX: Should be removed when admin console uses new response format.
|
||||
if (rsp instanceof Object && 'results' in rsp) {
|
||||
registry.util.butter(rsp['results']);
|
||||
this.bindToDom(this.nextId || '');
|
||||
this.nextId = null;
|
||||
} else {
|
||||
registry.util.butter(rsp.toString());
|
||||
}
|
||||
return rsp;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Handle resource create response.
|
||||
* @param {!Object} rsp Decoded JSON response from the server.
|
||||
* @return {!Object} a result object describing next steps. On
|
||||
* success, if next is defined, visit(ret.next) is called, otherwise
|
||||
* if err is set, the butterbar message is set to it.
|
||||
*/
|
||||
registry.ResourceComponent.prototype.handleCreateResponse =
|
||||
goog.abstractMethod;
|
||||
|
||||
|
||||
/**
|
||||
* Handle resource delete response.
|
||||
* @param {!Object} rsp Decoded JSON response from the server.
|
||||
* @return {!Object} a result object describing next steps. On
|
||||
* success, if next is defined, visit(ret.next) is called, otherwise
|
||||
* if err is set, the butterbar message is set to it.
|
||||
*/
|
||||
registry.ResourceComponent.prototype.handleDeleteResponse =
|
||||
goog.abstractMethod;
|
125
java/google/registry/ui/js/session.js
Normal file
125
java/google/registry/ui/js/session.js
Normal file
|
@ -0,0 +1,125 @@
|
|||
// Copyright 2016 The Domain Registry Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
goog.provide('registry.Session');
|
||||
|
||||
goog.require('goog.json');
|
||||
goog.require('goog.net.XhrIo');
|
||||
goog.require('goog.structs.Map');
|
||||
goog.require('registry.util');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* XHR launcher for both JSON and XML requests.
|
||||
* @param {!goog.Uri} defaultUri URI to which requests are POSTed.
|
||||
* @param {string} xsrfToken Cross-site request forgery protection token.
|
||||
* @param {!registry.Session.ContentType} contentType Payload mode.
|
||||
* @constructor
|
||||
* @template REQUEST, RESPONSE
|
||||
*/
|
||||
registry.Session = function(defaultUri, xsrfToken, contentType) {
|
||||
|
||||
/**
|
||||
* URI to which requests are posted.
|
||||
* @protected {!goog.Uri}
|
||||
* @const
|
||||
*/
|
||||
this.uri = defaultUri;
|
||||
|
||||
/**
|
||||
* Content type set in request body.
|
||||
* @private {!registry.Session.ContentType}
|
||||
* @const
|
||||
*/
|
||||
this.contentType_ = contentType;
|
||||
|
||||
/**
|
||||
* XHR request headers.
|
||||
* @private {!goog.structs.Map.<string, string>}
|
||||
* @const
|
||||
*/
|
||||
this.headers_ = new goog.structs.Map(
|
||||
'Content-Type', contentType,
|
||||
'X-CSRF-Token', xsrfToken,
|
||||
'X-Requested-With', 'XMLHttpRequest');
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Payload modes supported by this class.
|
||||
* @enum {string}
|
||||
*/
|
||||
registry.Session.ContentType = {
|
||||
JSON: 'application/json; charset=utf-8',
|
||||
EPP: 'application/epp+xml'
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Abstract method to send a request to the server.
|
||||
* @param {REQUEST} body HTTP request body as a string or JSON object.
|
||||
* @param {function(RESPONSE)} onSuccess XHR success callback.
|
||||
* @param {function(string)=} opt_onError XHR error callback. The default action
|
||||
* is to show a bloody butterbar.
|
||||
* @final
|
||||
*/
|
||||
registry.Session.prototype.sendXhrIo =
|
||||
function(body, onSuccess, opt_onError) {
|
||||
goog.net.XhrIo.send(
|
||||
this.uri.toString(),
|
||||
goog.bind(this.onXhrComplete_, this, onSuccess,
|
||||
opt_onError || goog.bind(this.displayError_, this)),
|
||||
'POST',
|
||||
goog.isObject(body) ? goog.json.serialize(body) : body,
|
||||
this.headers_);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Handler invoked when an asynchronous request is complete.
|
||||
* @param {function(RESPONSE)} onSuccess Success callback.
|
||||
* @param {function(string)} onError Success callback.
|
||||
* @param {{target: !goog.net.XhrIo}} e XHR event.
|
||||
* @private
|
||||
*/
|
||||
registry.Session.prototype.onXhrComplete_ = function(onSuccess, onError, e) {
|
||||
if (e.target.isSuccess()) {
|
||||
onSuccess(/** @type {!RESPONSE} */ (
|
||||
this.contentType_ == registry.Session.ContentType.JSON ?
|
||||
e.target.getResponseJson(registry.Session.PARSER_BREAKER_) :
|
||||
e.target.getResponseXml()));
|
||||
} else {
|
||||
onError(e.target.getLastError());
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* JSON response prefix which prevents evaluation.
|
||||
* @private {string}
|
||||
* @const
|
||||
*/
|
||||
registry.Session.PARSER_BREAKER_ = ')]}\'\n';
|
||||
|
||||
|
||||
/**
|
||||
* Displays {@code message} to user in bloody butterbar.
|
||||
* @param {string} message
|
||||
* @private
|
||||
*/
|
||||
registry.Session.prototype.displayError_ = function(message) {
|
||||
registry.util.butter(
|
||||
this.uri.toString() + ': ' + message + '. Please reload.', true);
|
||||
};
|
216
java/google/registry/ui/js/util.js
Normal file
216
java/google/registry/ui/js/util.js
Normal file
|
@ -0,0 +1,216 @@
|
|||
// Copyright 2016 The Domain Registry Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
goog.provide('registry.util');
|
||||
|
||||
goog.require('goog.dom');
|
||||
goog.require('goog.dom.classlist');
|
||||
goog.require('goog.soy');
|
||||
|
||||
|
||||
/**
|
||||
* Logging function that delegates to {@code console.log()}.
|
||||
* @param {...*} var_args
|
||||
*/
|
||||
registry.util.log = function(var_args) {
|
||||
if (goog.DEBUG) {
|
||||
if (goog.isDef(goog.global.console) &&
|
||||
goog.isDef(goog.global.console['log'])) {
|
||||
goog.global.console.log.apply(goog.global.console, arguments);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* CSS class for hiding an element whose visibility can be toggled.
|
||||
* @type {string}
|
||||
* @const
|
||||
*/
|
||||
registry.util.cssHidden = goog.getCssName('hidden');
|
||||
|
||||
|
||||
/**
|
||||
* CSS class for showing an element whose visibility can be toggled.
|
||||
* @type {string}
|
||||
* @const
|
||||
*/
|
||||
registry.util.cssShown = goog.getCssName('shown');
|
||||
|
||||
|
||||
/**
|
||||
* Changes element visibility by toggling CSS {@code shown} to {@code hidden}.
|
||||
* @param {!Element|string} element Element or id attribute of element.
|
||||
* @param {boolean} visible Shows {@code element} if true, or else hides it.
|
||||
*/
|
||||
registry.util.setVisible = function(element, visible) {
|
||||
goog.dom.classlist.addRemove(
|
||||
goog.dom.getElement(element),
|
||||
visible ? registry.util.cssHidden : registry.util.cssShown,
|
||||
visible ? registry.util.cssShown : registry.util.cssHidden);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Show a buttebar with the given message. A dismiss link will be added.
|
||||
* @param {string} message the message to show the user.
|
||||
* @param {boolean=} opt_isFatal indicates butterbar should be blood red. This
|
||||
* should only be used when an RPC returns a non-200 status.
|
||||
*/
|
||||
registry.util.butter = function(message, opt_isFatal) {
|
||||
goog.dom.setTextContent(
|
||||
goog.dom.getElementByClass(goog.getCssName('kd-butterbar-text')),
|
||||
message);
|
||||
var butterbar =
|
||||
goog.dom.getRequiredElementByClass(goog.getCssName('kd-butterbar'));
|
||||
registry.util.setVisible(butterbar, true);
|
||||
if (opt_isFatal) {
|
||||
goog.dom.classlist.add(butterbar, goog.getCssName('fatal'));
|
||||
} else {
|
||||
goog.dom.classlist.remove(butterbar, goog.getCssName('fatal'));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Hides the butterbar.
|
||||
*/
|
||||
registry.util.unbutter = function() {
|
||||
registry.util.setVisible(
|
||||
goog.dom.getRequiredElementByClass(goog.getCssName('kd-butterbar')),
|
||||
false);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Renders the tmpl at and then moves it before the given elt.
|
||||
* @param {Element|string} id dom id of the refElt to render before.
|
||||
* @param {function()} tmpl template to render.
|
||||
* @param {!Object} tmplParams params to pass to the template.
|
||||
* @return {!Element} the rendered row.
|
||||
*/
|
||||
registry.util.renderBeforeRow = function(id, tmpl, tmplParams) {
|
||||
var refElt = goog.dom.getElement(id);
|
||||
goog.soy.renderElement(refElt, tmpl, tmplParams);
|
||||
var prevSib = goog.dom.getPreviousElementSibling(refElt);
|
||||
goog.dom.removeNode(refElt);
|
||||
refElt.removeAttribute('id');
|
||||
goog.dom.insertSiblingAfter(refElt, prevSib);
|
||||
var newAnchorRefElt = goog.dom.createDom(refElt.tagName, {'id': id});
|
||||
goog.dom.insertSiblingAfter(newAnchorRefElt, refElt);
|
||||
return refElt;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Turns an HTML form's named elements into a JSON data structure with
|
||||
* Pablo's black magick.
|
||||
* @param {string} formName the name of the form to be parsed.
|
||||
* @throws {Error} if {@code formName} couldn't be found.
|
||||
* @return {!Object} the parsed form values as an object.
|
||||
*/
|
||||
registry.util.parseForm = function(formName) {
|
||||
var form = /** @type {HTMLFormElement}
|
||||
*/ (document.querySelector('form[name=' + formName + ']'));
|
||||
if (form == null) {
|
||||
throw new Error('No such form named ' + formName);
|
||||
}
|
||||
var obj = {};
|
||||
// Find names first, since e.g. radio buttons have two elts with the
|
||||
// same name.
|
||||
var eltNames = {};
|
||||
for (var i = 0; i < form.elements.length; i++) {
|
||||
var elt = form.elements[i];
|
||||
if (elt.name == '') {
|
||||
continue;
|
||||
}
|
||||
eltNames[elt.name] = null;
|
||||
}
|
||||
for (var eltName in eltNames) {
|
||||
var elt = form.elements[eltName];
|
||||
var val;
|
||||
if (elt.type == 'checkbox') {
|
||||
val = elt.checked;
|
||||
} else {
|
||||
val = elt.value;
|
||||
}
|
||||
registry.util.expandObject_(obj, eltName, val);
|
||||
}
|
||||
return obj;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Give the object a value at the given path. Paths are split on dot
|
||||
* and array elements are recognized.
|
||||
*
|
||||
* <ul>
|
||||
* <li>a = 1
|
||||
* <li>foo:a = 1.5
|
||||
* <li>b.c = 2
|
||||
* <li>b.d = 3
|
||||
* <li>c[0] = 4
|
||||
* <li>c[1] = 5
|
||||
* <li>d[0].a = 6
|
||||
* <li>d[1].foo:b = 7
|
||||
* <li>d[1].@b = 8
|
||||
* </ul>
|
||||
*
|
||||
* Yields the following object:
|
||||
* <pre>
|
||||
* {
|
||||
* a: '1',
|
||||
* 'foo:a': '1.5',
|
||||
* b: {
|
||||
* c: '2',
|
||||
* d: '3'
|
||||
* },
|
||||
* c: ['4','5'],
|
||||
* d: [{a:'6'},{'foo:b':'7'},{'@b':'8'}]
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* @param {!Object} obj
|
||||
* @param {string} pathSpec
|
||||
* @param {string|Array.<string>|null} val
|
||||
* @private
|
||||
*/
|
||||
registry.util.expandObject_ = function(obj, pathSpec, val) {
|
||||
var path = pathSpec.split('.');
|
||||
for (var p = 0; p < path.length; p++) {
|
||||
var fieldName = path[p];
|
||||
var arrElt = fieldName.match(/^([\w:]+)\[(\d+)\]$/);
|
||||
if (arrElt) {
|
||||
var arrName = arrElt[1];
|
||||
var arrNdx = arrElt[2];
|
||||
if (!obj[arrName]) {
|
||||
obj[arrName] = [];
|
||||
}
|
||||
obj = obj[arrName];
|
||||
fieldName = arrNdx;
|
||||
if (!obj[arrNdx]) {
|
||||
obj[arrNdx] = {};
|
||||
}
|
||||
} else {
|
||||
if (!obj[fieldName]) {
|
||||
obj[fieldName] = {};
|
||||
}
|
||||
}
|
||||
if (p == path.length - 1) {
|
||||
obj[fieldName] = val;
|
||||
} else {
|
||||
obj = obj[fieldName];
|
||||
}
|
||||
}
|
||||
};
|
89
java/google/registry/ui/js/xml.js
Normal file
89
java/google/registry/ui/js/xml.js
Normal file
|
@ -0,0 +1,89 @@
|
|||
// Copyright 2016 The Domain Registry Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
goog.provide('registry.xml');
|
||||
goog.provide('registry.xml.XmlJson');
|
||||
|
||||
goog.require('goog.dom.NodeType');
|
||||
goog.require('goog.object');
|
||||
|
||||
|
||||
/**
|
||||
* Turns XML document into a JSON data structure. This function is similar to
|
||||
* <a href="https://developer.mozilla.org/en-US/docs/JXON">Mozilla JXON</a>
|
||||
* except it handles text differently. This routine will not coalesce text
|
||||
* interspersed with elements. This routine does not coerce string types to
|
||||
* number, boolean, or null.
|
||||
* @param {!Node} node
|
||||
* @return {!Object<string, registry.xml.XmlJson>}
|
||||
* @throws {Error} upon encountering interspersed text.
|
||||
*/
|
||||
registry.xml.convertToJson = function(node) {
|
||||
var result = goog.object.create();
|
||||
if (goog.isDefAndNotNull(node.attributes)) {
|
||||
for (var i = 0; i < node.attributes.length; i++) {
|
||||
var attr = node.attributes.item(i);
|
||||
goog.object.set(result, '@' + attr.name, attr.value || '');
|
||||
}
|
||||
}
|
||||
for (var j = 0; j < node.childNodes.length; j++) {
|
||||
var child = node.childNodes.item(j);
|
||||
switch (child.nodeType) {
|
||||
case goog.dom.NodeType.TEXT:
|
||||
case goog.dom.NodeType.CDATA_SECTION:
|
||||
var text = String(child.nodeValue).trim();
|
||||
if (text != '') {
|
||||
var curr = goog.object.get(result, registry.xml.jsonValueFieldName_);
|
||||
if (goog.isDef(curr)) {
|
||||
throw new Error(
|
||||
'XML text "' + curr + '" interspersed with "' + text + '"');
|
||||
}
|
||||
goog.object.set(result, registry.xml.jsonValueFieldName_, text);
|
||||
}
|
||||
break;
|
||||
case goog.dom.NodeType.ELEMENT:
|
||||
var json = registry.xml.convertToJson(child);
|
||||
var name = child.nodeName;
|
||||
if (goog.object.containsKey(result, name)) {
|
||||
var field = goog.object.get(result, name);
|
||||
if (goog.isArray(field)) {
|
||||
field.push(json);
|
||||
} else {
|
||||
goog.object.set(result, name, [field, json]);
|
||||
}
|
||||
} else {
|
||||
goog.object.set(result, name, json);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* XML JSON object recursive type definition.
|
||||
* @typedef {(string|
|
||||
* !Array<registry.xml.XmlJson>|
|
||||
* !Object<string, registry.xml.XmlJson>)}
|
||||
*/
|
||||
registry.xml.XmlJson;
|
||||
|
||||
|
||||
/**
|
||||
* XML JSON value field name, inherited from JXON.
|
||||
* @private {string}
|
||||
* @const
|
||||
*/
|
||||
registry.xml.jsonValueFieldName_ = 'keyValue';
|
Loading…
Add table
Add a link
Reference in a new issue