[pve-devel] [PATCH manager 5/6] ui: add ACMEPluginEdit window

Dominik Csapak d.csapak at proxmox.com
Tue May 5 14:38:17 CEST 2020


this is a rather complex edit window, because we dynamically create form
fields according to the schema we get from the api

to do this properly we have to handle a few things:
* we have to properly set the values on edit
* we have to properly track the original values
* we have to merge and split with/from the generic 'data' field
  (so that if a plugin has some extra fields that we did not include in
  the schema the user can still enter them)

Signed-off-by: Dominik Csapak <d.csapak at proxmox.com>
---
 www/manager6/Makefile             |   1 +
 www/manager6/Parser.js            |  17 ++-
 www/manager6/dc/ACMEPluginEdit.js | 190 ++++++++++++++++++++++++++++++
 3 files changed, 207 insertions(+), 1 deletion(-)
 create mode 100644 www/manager6/dc/ACMEPluginEdit.js

diff --git a/www/manager6/Makefile b/www/manager6/Makefile
index 62253ede..fb4c51bb 100644
--- a/www/manager6/Makefile
+++ b/www/manager6/Makefile
@@ -217,6 +217,7 @@ JSSRC= 				                 	\
 	ha/GroupEdit.js					\
 	ha/Groups.js					\
 	ha/Fencing.js					\
+	dc/ACMEPluginEdit.js				\
 	dc/Summary.js					\
 	grid/Replication.js				\
 	dc/Health.js					\
diff --git a/www/manager6/Parser.js b/www/manager6/Parser.js
index 43cc4f5f..4cecb3e1 100644
--- a/www/manager6/Parser.js
+++ b/www/manager6/Parser.js
@@ -696,5 +696,20 @@ Ext.define('PVE.Parser', { statics: {
 	    };
 	}
 	return null;
-    }
+    },
+
+    parseACMEPluginData: function(data) {
+	let res = {};
+	let extradata = [];
+	data.split('\n').forEach((line) => {
+	    // capture everything after the first = as value
+	    let [key, value] = line.split(/=(.+)/);
+	    if (value !== undefined) {
+		res[key] = value;
+	    } else {
+		extradata.push(line);
+	    }
+	});
+	return [res, extradata];
+    },
 }});
