hunk.code_window:     | 'RepositoryGitCredentialID'
     >
   ) {
     if (model.RepositoryGitCredentialID) {
       return { gitCredentialId: model.RepositoryGitCredentialID };
     }
  
hunk.labels: keep keep add keep keep keep
hunk.after_edit:   if (!model.RepositoryAuthentication) {
       return undefined;
     }
  
hunk.file_path: app/react/portainer/gitops/utils.ts
hunk.type: add
file: import _ from 'lodash-es';
  import { AccessControlFormData } from 'Portainer/components/accessControlForm/porAccessControlFormModel';
  import { TEMPLATE_NAME_VALIDATION_REGEX } from '@/constants';
  import { renderTemplate } from '@/react/portainer/custom-templates/components/utils';
  import { isBE } from '@/react/portainer/feature-flags/feature-flags.service';
  import { confirmDelete } from '@@/modals/confirm';
  
  class CustomTemplatesViewController {
    /* @ngInject */
    constructor(
      $anchorScroll,
      $async,
      $rootScope,
      $state,
      Authentication,
      CustomTemplateService,
      FormValidator,
      NetworkService,
      Notifications,
      ResourceControlService,
      StackService,
      StateManager
    ) {
      this.$anchorScroll = $anchorScroll;
      this.$async = $async;
      this.$rootScope = $rootScope;
      this.$state = $state;
      this.Authentication = Authentication;
      this.CustomTemplateService = CustomTemplateService;
      this.FormValidator = FormValidator;
      this.NetworkService = NetworkService;
      this.Notifications = Notifications;
      this.ResourceControlService = ResourceControlService;
      this.StateManager = StateManager;
      this.StackService = StackService;
  
      this.isTemplateVariablesEnabled = isBE;
  
      this.DOCKER_STANDALONE = 'DOCKER_STANDALONE';
      this.DOCKER_SWARM_MODE = 'DOCKER_SWARM_MODE';
  
      this.state = {
        selectedTemplate: null,
        showAdvancedOptions: false,
        formValidationError: '',
        actionInProgress: false,
        deployable: false,
        templateNameRegex: TEMPLATE_NAME_VALIDATION_REGEX,
        templateContent: '',
        templateLoadFailed: false,
      };
  
      this.currentUser = {
        isAdmin: false,
        id: null,
      };
  
      this.formValues = {
        network: '',
        name: '',
        fileContent: '',
        AccessControlData: new AccessControlFormData(),
        variables: [],
      };
  
      this.getTemplates = this.getTemplates.bind(this);
      this.getTemplatesAsync = this.getTemplatesAsync.bind(this);
      this.removeTemplates = this.removeTemplates.bind(this);
      this.removeTemplatesAsync = this.removeTemplatesAsync.bind(this);
      this.validateForm = this.validateForm.bind(this);
      this.createStack = this.createStack.bind(this);
      this.createStackAsync = this.createStackAsync.bind(this);
      this.selectTemplate = this.selectTemplate.bind(this);
      this.selectTemplateAsync = this.selectTemplateAsync.bind(this);
      this.unselectTemplate = this.unselectTemplate.bind(this);
      this.unselectTemplateAsync = this.unselectTemplateAsync.bind(this);
      this.getNetworks = this.getNetworks.bind(this);
      this.getNetworksAsync = this.getNetworksAsync.bind(this);
      this.confirmDelete = this.confirmDelete.bind(this);
      this.confirmDeleteAsync = this.confirmDeleteAsync.bind(this);
      this.editorUpdate = this.editorUpdate.bind(this);
      this.isEditAllowed = this.isEditAllowed.bind(this);
      this.onChangeFormValues = this.onChangeFormValues.bind(this);
      this.onChangeTemplateVariables = this.onChangeTemplateVariables.bind(this);
    }
  
    isEditAllowed(template) {
      return this.currentUser.isAdmin || this.currentUser.id === template.CreatedByUserId;
    }
  
    getTemplates() {
      return this.$async(this.getTemplatesAsync);
    }
    async getTemplatesAsync() {
      try {
        this.templates = await this.CustomTemplateService.customTemplates([1, 2]);
      } catch (err) {
        this.Notifications.error('Failed loading templates', err, 'Unable to load custom templates');
      }
    }
  
    removeTemplates(templates) {
      return this.$async(this.removeTemplatesAsync, templates);
    }
    async removeTemplatesAsync(templates) {
      for (let template of templates) {
        try {
          await this.CustomTemplateService.remove(template.id);
          this.Notifications.success('Success', 'Removed template successfully');
          _.remove(this.templates, template);
        } catch (err) {
          this.Notifications.error('Failed removing template', err, 'Unable to remove custom template');
        }
      }
    }
  
    onChangeTemplateVariables(variables) {
      this.onChangeFormValues({ variables });
  
      this.renderTemplate();
    }
  
    renderTemplate() {
      if (!this.isTemplateVariablesEnabled) {
        return;
      }
  
      const fileContent = renderTemplate(this.state.templateContent, this.formValues.variables, this.state.selectedTemplate.Variables);
      this.onChangeFormValues({ fileContent });
    }
  
    onChangeFormValues(values) {
      this.formValues = {
        ...this.formValues,
        ...values,
      };
    }
  
    validateForm(accessControlData, isAdmin) {
      this.state.formValidationError = '';
      const error = this.FormValidator.validateAccessControl(accessControlData, isAdmin);
  
      if (error) {
        this.state.formValidationError = error;
        return false;
      }
      return true;
    }
  
    createStack() {
      return this.$async(this.createStackAsync);
    }
    async createStackAsync() {
      const userId = this.currentUser.id;
      const accessControlData = this.formValues.AccessControlData;
  
      if (!this.validateForm(accessControlData, this.currentUser.isAdmin)) {
        return;
      }
      const stackName = this.formValues.name;
  
      const endpointId = this.endpoint.Id;
  
      this.state.actionInProgress = true;
  
      try {
        const file = this.formValues.fileContent;
        const createAction = this.state.selectedTemplate.Type === 1 ? this.StackService.createSwarmStackFromFileContent : this.StackService.createComposeStackFromFileContent;
        const { ResourceControl: resourceControl } = await createAction(stackName, file, [], endpointId);
        await this.ResourceControlService.applyResourceControl(userId, accessControlData, resourceControl);
        this.Notifications.success('Success', 'Stack successfully deployed');
        this.$state.go('docker.stacks');
      } catch (err) {
        this.Notifications.error('Deployment error', err, 'Failed to deploy stack');
      } finally {
        this.state.actionInProgress = false;
      }
    }
  
    unselectTemplate(template) {
      // wrapping unselect with async to make a digest cycle run between unselect to select
      return this.$async(this.unselectTemplateAsync, template);
    }
    async unselectTemplateAsync(template) {
      template.Selected = false;
      this.state.selectedTemplate = null;
  
      this.formValues = {
        network: '',
        name: '',
        fileContent: '',
        AccessControlData: new AccessControlFormData(),
        variables: [],
      };
    }
  
    selectTemplate(template) {
      return this.$async(this.selectTemplateAsync, template);
    }
    async selectTemplateAsync(template) {
      if (this.state.selectedTemplate) {
        await this.unselectTemplate(this.state.selectedTemplate);
      }
  
      template.Selected = true;
  
      try {
        this.state.templateContent = this.formValues.fileContent = await this.CustomTemplateService.customTemplateFile(template.Id, template.GitConfig !== null);
      } catch (err) {
        this.state.templateLoadFailed = true;
        this.Notifications.error('Failure', err, 'Unable to retrieve custom template data');
      }
  
      this.formValues.network = _.find(this.availableNetworks, function (o) {
        return o.Name === 'bridge';
      });
  
      this.formValues.name = template.Title ? template.Title : '';
      this.state.selectedTemplate = template;
      this.$anchorScroll('view-top');
      const applicationState = this.StateManager.getState();
      this.state.deployable = this.isDeployable(applicationState.endpoint, template.Type);
  
      if (template.Variables && template.Variables.length > 0) {
        const variables = Object.fromEntries(template.Variables.map((variable) => [variable.name, '']));
        this.onChangeTemplateVariables(variables);
      }
    }
  
    getNetworks(provider, apiVersion) {
      return this.$async(this.getNetworksAsync, provider, apiVersion);
    }
    async getNetworksAsync(provider, apiVersion) {
      try {
        const networks = await this.NetworkService.networks(
          provider === 'DOCKER_STANDALONE' || provider === 'DOCKER_SWARM_MODE',
          false,
          provider === 'DOCKER_SWARM_MODE' && apiVersion >= 1.25
        );
        this.availableNetworks = networks;
      } catch (err) {
        this.Notifications.error('Failure', err, 'Failed to load networks.');
      }
    }
  
    confirmDelete(templateId) {
      return this.$async(this.confirmDeleteAsync, templateId);
    }
    async confirmDeleteAsync(templateId) {
      const confirmed = await confirmDelete('Are you sure that you want to delete this template?');
      if (!confirmed) {
        return;
      }
  
      try {
        var template = _.find(this.templates, { Id: templateId });
        await this.CustomTemplateService.remove(templateId);
        this.Notifications.success('Template successfully deleted', template && template.Title);
        _.remove(this.templates, { Id: templateId });
      } catch (err) {
        this.Notifications.error('Failure', err, 'Failed to delete template');
      }
    }
  
    editorUpdate(value) {
      this.formValues.fileContent = value;
    }
  
    isDeployable(endpoint, templateType) {
      let deployable = false;
      switch (templateType) {
        case 1:
          deployable = endpoint.mode.provider === this.DOCKER_SWARM_MODE;
          break;
        case 2:
          deployable = endpoint.mode.provider === this.DOCKER_STANDALONE;
          break;
      }
  
      return deployable;
    }
  
    $onInit() {
      const applicationState = this.StateManager.getState();
  
      const {
        endpoint: { mode: endpointMode },
        apiVersion,
      } = applicationState;
  
      this.getTemplates();
      this.getNetworks(endpointMode.provider, apiVersion);
  
      this.currentUser.isAdmin = this.Authentication.isAdmin();
      const user = this.Authentication.getUserDetails();
      this.currentUser.id = user.ID;
    }
  }
  
  export default CustomTemplatesViewController;
  