/* * Copyright (c) 2015-2016 Codenvy, S.A. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Codenvy, S.A. - initial API and implementation */ 'use strict'; /*global $:false, window:false */ /** * This class is handling the controller for the projects * @author Florent Benoit */ export class CreateProjectCtrl { /** * Default constructor that is using resource * @ngInject for Dependency injection */ constructor(cheAPI, cheStack, $websocket, $routeParams, $filter, $timeout, $location, $mdDialog, $scope, $rootScope, createProjectSvc, lodash, $q, $log, $document, routeHistory) { this.$log = $log; this.cheAPI = cheAPI; this.cheStack = cheStack; this.$websocket = $websocket; this.$timeout = $timeout; this.$location = $location; this.$mdDialog = $mdDialog; this.$scope = $scope; this.$rootScope = $rootScope; this.createProjectSvc = createProjectSvc; this.lodash = lodash; this.$q = $q; this.$document = $document; if ($routeParams.resetProgress) { this.resetCreateProgress(); routeHistory.popCurrentPath(); // remove param $location.search({}); $location.replace(); } // JSON used for import data this.importProjectData = this.getDefaultProjectJson(); this.stackTab = 'ready-to-go'; this.enableWizardProject = true; this.currentStackTags = null; // stacks not yet completed this.stacksInitialized = false; // keep references on workspaces and projects this.workspaces = []; // default options this.selectSourceOption = 'select-source-new'; this.templatesChoice = 'templates-samples'; // default RAM value for workspaces this.workspaceRam = 1000; this.websocketReconnect = 50; this.generateWorkspaceName(); this.headerSteps = [ { id: '#create-project-source-id', name: 'source', link: 'create-project-source' }, { id: '#create-project-source-stack', name: 'stack', link: 'create-project-stack' }, { id: '#create-project-workspace', name: 'workspace', link: 'create-project-workspace' }, { id: '#create-project-source-template', name: 'template', link: 'create-project-template' }, { id: '#create-project-source-information', name: 'metadata', link: 'create-project-information' } ]; this.messageBus = null; this.recipeUrl = null; //search the selected tab let routeParams = $routeParams.tabName; if (!routeParams) { this.selectedTabIndex = 0; } else { switch (routeParams) { case 'blank': this.selectedTabIndex = 0; break; case 'samples': this.selectedTabIndex = 1; break; case 'git': this.selectedTabIndex = 2; break; case 'github': this.selectedTabIndex = 3; break; case 'zip': this.selectedTabIndex = 4; break; case 'config': this.selectedTabIndex = 2; break; default: $location.path('/create-project'); } } if (cheStack.getStacks().length) { this.updateWorkspaces(); } else { cheStack.fetchStacks().then(() => { this.updateWorkspaces(); }, (error) => { if (error.status === 304) { this.updateWorkspaces(); return; } this.state = 'error'; }); } // selected current tab this.currentTab = ''; // all forms that we have this.forms = new Map(); this.jsonConfig = {}; this.jsonConfig.content = '{}'; try { this.jsonConfig.content = $filter('json')(angular.fromJson(this.importProjectData), 2); } catch (e) { // ignore the error } $rootScope.$on('create-project-stacks:initialized', () => { this.stacksInitialized = true; }); // sets isReady status after selection $rootScope.$on('create-project-github:selected', () => { if (!this.isReady && this.currentTab === 'github') { this.isReady = true; } }); $rootScope.$on('create-project-samples:selected', () => { if (!this.isReady && this.currentTab === 'samples') { this.isReady = true; } }); // channels on which we will subscribe on the workspace bus websocket this.listeningChannels = []; this.projectName = null; this.projectDescription = null; this.defaultWorkspaceName = null; } /** * Fetch workspaces when initializing */ updateWorkspaces() { this.workspaces = this.cheAPI.getWorkspace().getWorkspaces(); // fetch workspaces when initializing let promise = this.cheAPI.getWorkspace().fetchWorkspaces(); promise.then(() => { this.updateData(); }, (error) => { // retrieve last data that were fetched before if (error.status === 304) { // ok this.updateData(); return; } this.state = 'error'; }); } /** * Gets default project JSON used for import data */ getDefaultProjectJson() { return { source: { location: '', parameters: {} }, project: { name: '', description: '' } }; } /** * Fetching operation has been done, so get workspaces and websocket connection */ updateData() { this.workspaceResource = this.workspaces.length > 0 ? 'existing-workspace' : 'from-stack'; //if create project in progress and workspace have started if (this.createProjectSvc.isCreateProjectInProgress() && this.createProjectSvc.getCurrentProgressStep() > 0) { let workspaceName = this.createProjectSvc.getWorkspaceOfProject(); let findWorkspace = this.lodash.find(this.workspaces, (workspace) => { return workspace.config.name === workspaceName; }); //check current workspace if (findWorkspace) { // init WS bus this.messageBus = this.cheAPI.getWebsocket().getBus(findWorkspace.id); } else { this.resetCreateProgress(); } } else { let preselectWorkspaceId = this.$location.search().workspaceId; if (preselectWorkspaceId) { this.workspaceSelected = this.lodash.find(this.workspaces, (workspace) => { return workspace.id === preselectWorkspaceId; }); } // generate project name this.generateProjectName(true); } } /** * Force codemirror editor to be refreshed */ refreshCM() { // hack to make a refresh of the zone this.importProjectData.cm = 'aaa'; this.$timeout(() => { delete this.importProjectData.cm; }, 500); } /** * Update internal json data from JSON codemirror editor config file */ update() { try { this.importProjectData = angular.fromJson(this.jsonConfig.content); } catch (e) { // invalid JSON, ignore } } /** * Select the given github repository * @param gitHubRepository the repository selected */ selectGitHubRepository(gitHubRepository) { this.setProjectName(gitHubRepository.name); this.setProjectDescription(gitHubRepository.description); this.importProjectData.source.location = gitHubRepository.clone_url; } /** * Checks if the current forms are being validated * @returns {boolean|FormController.$valid|*|ngModel.NgModelController.$valid|context.ctrl.$valid|Ic.$valid} */ checkValidFormState() { // check project information form and selected tab form if (this.selectSourceOption === 'select-source-new') { return this.projectInformationForm && this.projectInformationForm.$valid; } else if (this.selectSourceOption === 'select-source-existing') { var currentForm = this.forms.get(this.currentTab); if (currentForm) { return this.projectInformationForm && this.projectInformationForm.$valid && currentForm.$valid; } } } /** * Defines the project information form * @param form */ setProjectInformationForm(form) { this.projectInformationForm = form; } /** * Sets the form for a given mode * @param form the selected form * @param mode the tab selected */ setForm(form, mode) { this.forms.set(mode, form); } /** * Sets the current selected tab * @param tab the selected tab */ setCurrentTab(tab) { this.currentTab = tab; this.importProjectData = this.getDefaultProjectJson(); if ('blank' === tab) { this.importProjectData.project.type = 'blank'; } else if ('git' === tab || 'github' === tab) { this.importProjectData.source.type = 'git'; } else if ('zip' === tab) { this.importProjectData.project.type = ''; } else if ('config' === tab) { this.importProjectData.project.type = 'blank'; this.importProjectData.source.type = 'git'; //try to set default values this.setProjectDescription(this.importProjectData.project.description); this.setProjectName(this.importProjectData.project.name); this.refreshCM(); } // github and samples tabs have broadcast selection events for isReady status this.isReady = !('github' === tab || 'samples' === tab); } startWorkspace(bus, workspace) { // then we've to start workspace this.createProjectSvc.setCurrentProgressStep(1); // get channels let environments = workspace.config.environments; let envName = workspace.config.defaultEnv; let defaultEnvironment = this.lodash.find(environments, (environment) => { return environment.name === envName; }); let machineConfigsLinks = defaultEnvironment.machineConfigs[0].links; let findStatusLink = this.lodash.find(machineConfigsLinks, (machineConfigsLink) => { return machineConfigsLink.rel === 'get machine status channel'; }); let findOutputLink = this.lodash.find(machineConfigsLinks, (machineConfigsLink) => { return machineConfigsLink.rel === 'get machine logs channel'; }); let workspaceId = workspace.id; let agentChannel = 'workspace:' + workspace.id + ':ext-server:output'; let statusChannel = findStatusLink ? findStatusLink.parameters[0].defaultValue : null; let outputChannel = findOutputLink ? findOutputLink.parameters[0].defaultValue : null; this.listeningChannels.push(agentChannel); bus.subscribe(agentChannel, (message) => { if (this.createProjectSvc.getCurrentProgressStep() < 2) { this.createProjectSvc.setCurrentProgressStep(2); } let agentStep = 2; if (this.getCreationSteps()[agentStep].logs.length > 0) { this.getCreationSteps()[agentStep].logs = this.getCreationSteps()[agentStep].logs + '\n' + message; } else { this.getCreationSteps()[agentStep].logs = message; } }); if (statusChannel) { // for now, display log of status channel in case of errors this.listeningChannels.push(statusChannel); bus.subscribe(statusChannel, (message) => { if (message.eventType === 'DESTROYED' && message.workspaceId === workspace.id) { this.getCreationSteps()[this.getCurrentProgressStep()].hasError = true; // need to show the error this.$mdDialog.show( this.$mdDialog.alert() .title('Unable to start workspace') .content('Unable to start workspace. It may be linked to OutOfMemory or the container has been destroyed') .ariaLabel('Workspace start') .ok('OK') ); } if (message.eventType === 'ERROR' && message.workspaceId === workspace.id) { this.getCreationSteps()[this.getCurrentProgressStep()].hasError = true; // need to show the error this.$mdDialog.show( this.$mdDialog.alert() .title('Error when starting workspace') .content('Unable to start workspace. Error when trying to start the workspace: ' + message.error) .ariaLabel('Workspace start') .ok('OK') ); } this.$log.log('Status channel of workspaceID', workspaceId, message); }); } if (outputChannel) { this.listeningChannels.push(outputChannel); bus.subscribe(outputChannel, (message) => { if (this.getCreationSteps()[this.getCurrentProgressStep()].logs.length > 0) { this.getCreationSteps()[this.getCurrentProgressStep()].logs = this.getCreationSteps()[this.getCurrentProgressStep()].logs + '\n' + message; } else { this.getCreationSteps()[this.getCurrentProgressStep()].logs = message; } }); } let startWorkspacePromise = this.cheAPI.getWorkspace().startWorkspace(workspace.id, workspace.config.defaultEnv); startWorkspacePromise.then(() => {}, (error) => { if (error.data.message) { this.getCreationSteps()[this.getCurrentProgressStep()].logs = error.data.message; } this.getCreationSteps()[this.getCurrentProgressStep()].hasError = true; }); } createProjectInWorkspace(workspaceId, projectName, projectData, bus, websocketStream, workspaceBus) { this.createProjectSvc.setCurrentProgressStep(3); var promise; var channel = null; // select mode (create or import) if (this.selectSourceOption === 'select-source-new' && this.templatesChoice === 'templates-wizard') { // we do not create project as it will be done through wizard var deferred = this.$q.defer(); promise = deferred.promise; deferred.resolve(true); } else if (projectData.source.location.length > 0) { // if it's a user-defined location we need to cleanup commands that may have been configured by templates if (this.selectSourceOption === 'select-source-existing') { projectData.project.commands = []; } // websocket channel channel = 'importProject:output:' + workspaceId + ':' + projectName; // on import bus.subscribe(channel, (message) => { this.getCreationSteps()[this.getCurrentProgressStep()].logs = message.line; }); let deferredImport = this.$q.defer(); let deferredImportPromise = deferredImport.promise; let deferredAddCommand = this.$q.defer(); let deferredAddCommandPromise = deferredAddCommand.promise; let deferredResolve = this.$q.defer(); let deferredResolvePromise = deferredResolve.promise; let importPromise = this.cheAPI.getProject().importProject(workspaceId, projectName, projectData.source); importPromise.then(() => { // add commands if there are some that have been defined let commands = projectData.project.commands; if (commands && commands.length > 0) { this.addCommand(workspaceId, projectName, commands, 0, deferredAddCommand); } else { deferredAddCommand.resolve('no commands to add'); } deferredImport.resolve(); }, (error) => { deferredImport.reject(error); }); // now, resolve the project deferredImportPromise.then(() => { this.resolveProjectType(workspaceId, projectName, projectData, deferredResolve); }); promise = this.$q.all([deferredImportPromise, deferredAddCommandPromise, deferredResolvePromise]); } promise.then(() => { this.cleanupChannels(websocketStream, workspaceBus, bus, channel); this.createProjectSvc.setCurrentProgressStep(4); }, (error) => { this.cleanupChannels(websocketStream, workspaceBus, bus, channel); this.getCreationSteps()[this.getCurrentProgressStep()].hasError = true; //if we have a SSH error if (error.data.errorCode === 32068) { this.showAddSecretKeyDialog(projectData.source.location, workspaceId); return; } this.$mdDialog.show( this.$mdDialog.alert() .title('Error while creating the project') .content(error.statusText + ': ' + error.data.message) .ariaLabel('Project creation') .ok('OK') ); }); } resolveProjectType(workspaceId, projectName, projectData, deferredResolve) { let projectDetails = projectData.project; if (!projectDetails.attributes) { projectDetails.source = projectData.source; projectDetails.attributes = {}; } if (projectDetails.type) { let updateProjectPromise = this.cheAPI.getProject().updateProject(workspaceId, projectName, projectDetails); updateProjectPromise.then(() => { deferredResolve.resolve(); }); return; } let resolvePromise = this.cheAPI.getProject().fetchResolve(workspaceId, projectName); resolvePromise.then(() => { let resultResolve = this.cheAPI.getProject().getResolve(workspaceId, projectName); // get project-types let fetchTypePromise = this.cheAPI.getProjectType().fetchTypes(workspaceId); fetchTypePromise.then(() => { let projectTypesByCategory = this.cheAPI.getProjectType().getProjectTypesIDs(workspaceId); // now try the estimate for each source let deferredEstimate = this.$q.defer(); let deferredEstimatePromise = deferredResolve.promise; let estimatePromises = []; let estimateTypes = []; resultResolve.forEach((sourceResolve) => { // add attributes if any if (sourceResolve.attributes && Object.keys(sourceResolve.attributes).length > 0) { for (let attributeKey in sourceResolve.attributes) { projectDetails.attributes[attributeKey] = sourceResolve.attributes[attributeKey]; } } let projectType = projectTypesByCategory.get(sourceResolve.type); if (projectType.primaryable) { // call estimate let estimatePromise = this.cheAPI.getProject().fetchEstimate(workspaceId, projectName, sourceResolve.type); estimatePromises.push(estimatePromise); estimateTypes.push(sourceResolve.type); } }); if (estimateTypes.length > 0) { // wait estimate are all finished let waitEstimate = this.$q.all(estimatePromises); waitEstimate.then(() => { var firstMatchingType; var firstMatchingResult; estimateTypes.forEach((type) => { let resultEstimate = this.cheAPI.getProject().getEstimate(workspaceId, projectName, type); // add attributes // there is a matching estimate if (Object.keys(resultEstimate.attributes).length > 0 && 'java' !== type && !firstMatchingType) { firstMatchingType = type; firstMatchingResult = resultEstimate.attributes; } }); if (firstMatchingType) { projectDetails.attributes = firstMatchingResult; projectDetails.type = firstMatchingType; let updateProjectPromise = this.cheAPI.getProject().updateProject(workspaceId, projectName, projectDetails); updateProjectPromise.then(() => { deferredResolve.resolve(); }); } else { deferredResolve.resolve(); } }); } else { deferredResolve.resolve(); } }); }, (error) => { deferredResolve.reject(error); }); } /** * Show the add ssh key dialog * @param repoURL the repository URL * @param workspaceId the workspace IDL */ showAddSecretKeyDialog(repoURL, workspaceId) { let parentEl = angular.element(this.$document.body); this.$mdDialog.show({ bindToController: true, clickOutsideToClose: true, controller: 'AddSecretKeyNotificationCtrl', controllerAs: 'addSecretKeyNotificationCtrl', locals: {repoURL: repoURL, workspaceId: workspaceId}, parent: parentEl, templateUrl: 'app/projects/create-project/add-ssh-key-notification/add-ssh-key-notification.html' }); } /** * Cleanup the websocket elements after actions are finished */ cleanupChannels(websocketStream, workspaceBus, bus, channel) { if (websocketStream != null) { websocketStream.close(); } if (workspaceBus != null) { this.listeningChannels.forEach((channel) => { workspaceBus.unsubscribe(channel); }); this.listeningChannels.length = 0; } if (channel != null) { bus.unsubscribe(channel); } } /** * Add commands sequentially by iterating on the number of the commands. * Wait the ack of remote addCommand before adding a new command to avoid concurrent access * @param workspaceId the ID of the workspace to use for adding commands * @param projectName the name that will be used to prefix the commands inserted * @param commands the array to follow * @param index the index of the array of commands to register */ addCommand(workspaceId, projectName, commands, index, deferred) { if (index < commands.length) { let newCommand = angular.copy(commands[index]); // Update project command lines using current.project.path with actual path based on workspace runtime configuration // so adding the same project twice allow to use commands for each project without first selecting project in tree let workspace = this.cheAPI.getWorkspace().getWorkspaceById(workspaceId); if (workspace && workspace.runtime) { let runtime = workspace.runtime.devMachine.runtime; if (runtime) { let envVar = runtime.envVariables; if (envVar) { let cheProjectsRoot = envVar['CHE_PROJECTS_ROOT']; if (cheProjectsRoot) { // replace current project path by the full path of the project let projectPath = cheProjectsRoot + '/' + projectName; newCommand.commandLine = newCommand.commandLine.replace(/\$\{current.project.path\}/g, projectPath); } } } } newCommand.name = projectName + ': ' + newCommand.name; var addPromise = this.cheAPI.getWorkspace().addCommand(workspaceId, newCommand); addPromise.then(() => { // call the method again this.addCommand(workspaceId, projectName, commands, ++index, deferred); }, (error) => { deferred.reject(error); }); } else { deferred.resolve('All commands added'); } } connectToExtensionServer(websocketURL, workspaceId, projectName, projectData, workspaceBus, bus) { // try to connect let websocketStream = this.$websocket(websocketURL); // on success, create project websocketStream.onOpen(() => { let bus = this.cheAPI.getWebsocket().getExistingBus(websocketStream); this.createProjectInWorkspace(workspaceId, projectName, projectData, bus, websocketStream, workspaceBus); }); // on error, retry to connect or after a delay, abort websocketStream.onError((error) => { this.websocketReconnect--; if (this.websocketReconnect > 0) { this.$timeout(() => { this.connectToExtensionServer(websocketURL, workspaceId, projectName, projectData, workspaceBus, bus); }, 1000); } else { this.getCreationSteps()[this.getCurrentProgressStep()].hasError = true; this.$log.log('error when starting remote extension', error); // need to show the error this.$mdDialog.show( this.$mdDialog.alert() .title('Workspace Connection Error') .content('It seems that your workspace is running, but we cannot connect your browser to it. This commonly happens when Che was' + ' not configured properly. If your browser is connecting to workspaces running remotely, then you must start Che with the ' + '--remote: flag where the is the IP address of the node that is running your Docker workspaces.' + 'Please restart Che with this flag. You can read about what this flag does and why it is essential at: ' + 'https://eclipse-che.readme.io/docs/configuration#envrionment-variables') .ariaLabel('Project creation') .ok('OK') ); } }); } /** * User has selected a stack. needs to find or add recipe for that stack */ computeRecipeForStack(stack) { // look at recipe let recipeSource = stack.source; let promise; // what is type of source ? if ('image' === recipeSource.type) { // needs to add recipe for that script promise = this.submitRecipe('generated-' + stack.name, 'FROM ' + recipeSource.origin); } else if ('dockerfile' === recipeSource.type.toLowerCase()) { promise = this.submitRecipe('generated-' + stack.name, recipeSource.origin); } else { throw 'Not implemented'; } return promise; } submitRecipe(recipeName, recipeScript) { let recipe = { type: 'docker', name: recipeName, permissions: { groups: [ { name: 'public', acl: [ 'read' ] } ], users: {} }, script: recipeScript }; return this.cheAPI.getRecipe().create(recipe); } /** * Call the create operation that may create or import a project */ create() { this.importProjectData.project.description = this.projectDescription; this.importProjectData.project.name = this.projectName; this.createProjectSvc.setProject(this.projectName); if (this.templatesChoice === 'templates-wizard') { this.createProjectSvc.setIDEAction('createProject:projectName=' + this.projectName); } // reset logs and errors this.resetCreateProgress(); this.setCreateProjectInProgress(); this.createProjectSvc.createPopup(); // logic to decide if we create workspace based on a stack or reuse existing workspace let option; if (this.workspaceResource === 'existing-workspace') { option = 'reuse-workspace'; this.recipeUrl = null; this.stack = null; } else { switch (this.stackTab) { case 'ready-to-go': option = 'create-workspace'; this.stack = this.readyToGoStack; break; case 'stack-library': option = 'create-workspace'; this.stack = this.stackLibraryUser; break; case 'custom-stack': option = 'create-workspace'; this.stack = null; break; } } // check workspace is selected if (option === 'create-workspace') { if (this.stack) { // needs to get recipe URL from stack let promise = this.computeRecipeForStack(this.stack); promise.then((recipe) => { let findLink = this.lodash.find(recipe.links, (link) => { return link.rel === 'get recipe script'; }); if (findLink) { this.recipeUrl = findLink.href; this.createWorkspace(); } }); } else { if (this.recipeUrl && this.recipeUrl.length > 0) { this.createWorkspace(); } else { let recipeName = 'rcp-' + (('0000' + (Math.random() * Math.pow(36, 4) << 0).toString(36)).slice(-4)); // jshint ignore:line // needs to get recipe URL from custom recipe let promise = this.submitRecipe(recipeName, this.recipeScript); promise.then((recipe) => { let findLink = this.lodash.find(recipe.links, (link) => { return link.rel === 'get recipe script'; }); if (findLink) { this.recipeUrl = findLink.href; this.createWorkspace(); } }); } } } else { this.createProjectSvc.setWorkspaceOfProject(this.workspaceSelected.config.name); // Now that the container is started, wait for the extension server. For this, needs to get runtime details let promiseWorkspace = this.cheAPI.getWorkspace().fetchWorkspaceDetails(this.workspaceSelected.id); promiseWorkspace.then(() => { let websocketUrl = this.cheAPI.getWorkspace().getWebsocketUrl(this.workspaceSelected.id); // Get bus let websocketStream = this.$websocket(websocketUrl); // on success, create project websocketStream.onOpen(() => { let bus = this.cheAPI.getWebsocket().getExistingBus(websocketStream); // mode this.createProjectInWorkspace(this.workspaceSelected.id, this.projectName, this.importProjectData, bus); }); }, (error) => { if (error.data.message) { this.getCreationSteps()[this.getCurrentProgressStep()].logs = error.data.message; } this.getCreationSteps()[this.getCurrentProgressStep()].hasError = true; }); } // do we have projects ? let projects = this.cheAPI.getProject().getAllProjects(); if (projects.length > 1) { // we have projects, show notification first and redirect to the list of projects this.createProjectSvc.showPopup(); this.$location.path('/projects'); } } /** * Create a new workspace from current workspace name, recipe url and workspace ram */ createWorkspace() { this.createProjectSvc.setWorkspaceOfProject(this.workspaceName); let attributes = this.stack ? {stackId: this.stack.id} : {}; //TODO: no account in che ? it's null when testing on localhost let creationPromise = this.cheAPI.getWorkspace().createWorkspace(null, this.workspaceName, this.recipeUrl, this.workspaceRam, attributes); creationPromise.then((workspace) => { // init message bus if not there if (this.workspaces.length === 0) { this.messageBus = this.cheAPI.getWebsocket().getBus(workspace.id); } // recipe url let bus = this.cheAPI.getWebsocket().getBus(workspace.id); // subscribe to workspace events let workspaceChannel = 'workspace:' + workspace.id; this.listeningChannels.push(workspaceChannel); bus.subscribe(workspaceChannel, (message) => { if (message.eventType === 'ERROR' && message.workspaceId === workspace.id) { this.createProjectSvc.setCurrentProgressStep(2); this.getCreationSteps()[this.getCurrentProgressStep()].hasError = true; // need to show the error this.$mdDialog.show( this.$mdDialog.alert() .title('Error when starting agent') .content('Unable to start workspace agent. Error when trying to start the workspace agent: ' + message.error) .ariaLabel('Workspace agent start') .ok('OK') ); } if (message.eventType === 'RUNNING' && message.workspaceId === workspace.id) { this.createProjectSvc.setCurrentProgressStep(2); this.importProjectData.project.name = this.projectName; let promiseWorkspace = this.cheAPI.getWorkspace().fetchWorkspaceDetails(workspace.id); promiseWorkspace.then(() => { let websocketUrl = this.cheAPI.getWorkspace().getWebsocketUrl(workspace.id); // try to connect this.websocketReconnect = 10; this.connectToExtensionServer(websocketUrl, workspace.id, this.importProjectData.project.name, this.importProjectData, bus); }); } }); this.$timeout(() => { this.startWorkspace(bus, workspace); }, 1000); }, (error) => { if (error.data.message) { this.getCreationSteps()[this.getCurrentProgressStep()].logs = error.data.message; } this.getCreationSteps()[this.getCurrentProgressStep()].hasError = true; }); } /** * Generates a default project name only if user has not entered any data * @param firstInit on first init, user do not have yet initialized something */ generateProjectName(firstInit) { // name has not been modified by the user if (firstInit || (this.projectInformationForm['deskname'].$pristine && this.projectInformationForm.name.$pristine)) { // generate a name // starts with project var name = 'project'; // type selected if (this.importProjectData.project.type) { name = this.importProjectData.project.type.replace(/\s/g, '_'); } name = name + '-' + (('0000' + (Math.random() * Math.pow(36, 4) << 0).toString(36)).slice(-4)); // jshint ignore:line this.setProjectName(name); } } /** * Generates a default workspace name */ generateWorkspaceName() { // starts with wksp let name = 'wksp'; name += '-' + (('0000' + (Math.random() * Math.pow(36, 4) << 0).toString(36)).slice(-4)); // jshint ignore:line this.setWorkspaceName(name); } isImporting() { return this.isCreateProjectInProgress(); } isReadyToCreate() { return !this.isCreateProjectInProgress() && this.isReady; } resetCreateProgress() { this.createProjectSvc.resetCreateProgress(); } resetCreateNewProject() { this.resetCreateProgress(); this.generateWorkspaceName(); this.generateProjectName(true); } showIDE() { this.$rootScope.showIDE = !this.$rootScope.showIDE; } getStepText(stepNumber) { return this.createProjectSvc.getStepText(stepNumber); } getCreationSteps() { return this.createProjectSvc.getProjectCreationSteps(); } getCurrentProgressStep() { return this.createProjectSvc.getCurrentProgressStep(); } isCreateProjectInProgress() { return this.createProjectSvc.isCreateProjectInProgress(); } setCreateProjectInProgress() { this.createProjectSvc.setCreateProjectInProgress(true); } hideCreateProjectPanel() { this.createProjectSvc.showPopup(); } getWorkspaceOfProject() { return this.createProjectSvc.getWorkspaceOfProject(); } getIDELink() { return this.createProjectSvc.getIDELink(); } isElementVisible(index) { // for each id, check last var maxVisibleElement = 0; for (var i = 0; i < this.headerSteps.length; i++) { var visibleElement = this.isvisible(this.headerSteps[i].id); if (visibleElement) { maxVisibleElement = i; } } return index <= maxVisibleElement; } isvisible(elementName) { let element = angular.element(elementName); var windowElement = $(window); var docViewTop = windowElement.scrollTop(); var docViewBottom = docViewTop + windowElement.height(); var offset = element.offset(); if (!offset) { return false; } var elemTop = offset.top; var elemBottom = elemTop + element.height(); // use elemTop if want to see all div or elemBottom if we see partially it /*((elemTop <= docViewBottom) && (elemTop >= docViewTop));*/ return ((elemBottom <= docViewBottom) && (elemTop >= docViewTop)); } setStackTab(stackTab) { this.stackTab = stackTab; } /** * Update data for selected workspace */ onWorkspaceChange() { if (!this.workspaceSelected) { return; } this.setWorkspaceName(this.workspaceSelected.config.name); let stack = null; if (this.workspaceSelected.attributes && this.workspaceSelected.attributes.stackId) { stack = this.cheStack.getStackById(this.workspaceSelected.attributes.stackId); } this.updateCurrentStack(stack); let findEnvironment = this.lodash.find(this.workspaceSelected.config.environments, (environment) => { return environment.name === this.workspaceSelected.config.defaultEnv; }); if (findEnvironment) { this.workspaceRam = findEnvironment.machineConfigs[0].limits.ram; } this.updateWorkspaceStatus(true); } /** * Update creation flow state when source option changes */ onSourceOptionChanged() { if ('select-source-existing' === this.selectSourceOption) { //Need to call selection of current tab this.setCurrentTab(this.currentTab); } } /** * Use of an existing stack * @param stack the stack to use */ cheStackLibrarySelecter(stack) { if (this.workspaceResource === 'existing-workspace') { return; } if (this.stackTab === 'ready-to-go') { this.readyToGoStack = angular.copy(stack); } else if (this.stackTab === 'stack-library') { this.stackLibraryUser = angular.copy(stack); } this.updateCurrentStack(stack); this.updateWorkspaceStatus(false); } updateWorkspaceStatus(isExistingWorkspace) { if (isExistingWorkspace) { this.stackLibraryOption = 'existing-workspace'; } else { this.stackLibraryOption = 'new-workspace'; this.generateWorkspaceName(); } this.$rootScope.$broadcast('chePanel:disabled', {id: 'create-project-workspace', disabled: isExistingWorkspace}); } /** * Update current stack * @param stack the stack to use */ updateCurrentStack(stack) { this.currentStackTags = stack && stack.tags ? angular.copy(stack.tags) : null; if (!stack) { return; } this.templatesChoice = 'templates-samples'; this.generateProjectName(true); // Enable wizard only if // - ready-to-go-stack with PT // - custom stack if (stack === null || 'general' !== stack.scope) { this.enableWizardProject = true; return; } this.enableWizardProject = 'Java' === stack.name; } selectWizardProject() { this.importProjectData.source.location = ''; } /** * Set workspace name * @param name */ setWorkspaceName(name) { if (!name) { return; } if (!this.defaultWorkspaceName || this.defaultWorkspaceName === this.workspaceName) { this.defaultWorkspaceName = name; this.workspaceName = angular.copy(name); } } /** * Set project name * @param name */ setProjectName(name) { if (!name) { return; } if (!this.projectName || !this.defaultProjectName || this.defaultProjectName === this.projectName) { this.defaultProjectName = name; this.projectName = angular.copy(name); } this.importProjectData.project.name = this.projectName; } /** * Set project description * @param description */ setProjectDescription(description) { if (!description) { return; } if (!this.projectDescription || !this.defaultProjectDescription || this.defaultProjectDescription === this.projectDescription) { this.defaultProjectDescription = description; this.projectDescription = angular.copy(description); } this.importProjectData.project.description = this.projectDescription; } downloadLogs() { let logs = ''; this.getCreationSteps().forEach((step) => { logs += step.logs + '\n'; }); window.open('data:text/csv,' + encodeURIComponent(logs)); } }