Ext.define('PVE.dc.RealmSyncJobView', { extend: 'Ext.grid.Panel', alias: 'widget.pveRealmSyncJobView', stateful: true, stateId: 'grid-realmsyncjobs', emptyText: Ext.String.format(gettext('No {0} configured'), gettext('Realm Sync Job')), controller: { xclass: 'Ext.app.ViewController', addRealmSyncJob: function(button) { let me = this; Ext.create(`PVE.dc.RealmSyncJobEdit`, { autoShow: true, listeners: { destroy: () => me.reload(), }, }); }, editRealmSyncJob: function() { let me = this; let view = me.getView(); let selection = view.getSelection(); if (!selection || selection.length < 1) { return; } Ext.create(`PVE.dc.RealmSyncJobEdit`, { jobid: selection[0].data.id, autoShow: true, listeners: { destroy: () => me.reload(), }, }); }, runNow: function() { let me = this; let view = me.getView(); let selection = view.getSelection(); if (!selection || selection.length < 1) { return; } let params = selection[0].data; let realm = params.realm; let propertiesToDelete = ['comment', 'realm', 'id', 'type', 'schedule', 'last-run', 'next-run', 'enabled']; for (const prop of propertiesToDelete) { delete params[prop]; } Proxmox.Utils.API2Request({ url: `/access/domains/${realm}/sync`, params, waitMsgTarget: view, method: 'POST', failure: response => Ext.Msg.alert(gettext('Error'), response.htmlStatus), success: function(response, options) { Ext.create('Proxmox.window.TaskProgress', { autoShow: true, upid: response.result.data, taskDone: () => { me.reload(); }, }); }, }); }, reload: function() { this.getView().getStore().load(); }, }, store: { autoLoad: true, id: 'realm-syncs', proxy: { type: 'proxmox', url: '/api2/json/cluster/jobs/realm-sync', }, }, viewConfig: { getRowClass: (record, _index) => record.get('enabled') ? '' : 'proxmox-disabled-row', }, columns: [ { header: gettext('Enabled'), width: 80, dataIndex: 'enabled', sortable: true, align: 'center', stopSelection: false, renderer: Proxmox.Utils.renderEnabledIcon, }, { text: gettext('Name'), flex: 1, dataIndex: 'id', hidden: true, }, { text: gettext('Realm'), width: 200, dataIndex: 'realm', }, { header: gettext('Schedule'), width: 150, dataIndex: 'schedule', }, { text: gettext('Next Run'), dataIndex: 'next-run', width: 150, renderer: PVE.Utils.render_next_event, }, { header: gettext('Comment'), dataIndex: 'comment', renderer: Ext.htmlEncode, sorter: (a, b) => (a.data.comment || '').localeCompare(b.data.comment || ''), flex: 1, }, ], tbar: [ { text: gettext('Add'), handler: 'addRealmSyncJob', }, { text: gettext('Edit'), xtype: 'proxmoxButton', handler: 'editRealmSyncJob', disabled: true, }, { xtype: 'proxmoxStdRemoveButton', baseurl: `/api2/extjs/cluster/jobs/realm-sync`, callback: 'reload', }, { xtype: 'proxmoxButton', handler: 'runNow', disabled: true, text: gettext('Run Now'), }, ], listeners: { itemdblclick: 'editRealmSyncJob', }, initComponent: function() { var me = this; me.callParent(); Proxmox.Utils.monStoreErrors(me, me.getStore()); }, }); Ext.define('PVE.dc.RealmSyncJobEdit', { extend: 'Proxmox.window.Edit', mixins: ['Proxmox.Mixin.CBind'], subject: gettext('Realm Sync Job'), onlineHelp: 'pveum_ldap_sync', // don't focus the schedule field on edit defaultFocus: 'field[name=id]', cbindData: function() { let me = this; me.isCreate = !me.jobid; me.jobid = me.jobid || ""; let url = '/api2/extjs/cluster/jobs/realm-sync'; me.url = me.jobid ? `${url}/${me.jobid}` : url; me.method = me.isCreate ? 'POST' : 'PUT'; if (!me.isCreate) { me.subject = `${me.subject}: ${me.jobid}`; } return {}; }, submitUrl: function(url, values) { return this.isCreate ? `${url}/${values.id}` : url; }, controller: { xclass: 'Ext.app.ViewController', updateDefaults: function(_field, newValue) { let me = this; ['scope', 'enable-new', 'schedule'].forEach((reference) => { me.lookup(reference)?.setDisabled(false); }); // only update on create if (!me.getView().isCreate) { return; } Proxmox.Utils.API2Request({ url: `/access/domains/${newValue}`, success: function(response) { // first reset the fields to their default ['acl', 'entry', 'properties'].forEach(opt => { me.lookup(`remove-vanished-${opt}`)?.setValue(false); }); me.lookup('enable-new')?.setValue('1'); me.lookup('scope')?.setValue(undefined); let options = response?.result?.data?.['sync-defaults-options']; if (options) { let parsed = PVE.Parser.parsePropertyString(options); if (parsed['remove-vanished']) { let opts = parsed['remove-vanished'].split(';'); for (const opt of opts) { me.lookup(`remove-vanished-${opt}`)?.setValue(true); } delete parsed['remove-vanished']; } for (const [name, value] of Object.entries(parsed)) { me.lookup(name)?.setValue(value); } } }, }); }, }, items: [ { xtype: 'inputpanel', cbind: { isCreate: '{isCreate}', }, onGetValues: function(values) { let me = this; let vanished_opts = []; ['acl', 'entry', 'properties'].forEach((prop) => { if (values[`remove-vanished-${prop}`]) { vanished_opts.push(prop); } delete values[`remove-vanished-${prop}`]; }); if (!values.id && me.isCreate) { values.id = 'realmsync-' + Ext.data.identifier.Uuid.Global.generate().slice(0, 13); } if (vanished_opts.length > 0) { values['remove-vanished'] = vanished_opts.join(';'); } else { values['remove-vanished'] = 'none'; } PVE.Utils.delete_if_default(values, 'node', ''); if (me.isCreate) { delete values.delete; // on create we cannot delete values } return values; }, column1: [ { xtype: 'pmxDisplayEditField', editConfig: { xtype: 'pmxRealmComboBox', storeFilter: rec => rec.data.type === 'ldap' || rec.data.type === 'ad', }, listConfig: { emptyText: `
${gettext('No LDAP/AD Realm found')}
`, }, cbind: { editable: '{isCreate}', }, listeners: { change: 'updateDefaults', }, fieldLabel: gettext('Realm'), name: 'realm', reference: 'realm', }, { xtype: 'pveCalendarEvent', fieldLabel: gettext('Schedule'), disabled: true, allowBlank: false, name: 'schedule', reference: 'schedule', }, { xtype: 'proxmoxcheckbox', fieldLabel: gettext('Enable Job'), name: 'enabled', reference: 'enabled', uncheckedValue: 0, defaultValue: 1, checked: true, }, ], column2: [ { xtype: 'proxmoxKVComboBox', name: 'scope', reference: 'scope', disabled: true, fieldLabel: gettext('Scope'), value: '', emptyText: gettext('No default available'), deleteEmpty: false, allowBlank: false, comboItems: [ ['users', gettext('Users')], ['groups', gettext('Groups')], ['both', gettext('Users and Groups')], ], }, { xtype: 'proxmoxKVComboBox', value: '1', deleteEmpty: false, disabled: true, allowBlank: false, comboItems: [ ['1', Proxmox.Utils.yesText], ['0', Proxmox.Utils.noText], ], name: 'enable-new', reference: 'enable-new', fieldLabel: gettext('Enable New'), }, ], columnB: [ { xtype: 'fieldset', title: gettext('Remove Vanished Options'), items: [ { xtype: 'proxmoxcheckbox', fieldLabel: gettext('ACL'), name: 'remove-vanished-acl', reference: 'remove-vanished-acl', boxLabel: gettext('Remove ACLs of vanished users and groups.'), }, { xtype: 'proxmoxcheckbox', fieldLabel: gettext('Entry'), name: 'remove-vanished-entry', reference: 'remove-vanished-entry', boxLabel: gettext('Remove vanished user and group entries.'), }, { xtype: 'proxmoxcheckbox', fieldLabel: gettext('Properties'), name: 'remove-vanished-properties', reference: 'remove-vanished-properties', boxLabel: gettext('Remove vanished properties from synced users.'), }, ], }, { xtype: 'proxmoxtextfield', name: 'comment', fieldLabel: gettext('Job Comment'), cbind: { deleteEmpty: '{!isCreate}', }, autoEl: { tag: 'div', 'data-qtip': gettext('Description of the job'), }, }, { xtype: 'displayfield', reference: 'defaulthint', value: gettext('Default sync options can be set by editing the realm.'), userCls: 'pmx-hint', hidden: true, }, ], }, ], initComponent: function() { let me = this; me.callParent(); if (me.jobid) { me.load({ success: function(response, options) { let values = response.result.data; if (values['remove-vanished']) { let opts = values['remove-vanished'].split(';'); for (const opt of opts) { values[`remove-vanished-${opt}`] = 1; } } me.down('inputpanel').setValues(values); }, }); } }, });