import { ResourceControlViewModel } from '@/portainer/access-control/models/ResourceControlViewModel'; import { AccessControlFormData } from '@/portainer/components/accessControlForm/porAccessControlFormModel'; import { isBE } from '@/portainer/feature-flags/feature-flags.service'; import { getTemplateVariables, intersectVariables } from '@/react/portainer/custom-templates/components/utils'; class KubeEditCustomTemplateViewController { /* @ngInject */ constructor($async, $state, ModalService, Authentication, CustomTemplateService, FormValidator, Notifications, ResourceControlService) { Object.assign(this, { $async, $state, ModalService, Authentication, CustomTemplateService, FormValidator, Notifications, ResourceControlService }); this.isTemplateVariablesEnabled = isBE; this.formValues = null; this.state = { formValidationError: '', isEditorDirty: false, isTemplateValid: true, }; this.templates = []; this.getTemplate = this.getTemplate.bind(this); this.submitAction = this.submitAction.bind(this); this.onChangeFileContent = this.onChangeFileContent.bind(this); this.onBeforeUnload = this.onBeforeUnload.bind(this); this.handleChange = this.handleChange.bind(this); this.onVariablesChange = this.onVariablesChange.bind(this); } getTemplate() { return this.$async(async () => { try { const { id } = this.$state.params; const [template, file] = await Promise.all([this.CustomTemplateService.customTemplate(id), this.CustomTemplateService.customTemplateFile(id)]); template.FileContent = file; template.Variables = template.Variables || []; this.formValues = template; this.parseTemplate(file); this.oldFileContent = this.formValues.FileContent; this.formValues.ResourceControl = new ResourceControlViewModel(template.ResourceControl); this.formValues.AccessControlData = new AccessControlFormData(); } catch (err) { this.Notifications.error('Failure', err, 'Unable to retrieve custom template data'); } }); } onVariablesChange(values) { this.handleChange({ Variables: values }); } handleChange(values) { return this.$async(async () => { this.formValues = { ...this.formValues, ...values, }; }); } parseTemplate(templateStr) { if (!this.isTemplateVariablesEnabled) { return; } const variables = getTemplateVariables(templateStr); const isValid = !!variables; this.state.isTemplateValid = isValid; if (isValid) { this.onVariablesChange(intersectVariables(this.formValues.Variables, variables)); } } validateForm() { this.state.formValidationError = ''; if (!this.formValues.FileContent) { this.state.formValidationError = 'Template file content must not be empty'; return false; } const title = this.formValues.Title; const id = this.$state.params.id; const isNotUnique = this.templates.some((template) => template.Title === title && template.Id != id); if (isNotUnique) { this.state.formValidationError = `A template with the name ${title} already exists`; return false; } const isAdmin = this.Authentication.isAdmin(); const accessControlData = this.formValues.AccessControlData; const error = this.FormValidator.validateAccessControl(accessControlData, isAdmin); if (error) { this.state.formValidationError = error; return false; } return true; } submitAction() { return this.$async(async () => { if (!this.validateForm()) { return; } this.actionInProgress = true; try { await this.CustomTemplateService.updateCustomTemplate(this.formValues.Id, this.formValues); const userDetails = this.Authentication.getUserDetails(); const userId = userDetails.ID; await this.ResourceControlService.applyResourceControl(userId, this.formValues.AccessControlData, this.formValues.ResourceControl); this.Notifications.success('Success', 'Custom template successfully updated'); this.state.isEditorDirty = false; this.$state.go('kubernetes.templates.custom'); } catch (err) { this.Notifications.error('Failure', err, 'Unable to update custom template'); } finally { this.actionInProgress = false; } }); } onChangeFileContent(value) { if (stripSpaces(this.formValues.FileContent) !== stripSpaces(value)) { this.formValues.FileContent = value; this.parseTemplate(value); this.state.isEditorDirty = true; } } async $onInit() { this.$async(async () => { this.getTemplate(); try { this.templates = await this.CustomTemplateService.customTemplates(); } catch (err) { this.Notifications.error('Failure loading', err, 'Failed loading custom templates'); } window.addEventListener('beforeunload', this.onBeforeUnload); }); } isEditorDirty() { return this.formValues.FileContent !== this.oldFileContent && this.state.isEditorDirty; } uiCanExit() { if (this.isEditorDirty()) { return this.ModalService.confirmWebEditorDiscard(); } } onBeforeUnload(event) { if (this.formValues.FileContent !== this.oldFileContent && this.state.isEditorDirty) { event.preventDefault(); event.returnValue = ''; return ''; } } $onDestroy() { window.removeEventListener('beforeunload', this.onBeforeUnload); } } export default KubeEditCustomTemplateViewController; function stripSpaces(str = '') { return str.replace(/(\r\n|\n|\r)/gm, ''); }