[Kimchi-devel] [PATCH V2] Add nfs server and target UI in create storage pool
Yu Xin Huo
huoyuxin at linux.vnet.ibm.com
Fri Jan 3 08:18:10 UTC 2014
On 12/30/2013 5:42 PM, zhoumeina wrote:
> v1-v2 Fix some bug in this patch, make it working and retest
> This patch is working adding a select box in create nfs pool,
> when user don't want to input the server ip or name, he can
> simply select "used the server I have used before" and choose
> a server in a select box, this is more convinent.
>
> Add a new jquery filter-select widget
>
> In order to show server target I did a select box in UI, but sometimes
> maybe the export path number is large. So I made a filter-select
> widget to make user easier to choose the export path he want.
> This is the first jquery widget. And let us make all common widget to
> jquery widget from now on.
>
> Signed-off-by: zhoumeina <zhoumein at linux.vnet.ibm.com>
> ---
> ui/css/theme-default/button.css | 11 +--
> ui/css/theme-default/form.css | 6 ++
> ui/css/theme-default/storage.css | 4 +-
> ui/js/Makefile.am | 4 +-
> ui/js/src/kimchi.api.js | 26 ++++++-
> ui/js/src/kimchi.storagepool_add_main.js | 73 +++++++++++++++--
> ui/js/src/kimchi.utils.js | 11 +++
> ui/js/widgets/filter-select.js | 137 ++++++++++++++++++++++++++++++
> ui/js/widgets/select-menu.js | 86 +++++++++++++++++++
> ui/pages/i18n.html.tmpl | 5 +-
> ui/pages/storagepool-add.html.tmpl | 41 +++++++--
> 11 files changed, 376 insertions(+), 28 deletions(-)
> create mode 100644 ui/js/widgets/filter-select.js
> create mode 100644 ui/js/widgets/select-menu.js
>
> diff --git a/ui/css/theme-default/button.css b/ui/css/theme-default/button.css
> index c7ed3f6..f99679a 100644
> --- a/ui/css/theme-default/button.css
> +++ b/ui/css/theme-default/button.css
> @@ -247,17 +247,16 @@
> .btn-select {
> display: inline-block;
> position: relative;
> - height: 38px;
> + height: 30px;
> padding-right: 20px;
> margin: 5px;
> vertical-align: top;
> -webkit-border-radius: 5px;
> -moz-border-radius: 5px;
> - border-radius: 5px;
> - background: #eee;
> + background: #fff;
> box-shadow: -1px -1px 1px #666, 1px 1px 1px #fff, 2px 2px 2px rgba(0, 0, 0, .15) inset;
> font-size: 13px;
> - line-height: 38px;
> + line-height: 30px;
> text-align: left;
> cursor: pointer;
> }
> @@ -269,8 +268,8 @@
> .btn-select .arrow {
> position: absolute;
> width: 15px;
> - height: 38px;
> - line-height: 38px;
> + height: 30px;
> + line-height: 30px;
> top: 0;
> right: 5px;
> background: url(../images/theme-default/arrow-down-black.png) no-repeat center center;
> diff --git a/ui/css/theme-default/form.css b/ui/css/theme-default/form.css
> index c24b277..9db4bba 100644
> --- a/ui/css/theme-default/form.css
> +++ b/ui/css/theme-default/form.css
> @@ -45,3 +45,9 @@
> line-height: 30px;
> padding: 0 5px;
> }
> +
> +.text-help {
> + font-size: 12px;
> + color: #333;
> + margin: 0 0 5px 5px;
> +}
> diff --git a/ui/css/theme-default/storage.css b/ui/css/theme-default/storage.css
> index d81dc75..ae89f1b 100644
> --- a/ui/css/theme-default/storage.css
> +++ b/ui/css/theme-default/storage.css
> @@ -529,7 +529,7 @@
> width: 300px;
> display: inline-block;
> vertical-align: top;
> - padding: 5px 5px 5px 20px;
> + padding: 5px 5px 5px 22px;
> }
>
> .storage-type-wrapper-controls input[type="text"] {
> @@ -548,7 +548,7 @@
>
> .storage-type-wrapper-controls > .dropdown {
> margin: 5px 0 0 1px;
> - width: 250px;
> + width: 150px;
> }
>
> .storage-type-wrapper-controls input[type="text"][disabled] {
> diff --git a/ui/js/Makefile.am b/ui/js/Makefile.am
> index 337e369..8dfd4d6 100644
> --- a/ui/js/Makefile.am
> +++ b/ui/js/Makefile.am
> @@ -20,7 +20,7 @@
>
> SUBDIRS = novnc
>
> -EXTRA_DIST = src
> +EXTRA_DIST = src widgets
>
> jsdir = $(datadir)/kimchi/ui/js
>
> @@ -32,7 +32,7 @@ dist_js_DATA = \
> modernizr.custom.2.6.2.min.js \
> $(NULL)
>
> -kimchi.min.js: src/*.js
> +kimchi.min.js: widgets/*.js src/*.js
> cat $(sort $^) > $@
>
> CLEANFILES = kimchi.min.js
> diff --git a/ui/js/src/kimchi.api.js b/ui/js/src/kimchi.api.js
> index fbcf4a2..f6115a8 100644
> --- a/ui/js/src/kimchi.api.js
> +++ b/ui/js/src/kimchi.api.js
> @@ -681,5 +681,29 @@ var kimchi = {
> success : suc,
> error : err
> });
> + },
> +
> + getStorageServers: function(type, suc, err) {
> + var url = kimchi.url + 'storageservers?target_type=' + type;
> + kimchi.requestJSON({
> + url : url,
> + type : 'GET',
> + contentType : 'application/json',
> + dataType : 'json',
> + success : suc,
> + error : err
> + });
> + },
> +
> + getStorageTargets: function(server,type, suc, err) {
> + var url = kimchi.url + 'storageservers/' + server + '/storagetargets?target_type=' + type;
> + kimchi.requestJSON({
> + url : url,
> + type : 'GET',
> + contentType : 'application/json',
> + dataType : 'json',
> + success : suc,
> + error : err
> + });
> }
> -};
> +};
> \ No newline at end of file
> diff --git a/ui/js/src/kimchi.storagepool_add_main.js b/ui/js/src/kimchi.storagepool_add_main.js
> index b31610a..7955352 100644
> --- a/ui/js/src/kimchi.storagepool_add_main.js
> +++ b/ui/js/src/kimchi.storagepool_add_main.js
> @@ -51,7 +51,72 @@ kimchi.initStorageAddPage = function() {
> });
> $('.host-partition').html(listHtml);
> }
> - kimchi.select('storagePool-list', options);
> + $('#storagePool-list').selectMenu();
> + $('#storagePool-list').selectMenu("setData", options);
> + kimchi.getStorageServers('netfs', function(data) {
> + var serverContent = [];
> + serverContent.push({
> + label : i18n['select_default'],
> + value : ''
> + });
> + if (data.length > 0) {
> + $.each(data, function(index, value) {
> + serverContent.push({
> + label : value,
> + value : value
> + });
> + });
> + }
> + $('#nfs-server-used').selectMenu();
> + $('#nfs-server-used').selectMenu("setData", serverContent);
> + $('#nfs-server-target').filterselect();
> + $('input[name=nfsServerType]').change(function() {
> + if ($(this).val() === 'input') {
> + $('#nfsServerInputDiv').removeClass('tmpl-html');
> + $('#nfsServerChooseDiv').addClass('tmpl-html');
> + } else {
> + $('#nfsServerInputDiv').addClass('tmpl-html');
> + $('#nfsServerChooseDiv').removeClass('tmpl-html');
> + }
> + });
> + $('#nfsServerSelect').change(function() {
> + $('#nfsserverId').val($(this).val());
> + $('#nfsserverId').trigger("keydown");
> + });
> + $('#nfsserverId').on("keydown",function() {
> + if ($(this).val() !== '' && kimchi.isServer($(this).val())) {
> + $('#nfspathId').removeAttr('disabled');
> + } else {
> + $('#nfspathId').attr('disabled','disabled');
> + }
> + $('#nfs-server-target').filterselect('clear');
> + });
> + $('#nfspathId').focus(function() {
> + var targetContent = [];
> + kimchi.getStorageTargets($('#nfsserverId').val(), 'netfs', function(data) {
> + if (data.length > 0) {
> + $.each(data, function(index, value) {
> + targetContent.push({
> + label : value.target,
> + value : value.target
> + });
> + });
> + } else {
> + targetContent.push({
> + label : i18n['msg.no.result'],
> + value : ''
> + });
> + }
> + $('#nfs-server-target').filterselect("setData", targetContent);
> + },function() {
> + targetContent.push({
> + label : i18n['msg.no.result'],
> + value : ''
> + });
> + $('#nfs-server-target').filterselect("setData", targetContent);
> + });
> + });
> + });
> $('#poolType').change(function() {
> if ($(this).val() === 'dir') {
> $('.path-section').removeClass('tmpl-html');
> @@ -88,7 +153,6 @@ kimchi.validateForm = function() {
> } else {
> return kimchi.validateLogicalForm();
> }
> -
> };
>
> kimchi.validateDirForm = function () {
> @@ -116,11 +180,8 @@ kimchi.validateNfsForm = function () {
> kimchi.message.error(i18n['msg.pool.edit.nfspath.blank']);
> return false;
> }
> - var domain = "([0-9a-z_!~*'()-]+\.)*([0-9a-z][0-9a-z-]{0,61})?[0-9a-z]\.[a-z]{2,6}"
> - var ip = "(\\d{1,3}\.){3}\\d{1,3}"
> - regex = new RegExp('^' + domain + '|' + ip + '$')
>
> - if(!regex.test(nfsserver)) {
> + if(kimchi.isServer(nfsserver)) {
> kimchi.message.error(i18n['msg.validate.pool.edit.nfsserver']);
> return false;
> }
> diff --git a/ui/js/src/kimchi.utils.js b/ui/js/src/kimchi.utils.js
> index 8af6a11..6439db6 100644
> --- a/ui/js/src/kimchi.utils.js
> +++ b/ui/js/src/kimchi.utils.js
> @@ -163,3 +163,14 @@ kimchi.changetoProperUnit = function(numOrg, digits, base) {
>
> kimchi.formatMeasurement = format;
> })();
> +
> +kimchi.isServer = function(server) {
> + var domain = "([0-9a-z_!~*'()-]+\.)*([0-9a-z][0-9a-z-]{0,61})?[0-9a-z]\.[a-z]{2,6}"
> + var ip = "(\\d{1,3}\.){3}\\d{1,3}"
> + regex = new RegExp('^' + domain + '|' + ip + '$');
> + if(!regex.test(server)) {
> + return false;
> + } else {
> + return true;
> + }
> +};
> \ No newline at end of file
> diff --git a/ui/js/widgets/filter-select.js b/ui/js/widgets/filter-select.js
> new file mode 100644
> index 0000000..02fe5d9
> --- /dev/null
> +++ b/ui/js/widgets/filter-select.js
> @@ -0,0 +1,137 @@
> +/*
> + * Project Kimchi
> + *
> + * Copyright IBM, Corp. 2013
> + *
> + * Authors:
> + * zhoumeina <zhoumein at linux.vnet.ibm.com>
> + *
> + * 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.
> + */
> +(function($) {
> + $.widget('kimchi.filterselect', {
> + options : {
> + key : 0,
> + value : 0
> + },
'key', 'value' are not used, remove them.
> +
> + _create : function() {
> + this.listControl = this.element;
> + this.element.html('');
> + var targetId = this.listControl.data('target');
> + var labelId = this.listControl.data('label');
> + this.target = $('#' + targetId);
> + this.label = $('#' + labelId);
> + this.selectDiv = this.listControl.parent().parent();
> + this.selectDiv.addClass('btn-select dropdown popable');
> + this.listControl.parent().addClass('popover');
> + },
> +
> + _setOption : function(key, value) {
> + },
method body is empty, remove it.
> +
> + setData : function(options) {
> + this.element.html('');
> + var that = this;
> + var value = this.target.val();
> + var selectedClass = 'active';
> + var itemTag = 'li';
> +
> + that.listControl.on('click', itemTag, function(e) {
> + that.listControl.children().removeClass(selectedClass);
> + that.listControl.addClass(selectedClass);
> + that.target.text($(this).text());
> + var oldValue = that.target.val();
> + var newValue = $(this).data('value');
> + that.target.val(newValue);
> + if (oldValue !== newValue) {
> + that.target.change();
> + }
> + });
> +
> + that.selectDiv.click(function(e) {
> + that.listControl.html('');
> + var items = that._dataList(options);
> + $.each(items, function(index, item) {
> + that.listControl.append(item);
> + })
> + var isOpen = that.selectDiv.hasClass('open');
> + that.selectDiv.removeClass('open');
> + if (!isOpen && items.length > 0) {
> + that.selectDiv.addClass('open');
> + }
> + e.preventDefault();
> + e.stopPropagation();
> + });
> +
> + that.target.keyup(function(event) {
> + that.listControl.html('');
> + var items = that._dataList(options);
> + var temp = 0;
> + $.each(items, function(index, item) {
> + if (item.html().indexOf(that.target.val()) >= 0) {
> + that.listControl.append(item);
> + temp++;
> + }
> + });
> + if (that.listControl.html() === '') {
> + that.listControl.html(i18n['msg.storagepool.unvalid.path']);
> + }
> + if (temp > 0) {
> + that._open();
> + }
> + });
> + },
> +
> + _dataList : function(options) {
> + var item;
> + var itemTag = 'li';
> + var selectedClass = 'active';
> + var items = [];
> + var that = this;
> + $.each(options, function(index, option) {
> + item = $('<' + itemTag + '></' + itemTag + '>');
> + item.text(option.label);
> + item.data('value', option.value);
> + if (option.value === that.target.val()) {
> + item.addClass(selectedClass);
> + }
> + items.push(item);
> + });
> + return items;
> + },
> +
> + clear : function() {
> + this.target.val("");
> + },
> +
> + _open : function() {
> + var isOpen = this.selectDiv.hasClass('open');
> + if (!isOpen) {
> + this.selectDiv.addClass('open');
> + }
> + },
> +
> + _close : function() {
> + var isOpen = this.selectDiv.hasClass('open');
> + if (isOpen) {
> + this.selectDiv.removeClass('open');
> + }
> + },
> +
> + destroy : function() {
> + // call the base destroy function
> + $.Widget.prototype.destroy.call(this);
> + }
in destroy, remove anything added to dom nodes in _create.
> + });
> +}(jQuery));
> \ No newline at end of file
> diff --git a/ui/js/widgets/select-menu.js b/ui/js/widgets/select-menu.js
> new file mode 100644
> index 0000000..4124096
> --- /dev/null
> +++ b/ui/js/widgets/select-menu.js
> @@ -0,0 +1,86 @@
> +/*
> + * Project Kimchi
> + *
> + * Copyright IBM, Corp. 2013
> + *
> + * Authors:
> + * zhoumeina <zhoumein at linux.vnet.ibm.com>
> + *
> + * 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.
> + */
> +(function($) {
> + $.widget('kimchi.selectMenu', {
> + options: {
> + key: 0,
> + value: 0
> + },
remove 'key', 'value' as they are not used.
> +
> + _create : function() {
> + this.listControl = this.element;
> + this.element.html('');
> + var targetId = this.listControl.data('target');
> + var labelId = this.listControl.data('label');
> + this.target = $('#' + targetId);
> + this.label = $('#' + labelId);
> + this.selectDiv = this.listControl.parent().parent();
> + this.selectDiv.addClass('btn-select dropdown popable');
> + this.listControl.parent().addClass('popover');
> + var that = this;
> + },
> +
> + _setOption : function(key,value) {},
remove "_setOption" as the method body is empty.
> +
> + setData : function (options) {
> + var that = this;
> + var value = this.target.val();
> + var selectedClass = 'active';
> + var itemTag = 'li';
> + $.each(options, function(index, option) {
> + item = $('<' + itemTag + '></' + itemTag + '>');
> + item.text(option.label);
> + item.data('value', option.value);
> + if(option.value === value) {
> + item.addClass(selectedClass);
> + that.label.text(option.label);
> + }
> + that.listControl.append(item);
> + });
> + that.listControl.on('click', itemTag, function() {
> + that.listControl.children().removeClass(selectedClass);
> + that.listControl.addClass(selectedClass);
> + that.label.text($(this).text());
> + var oldValue = that.target.val();
> + var newValue = $(this).data('value');
> + that.target.val(newValue);
> + if(oldValue !== newValue) {
> + that.target.change();
> + }
> + });
> +
> + that.selectDiv.click(function(e) {
> + var isOpen = that.selectDiv.hasClass('open');
> + that.selectDiv.removeClass('open');
> + if (!isOpen) {
> + that.selectDiv.addClass('open');
> + }
> + e.preventDefault();
> + e.stopPropagation();
> + });
> + },
> +
> + destroy : function() {
> + // call the base destroy function
> + $.Widget.prototype.destroy.call(this);
> + }
In "destroy", remove what has added to dom nodes in "_create".
> + });
> +}(jQuery));
> \ No newline at end of file
> diff --git a/ui/pages/i18n.html.tmpl b/ui/pages/i18n.html.tmpl
> index c1fc3d1..c95da9b 100644
> --- a/ui/pages/i18n.html.tmpl
> +++ b/ui/pages/i18n.html.tmpl
> @@ -121,7 +121,10 @@ var i18n = {
> 'action_create': "$_("Create")",
> 'msg_warning': "$_("Warning")",
> 'msg.logicalpool.confirm.delete': "$_("It will format your disk and you will loose any data in"
> - " there, are you sure to continue? ")"
> + " there, are you sure to continue? ")",
> + 'select_default': "$_("Please choose")",
> + 'msg.storagepool.unvalid.path': "$_("This is not a valid path")",
> + 'msg.no.result' : "$_("No valid result")"
> };
> </script>
> </body>
> diff --git a/ui/pages/storagepool-add.html.tmpl b/ui/pages/storagepool-add.html.tmpl
> index 5a2dd45..3a73390 100644
> --- a/ui/pages/storagepool-add.html.tmpl
> +++ b/ui/pages/storagepool-add.html.tmpl
> @@ -27,7 +27,7 @@
> <!DOCTYPE html>
> <html>
> <body>
> - <div class="window" style="width: 600px; height: 600px;">
> + <div class="window" style="width: 800px; height: 650px;">
> <header>
> <h1 class="title">$_("Define a New Storage Pool")</h1>
> <div class="close">X</div>
> @@ -47,10 +47,10 @@
> <section class="form-section">
> <h2>2. $_("Storage Pool Type")</h2>
> <div class="storage-type-wrapper-controls">
> - <div class="btn dropdown popable">
> + <div>
> <input id="poolType" name="type" type="hidden" value="dir"/>
> <span class="text" id="pool-type-label"></span><span class="arrow"></span>
> - <div class="popover" style="width: 100%">
> + <div style="width: 100%">
> <ul class="select-list" id="storagePool-list" data-target="poolType" data-label="pool-type-label">
> </ul>
> </div>
> @@ -74,22 +74,43 @@
> <section class="form-section">
> <h2>3. $_("NFS server IP")</h2>
> <div class="field">
> + <input type="radio" id="nfsServerInput" value="input" name="nfsServerType" checked="true">
> + <label>$_("I want to input the server myself.")</label>
> + <input type="radio" id="nfsServerChoose" value="choose" name="nfsServerType">
> + <label>$_("I want to choose a server I used before.")</label>
> + </div>
> + <div id="nfsServerInputDiv" class="field">
> <p class="text-help">
> $_("NFS server IP or hostname. It should not be empty.")</p>
> <input id="nfsserverId" type="text" class="text"
> style="width: 300px">
> </div>
> + <div id="nfsServerChooseDiv" class="field tmpl-html" style="overflow:visible">
> + <p class="text-help">
> + $_("Please choose the nfs server you want to create storage pool.")</p>
> + <div style="width: 285px">
> + <input id="nfsServerSelect" type="hidden"/>
> + <span class="text" id="nfs-server-label"></span><span class="arrow"></span>
> + <div style="width: 100%">
> + <ul class="select-list" id="nfs-server-used" data-target="nfsServerSelect" data-label="nfs-server-label">
> + </ul>
> + </div>
> + </div>
> + </div>
> </section>
> <section class="form-section">
> <h2>4. $_("NFS Path")</h2>
> - <div class="field">
> + <div class="field" style="overflow:visible">
> <p class="text-help">$_("The nfs exported path on nfs server")</p>
> - <input id="nfspathId" type="text" class="text"
> - style="width: 300px">
> - <input type="hidden" id="localpathId" class="text"
> - value="none">
> - </div>
> - <div class="clear"></div>
> + <div style="width: 285px">
> + <input id="nfspathId" class="text" disabled="disabled" style="width:293px;"/>
> + <span class="text" id="nfs-target-label"></span><span class="arrow"></span>
> + <div style="width: 100%">
> + <ul class="select-list" id="nfs-server-target" data-target="nfspathId" data-label="nfs-target-label">
> + </ul>
> + </div>
> + </div>
> + </div>
> </section>
> </div>
> <div class="logical-section tmpl-html">
More information about the Kimchi-devel
mailing list