diff --git a/www/manager6/dc/ACMEPluginEdit.js b/www/manager6/dc/ACMEPluginEdit.js
new file mode 100644
index 00000000..fd07017c
--- /dev/null
+++ b/www/manager6/dc/ACMEPluginEdit.js
@@ -0,0 +1,190 @@
+Ext.define('PVE.dc.ACMEPluginEditor', {
+    extend: 'Proxmox.window.Edit',
+    xtype: 'pveACMEPluginEditor',
+    mixins: ['Proxmox.Mixin.CBind'],
+
+    isAdd: true,
+    isCreate: false,
+
+    width: 400,
+    url: '/cluster/acme/plugins/',
+
+    subject: gettext('Plugin'),
+    items: [
+	{
+	    xtype: 'inputpanel',
+	    // we dynamically create fields from the given schema
+	    // things we have to do here:
+	    // * save which fields we created to remove them again
+	    // * split the data from the generic 'data' field into the boxes
+	    // * on deletion collect those values again
+	    // * save the original values of the data field
+	    createdFields: {},
+	    createdInitially: false,
+	    originalValues: {},
+	    createSchemaFields: function(schema) {
+		let me = this;
+		// we know where to add because we define it right below
+		let container = me.down('container');
+		let datafield = me.down('field[name=data]');
+		if (!me.createdInitially) {
+		    [me.originalValues] = PVE.Parser.parseACMEPluginData(datafield.getValue());
+		}
+
+		// collect values from custom fields and add it to 'data'',
+		// then remove the custom fields
+		let data = [];
+		for (const [name, field] of Object.entries(me.createdFields)) {
+		    let value = field.getValue();
+		    if (value !== undefined && value !== null && value !== '') {
+			data.push(`${name}=${value}`);
+		    }
+		    container.remove(field);
+		}
+		let datavalue = datafield.getValue();
+		if (datavalue !== undefined && datavalue !== null && datavalue !== '') {
+		    data.push(datavalue);
+		}
+		datafield.setValue(data.join('\n'));
+
+		me.createdFields = {};
+
+		// create custom fields according to schema
+		for (const [name, definition] of Object.entries(schema)) {
+		    let xtype;
+		    switch (definition.type) {
+			case 'string':
+			    xtype = 'proxmoxtextfield';
+			    break;
+			case 'integer':
+			    xtype = 'proxmoxintegerfield';
+			    break;
+			case 'number':
+			    xtype = 'numberfield';
+			    break;
+			default:
+			    console.warn(`unknown type '${definition.type}'`);
+			    xtype = 'proxmoxtextfield';
+			    break;
+		    }
+
+		    let field = Ext.create({
+			xtype,
+			name: `custom_${name}`,
+			fieldLabel: name,
+			width: '100%',
+			labelWidth: 120,
+			autoEl: definition.description ? {
+			    tag: 'div',
+			    'data-qtip': definition.description,
+			} : undefined,
+		    });
+
+		    me.createdFields[name] = field;
+		    container.add(field);
+		}
+
+		// parse data from field and set it to the custom ones
+		let extradata = [];
+		[data, extradata] = PVE.Parser.parseACMEPluginData(datafield.getValue());
+		for (const [key, value] of Object.entries(data)) {
+		    if (me.createdFields[key]) {
+			me.createdFields[key].setValue(value);
+			me.createdFields[key].originalValue = me.originalValues[key];
+		    } else {
+			extradata.push(`${key}=${value}`);
+		    }
+		}
+		datafield.setValue(extradata.join('\n'));
+		if (!me.createdInitially) {
+		    datafield.resetOriginalValue();
+		    me.createdInitially = true; // save that we initally set that
+		}
+	    },
+	    onGetValues: function(values) {
+		let me = this;
+		let win = me.up('pveACMEPluginEditor');
+		if (win.isCreate) {
+		    values.id = values.plugin;
+		    values.type = 'dns'; // the only one for now
+		}
+		delete values.plugin;
+
+		PVE.Utils.delete_if_default(values, 'validation-delay', '30', win.isCreate);
+
+		let data = '';
+		for (const [name, field] of Object.entries(me.createdFields)) {
+		    let value = field.getValue();
+		    if (value !== null && value !== undefined && value !== '') {
+			data += `${name}=${value}\n`;
+		    }
+		    delete values[`custom_${name}`];
+		}
+		values.data = Ext.util.Base64.encode(data + values.data);
+		return values;
+	    },
+	    items: [
+		{
+		    xtype: 'pmxDisplayEditField',
+		    cbind: {
+			editable: (get) => get('isCreate'),
+			submitValue: (get) => get('isCreate'),
+		    },
+		    editConfig: {
+			flex: 1,
+			xtype: 'proxmoxtextfield',
+			allowBlank: false,
+		    },
+		    name: 'plugin',
+		    labelWidth: 120,
+		    fieldLabel: gettext('Plugin'),
+		},
+		{
+		    xtype: 'proxmoxintegerfield',
+		    name: 'validation-delay',
+		    labelWidth: 120,
+		    fieldLabel: gettext('Validation Delay'),
+		    emptyText: 30,
+		    cbind: {
+			deleteEmpty: '{!isCreate}',
+		    },
+		    minValue: 0,
+		    maxValue: 172800,
+		},
+		{
+		    xtype: 'pveACMEApiSelector',
+		    name: 'api',
+		    labelWidth: 120,
+		    listeners: {
+			change: function(selector) {
+			    let schema = selector.getSchema();
+			    selector.up('inputpanel').createSchemaFields(schema);
+			},
+		    },
+		},
+		{
+		    fieldLabel: gettext('API Data'),
+		    labelWidth: 120,
+		    xtype: 'textarea',
+		    name: 'data',
+		},
+	    ],
+	},
+    ],
+
+    initComponent: function() {
+	var me = this;
+
+	me.callParent();
+
+	if (!me.isCreate) {
+	    me.load({
+		success: function(response, opts) {
+		    me.setValues(response.result.data);
+		},
+	    });
+	} else {
+	    me.method = 'POST';
+	}
+    },
+});
-- 
2.20.1





More information about the pve-devel mailing list