Adapt project selector widget to manage projects from devfile

Signed-off-by: Anna Shumilova <ashumilo@redhat.com>
7.20.x
Anna Shumilova 2019-07-12 10:36:56 +03:00
parent ccda8e309f
commit 69e7929509
27 changed files with 153 additions and 525 deletions

View File

@ -28,7 +28,6 @@ export class SelectTemplateController {
projectsOrderBy: string;
private $mdDialog: ng.material.IDialogService;
private templates: Array<che.IProject>;
private callbackController: StackController;
/**
@ -37,11 +36,6 @@ export class SelectTemplateController {
constructor(cheAPI: CheAPI, $mdDialog: ng.material.IDialogService) {
this.$mdDialog = $mdDialog;
this.templates = cheAPI.getProjectTemplate().getAllProjectTemplates();
if (!this.templates.length) {
cheAPI.getProjectTemplate().fetchTemplates();
}
this.projectsOrderBy = 'displayName';
this.selectedTemplates = [];
}

View File

@ -16,7 +16,6 @@ import {EnvironmentManager} from '../../../components/api/environment/environmen
import {IEnvironmentManagerMachine} from '../../../components/api/environment/environment-manager-machine';
import {CreateWorkspaceSvc} from './create-workspace.service';
import {NamespaceSelectorSvc} from './namespace-selector/namespace-selector.service';
import {StackSelectorSvc} from './stack-selector/stack-selector.service';
import {RandomSvc} from '../../../components/utils/random.service';
import {CheNotification} from '../../../components/notification/che-notification.factory';
import {
@ -174,7 +173,7 @@ export class CreateWorkspaceController {
this.$timeout(() => {
this.hideLoader = true;
}, 10);
this.selectedDevfile = devfile
this.selectedDevfile = devfile;
}
/**
@ -298,8 +297,9 @@ export class CreateWorkspaceController {
*/
createWorkspace(): ng.IPromise<che.IWorkspace> {
// update workspace name
this.selectedDevfile.metadata.name = this.workspaceName;
return this.createWorkspaceSvc.createWorkspaceFromDevfile(this.selectedDevfile, null);
let devfileSource = angular.copy(this.selectedDevfile);
devfileSource.metadata.name = this.workspaceName;
return this.createWorkspaceSvc.createWorkspaceFromDevfile(devfileSource, null);
}
/**

View File

@ -56,12 +56,10 @@
<devfile-selector on-devfile-select="createWorkspaceController.onDevfileSelected(devfile)">
</devfile-selector>
</che-label-container>
<!-- Project source selector -->
<che-label-container che-label-name="Projects">
<project-source-selector></project-source-selector>
<project-source-selector devfile="createWorkspaceController.selectedDevfile"></project-source-selector>
</che-label-container>
<che-label-container>
<che-button-save-flat class="create-workspace-footer-button"
name="saveButton"

View File

@ -12,7 +12,6 @@
'use strict';
import {NamespaceSelectorSvc} from './namespace-selector/namespace-selector.service';
import {StackSelectorSvc} from './stack-selector/stack-selector.service';
import {ProjectSourceSelectorService} from './project-source-selector/project-source-selector.service';
import {CheNotification} from '../../../components/notification/che-notification.factory';
import {ConfirmDialogService} from '../../../components/service/confirm-dialog/confirm-dialog.service';
@ -25,7 +24,7 @@ import {CheWorkspace} from '../../../components/api/workspace/che-workspace.fact
*/
export class CreateWorkspaceSvc {
static $inject = ['$location', '$log', '$q', 'cheWorkspace', 'namespaceSelectorSvc', 'stackSelectorSvc', 'projectSourceSelectorService', 'cheNotification', 'confirmDialogService', '$document'];
static $inject = ['$location', '$log', '$q', 'cheWorkspace', 'namespaceSelectorSvc', 'projectSourceSelectorService', 'cheNotification', 'confirmDialogService', '$document'];
/**
* Location service.
@ -51,10 +50,6 @@ export class CreateWorkspaceSvc {
* Project selector service.
*/
private projectSourceSelectorService: ProjectSourceSelectorService;
/**
* Stack selector service.
*/
private stackSelectorSvc: StackSelectorSvc;
/**
* The list of workspaces by namespace.
*/
@ -77,13 +72,12 @@ export class CreateWorkspaceSvc {
/**
* Default constructor that is using resource injection
*/
constructor($location: ng.ILocationService, $log: ng.ILogService, $q: ng.IQService, cheWorkspace: CheWorkspace, namespaceSelectorSvc: NamespaceSelectorSvc, stackSelectorSvc: StackSelectorSvc, projectSourceSelectorService: ProjectSourceSelectorService, cheNotification: CheNotification, confirmDialogService: ConfirmDialogService, $document: ng.IDocumentService) {
constructor($location: ng.ILocationService, $log: ng.ILogService, $q: ng.IQService, cheWorkspace: CheWorkspace, namespaceSelectorSvc: NamespaceSelectorSvc, projectSourceSelectorService: ProjectSourceSelectorService, cheNotification: CheNotification, confirmDialogService: ConfirmDialogService, $document: ng.IDocumentService) {
this.$location = $location;
this.$log = $log;
this.$q = $q;
this.cheWorkspace = cheWorkspace;
this.namespaceSelectorSvc = namespaceSelectorSvc;
this.stackSelectorSvc = stackSelectorSvc;
this.projectSourceSelectorService = projectSourceSelectorService;
this.cheNotification = cheNotification;
this.confirmDialogService = confirmDialogService;
@ -189,27 +183,39 @@ export class CreateWorkspaceSvc {
});
}
createWorkspaceFromDevfile(workspaceDevfile: che.IWorkspaceDevfile, attributes: any): ng.IPromise<che.IWorkspace> {
createWorkspaceFromDevfile(sourceDevfile: che.IWorkspaceDevfile, attributes: any): ng.IPromise<che.IWorkspace> {
const namespaceId = this.namespaceSelectorSvc.getNamespaceId(),
projectTemplates = this.projectSourceSelectorService.getProjectTemplates();
let projects = [];
let noProjectsFromDevfile = true;
projectTemplates.forEach((template: che.IProjectTemplate) => {
let project = {
name: template.displayName,
name: template.name,
source: {
type: template.source.type,
location: template.source.location
}
};
projects.push(project);
});
sourceDevfile.projects.forEach((project) => {
if (project.name === template.name) {
noProjectsFromDevfile = false;
}
});
projects.push(project);
});
return this.checkEditingProgress().then(() => {
workspaceDevfile.projects = projects;
//TODO waits for fix https://github.com/eclipse/che/issues/13514
//this.addProjectCommands({devfile: workspaceDevfile}, projectTemplates);
return this.cheWorkspace.createWorkspaceFromDevfile(namespaceId, workspaceDevfile, attributes).then((workspace: che.IWorkspace) => {
sourceDevfile.projects = projects;
// If no projects defined in devfile were added - remove the commands from devfile as well:
if (noProjectsFromDevfile) {
sourceDevfile.commands = [];
}
return this.cheWorkspace.createWorkspaceFromDevfile(namespaceId, sourceDevfile, attributes).then((workspace: che.IWorkspace) => {
return this.cheWorkspace.fetchWorkspaces().then(() => this.cheWorkspace.getWorkspaceById(workspace.id));
})
.then((workspace: che.IWorkspace) => {

View File

@ -27,6 +27,7 @@ export class AddImportProjectController {
* Project selector service.
*/
private addImportProjectService: AddImportProjectService;
/**
* Project source selector service.
*/
@ -46,6 +47,7 @@ export class AddImportProjectController {
/* tslint:disable */
private isProjectNameUnique: (data: {name: string}) => boolean;
/* tslint:enable */
private devfile: che.IWorkspaceDevfile;
/**
* Callback provided by parent controller.
*/
@ -60,7 +62,7 @@ export class AddImportProjectController {
this.projectSource = ProjectSource;
this.activeProjectSource = ProjectSource.SAMPLES;
this.activeProjectSource = this.devfile ? ProjectSource.SAMPLES : ProjectSource.GIT;
this.sourceChanged();
$scope.$on('$destroy', () => {

View File

@ -28,6 +28,7 @@ export class AddImportProject implements ng.IDirective {
[propName: string]: string;
} = {
isProjectNameUnique: '&',
projectOnAdd: '&'
projectOnAdd: '&',
devfile: '='
};
}

View File

@ -8,34 +8,24 @@
flex-order="2" flex-order-gt-md="1">
<che-toggle-joined-button che-title="Samples"
id="samples-button"
ng-if="addImportProjectController.devfile"
che-value="addImportProjectController.projectSource.SAMPLES"></che-toggle-joined-button>
<che-toggle-joined-button che-title="Blank"
id="blank-button"
che-value="addImportProjectController.projectSource.BLANK"></che-toggle-joined-button>
<che-toggle-joined-button che-title="Git"
id="git-button"
che-value="addImportProjectController.projectSource.GIT"></che-toggle-joined-button>
<che-toggle-joined-button che-title="GitHub"
id="github-button"
che-value="addImportProjectController.projectSource.GITHUB"></che-toggle-joined-button>
<che-toggle-joined-button che-title="Zip"
id="zip-button"
che-value="addImportProjectController.projectSource.ZIP"></che-toggle-joined-button>
</che-toggle-joined>
</div>
<div class="add-import-project-sources">
<template-selector
ng-if="addImportProjectController.activeProjectSource === addImportProjectController.projectSource.SAMPLES"></template-selector>
<import-blank-project
ng-if="addImportProjectController.activeProjectSource === addImportProjectController.projectSource.BLANK"
is-project-name-unique="addImportProjectController.isProjectNameUnique({name: name})"></import-blank-project>
ng-if="addImportProjectController.activeProjectSource === addImportProjectController.projectSource.SAMPLES && addImportProjectController.devfile" devfile="addImportProjectController.devfile"></template-selector>
<import-git-project
ng-if="addImportProjectController.activeProjectSource === addImportProjectController.projectSource.GIT"></import-git-project>
<import-github-project
ng-if="addImportProjectController.activeProjectSource === addImportProjectController.projectSource.GITHUB"></import-github-project>
<import-zip-project
ng-if="addImportProjectController.activeProjectSource === addImportProjectController.projectSource.ZIP"></import-zip-project>
</div>
<md-divider></md-divider>

View File

@ -53,6 +53,7 @@ export class AddImportProjectService {
* Project source.
*/
private activeProjectSource: ProjectSource;
/**
* Instance of Observable.
*/
@ -105,7 +106,7 @@ export class AddImportProjectService {
* @return {che.IProjectTemplate}
*/
buildProjectTemplate(props: any): che.IProjectTemplate {
const blankProjectTemplate = this.templateSelectorSvc.getTemplateByName('blank-project');
const blankProjectTemplate = {name: 'blank-project'};
return angular.merge({}, blankProjectTemplate, props);
}
@ -201,5 +202,4 @@ export class AddImportProjectService {
return null;
}
}

View File

@ -12,7 +12,6 @@
'use strict';
import {TemplateSelectorSvc} from './template-selector.service';
import {StackSelectorSvc} from '../../../stack-selector/stack-selector.service';
import {ProjectSource} from '../../project-source.enum';
import {AddImportProjectService} from '../add-import-project.service';
@ -23,7 +22,7 @@ import {AddImportProjectService} from '../add-import-project.service';
*/
export class TemplateSelectorController {
static $inject = ['$filter', '$scope', 'addImportProjectService', 'templateSelectorSvc', 'stackSelectorSvc', 'cheListHelperFactory'];
static $inject = ['$filter', '$scope', 'addImportProjectService', 'templateSelectorSvc', 'cheListHelperFactory'];
/**
* Filter service.
@ -33,10 +32,6 @@ export class TemplateSelectorController {
* Template selector service.
*/
private templateSelectorSvc: TemplateSelectorSvc;
/**
* Stack selector service.
*/
private stackSelectorSvc: StackSelectorSvc;
/**
* Service for project adding and importing.
*/
@ -45,10 +40,8 @@ export class TemplateSelectorController {
* Helper for lists.
*/
private cheListHelper: che.widget.ICheListHelper;
/**
* The list of tags of selected stack.
*/
private stackTags: string[];
private devfile: che.IWorkspaceDevfile;
/**
* Sorted list of all templates.
*/
@ -66,31 +59,32 @@ export class TemplateSelectorController {
* Default constructor that is using resource injection
*/
constructor($filter: ng.IFilterService, $scope: ng.IScope, addImportProjectService: AddImportProjectService, templateSelectorSvc: TemplateSelectorSvc,
stackSelectorSvc: StackSelectorSvc, cheListHelperFactory: che.widget.ICheListHelperFactory) {
cheListHelperFactory: che.widget.ICheListHelperFactory) {
this.$filter = $filter;
this.templateSelectorSvc = templateSelectorSvc;
this.stackSelectorSvc = stackSelectorSvc;
this.addImportProjectService = addImportProjectService;
const helperId = 'template-selector';
this.cheListHelper = cheListHelperFactory.getHelper(helperId);
$scope.$watch(() => {
return this.devfile;
}, () => {
if (!this.devfile) {
return;
}
let projects = this.devfile.projects ? this.devfile.projects : [];
this.allTemplates = this.$filter('orderBy')(projects, ['name']);
this.filterAndSortTemplates();
}, true);
$scope.$on('$destroy', () => {
cheListHelperFactory.removeHelper(helperId);
});
this.filteredTemplates = [];
this.selectedTemplates = this.templateSelectorSvc.getTemplates();
this.allTemplates = this.$filter('orderBy')(this.templateSelectorSvc.getAllTemplates(), ['projectType', 'displayName']);
this.filterAndSortTemplates();
const actionOnStackChanged = () => {
this.onStackChanged();
};
this.stackSelectorSvc.subscribe(actionOnStackChanged);
this.onStackChanged();
const actionOnPublish = (source: ProjectSource) => {
this.onAddImportProjectServicePublish(source);
};
@ -98,25 +92,9 @@ export class TemplateSelectorController {
$scope.$on('$destroy', () => {
this.addImportProjectService.unsubscribe(actionOnPublish);
this.stackSelectorSvc.unsubscribe(actionOnStackChanged);
});
}
/**
* Callback which is called when stack is selected.
*/
onStackChanged(): void {
const stackId = this.stackSelectorSvc.getStackId();
if (!stackId) {
return;
}
const stack = this.stackSelectorSvc.getStackById(stackId);
this.stackTags = stack ? stack.tags : [];
this.filterAndSortTemplates();
}
/**
* Callback which is called when project template is added to the list of ready-to-import projects.
* Make samples not checked.
@ -138,17 +116,7 @@ export class TemplateSelectorController {
* Filters templates by tags and sort them by project type and template name.
*/
filterAndSortTemplates(): void {
const stackTags = !this.stackTags ? [] : this.stackTags.map((tag: string) => tag.toLowerCase());
if (stackTags.length === 0) {
this.filteredTemplates = angular.copy(this.allTemplates);
} else {
this.filteredTemplates = this.allTemplates.filter((template: che.IProjectTemplate) => {
const templateTags = template.tags.map((tag: string) => tag.toLowerCase());
return stackTags.some((tag: string) => templateTags.indexOf(tag) > -1);
});
}
this.filteredTemplates = angular.copy(this.allTemplates);
this.cheListHelper.setList(this.filteredTemplates, 'name');
this.selectedTemplates.forEach((template: che.IProjectTemplate) => {
this.cheListHelper.itemsSelectionStatus[template.name] = true;

View File

@ -35,7 +35,7 @@ export class TemplateSelector implements ng.IDirective {
*/
constructor() {
this.scope = {
stackTags: '='
devfile: '='
};
}

View File

@ -11,7 +11,6 @@
*/
'use strict';
import {CheProjectTemplate} from '../../../../../../components/api/che-project-template.factory';
import {editingProgress, IEditingProgress} from '../../project-source-selector-editing-progress';
/**
@ -21,7 +20,7 @@ import {editingProgress, IEditingProgress} from '../../project-source-selector-e
*/
export class TemplateSelectorSvc implements IEditingProgress {
static $inject = ['$filter', '$q', 'cheProjectTemplate'];
static $inject = ['$filter', '$q'];
/**
* Filter service.
@ -31,10 +30,6 @@ export class TemplateSelectorSvc implements IEditingProgress {
* Promises service.
*/
$q: ng.IQService;
/**
* Project template API interactions.
*/
cheProjectTemplate: CheProjectTemplate;
/**
* The list of selected templates.
*/
@ -43,10 +38,9 @@ export class TemplateSelectorSvc implements IEditingProgress {
/**
* Default constructor that is using resource injection
*/
constructor($filter: ng.IFilterService, $q: ng.IQService, cheProjectTemplate: CheProjectTemplate) {
constructor($filter: ng.IFilterService, $q: ng.IQService) {
this.$filter = $filter;
this.$q = $q;
this.cheProjectTemplate = cheProjectTemplate;
this.templates = [];
}
@ -68,45 +62,6 @@ export class TemplateSelectorSvc implements IEditingProgress {
};
}
/**
* Fetches list of templates.
*/
getOrFetchTemplates(): ng.IPromise<any> {
const defer = this.$q.defer();
const templates = this.cheProjectTemplate.getAllProjectTemplates();
if (templates.length) {
defer.resolve();
} else {
this.cheProjectTemplate.fetchTemplates().finally(() => {
defer.resolve();
});
}
return defer.promise;
}
/**
* Returns list of fetched project templates.
*
* @return {Array<che.IProjectTemplate>}
*/
getAllTemplates(): Array<che.IProjectTemplate> {
return this.cheProjectTemplate.getAllProjectTemplates();
}
/**
* Returns project template by name.
*
* @param {string} name the project template name
* @return {undefined|che.IProjectTemplate}
*/
getTemplateByName(name: string): che.IProjectTemplate {
return this.getAllTemplates().find((template: che.IProjectTemplate) => {
return template.name === name;
});
}
/**
* Callback which is called when template is checked or unchecked.
*

View File

@ -87,7 +87,7 @@ export class ProjectMetadataController {
}
/**
* Restores project's name, description and source location from original state.
* Restores project's name and source location from original state.
*/
onEditProjectServicePublish(): void {
this.template = angular.copy(this.origTemplate);
@ -104,16 +104,6 @@ export class ProjectMetadataController {
this.projectMetadataService.onMetadataChanged(this.template);
}
/**
* Callback which is called when description is changed.
*
* @param {string} description the project's description
*/
onDescriptionChanged(description: string): void {
this.template.description = description;
this.projectMetadataService.onMetadataChanged(this.template);
}
/**
* Callback which is called when metadata is changed.
*

View File

@ -19,20 +19,6 @@
<div ng-message="md-maxlength">The name has to be less than 128 characters long.</div>
</che-input-box>
<!-- Project description -->
<che-input-box che-form="projectMetadataController.projectForm"
che-name="projectDescription"
che-label-name="Description"
che-place-holder="Description of the project"
aria-label="Description of the project"
ng-model="projectMetadataController.template.description"
ng-model-options="{allowInvalid: true}"
che-on-change="projectMetadataController.onDescriptionChanged($value)"
ng-maxlength="256">
<div ng-message="maxlength">The name has to be less than 128 characters long.</div>
<div ng-message="md-maxlength">The name has to be less than 128 characters long.</div>
</che-input-box>
<!-- Git URL -->
<div ng-if="projectMetadataController.template.source.type==='git'">
<che-input-box che-form="projectMetadataController.projectForm"

View File

@ -47,9 +47,8 @@ export class ProjectMetadataService implements IEditingProgress {
}
const sameName = this.origProjectTemplate.name.trim() === this.projectTemplate.name.trim(),
sameDescription = this.origProjectTemplate.description.trim() === this.projectTemplate.description.trim(),
sameSourceLocation = this.origProjectTemplate.source.location.trim() === this.projectTemplate.source.location.trim();
if (sameName && sameDescription && sameSourceLocation) {
if (sameName && sameSourceLocation) {
return null;
}
@ -74,7 +73,6 @@ export class ProjectMetadataService implements IEditingProgress {
* @return {che.IProjectTemplate}
*/
getProjectTemplate(): che.IProjectTemplate {
this.projectTemplate.displayName = this.projectTemplate.name;
this.projectTemplate.path = '/' + this.projectTemplate.name.replace(/[^\w-_]/g, '_');
return this.projectTemplate;

View File

@ -28,6 +28,9 @@ export class ProjectSourceSelectorController {
* Directive's scope.
*/
private $scope: IProjectSourceSelectorScope;
private devfileProjectsCopy: Array<any>;
private devfileProjects: Array<any>;
private devfile: che.IWorkspaceDevfile;
/**
* Project selector service.
*/
@ -68,8 +71,37 @@ export class ProjectSourceSelectorController {
constructor($scope: IProjectSourceSelectorScope, projectSourceSelectorService: ProjectSourceSelectorService) {
this.$scope = $scope;
this.projectSourceSelectorService = projectSourceSelectorService;
this.actionType = ActionType;
this.devfileProjectsCopy = null;
$scope.$watch(() => {
return this.devfile;
}, () => {
if (!this.devfile) {
return;
}
this.devfileProjects = angular.copy(this.devfile.projects);
if (this.devfileProjectsCopy && this.devfileProjectsCopy !== this.devfileProjects) {
this.devfileProjectsCopy.forEach((project: any) => {
this.projectSourceSelectorService.removeProjectTemplate(project.name);
});
this.projectTemplates = this.projectSourceSelectorService.getProjectTemplates();
this.updateData({buttonState: true, actionType: ActionType.ADD_PROJECT});
}
if (this.devfileProjects && this.devfileProjects.length > 0) {
this.devfileProjects.forEach((project: any) => {
this.projectSourceSelectorService.addProjectTemplate(project);
});
// update list of templates to redraw the projects section
this.projectTemplates = this.projectSourceSelectorService.getProjectTemplates();
this.updateData({buttonState: true, actionType: ActionType.EDIT_PROJECT, template: this.devfileProjects[0]});
}
this.devfileProjectsCopy = angular.copy(this.devfileProjects);
}, true);
$scope.$watch('$destroy', () => {
this.projectSourceSelectorService.clearTemplatesList();
@ -94,7 +126,6 @@ export class ProjectSourceSelectorController {
// update list of templates to redraw the projects section
this.projectTemplates = this.projectSourceSelectorService.getProjectTemplates();
this.updateData({buttonState: true, actionType: ActionType.EDIT_PROJECT, template: projectTemplate});
}
@ -106,7 +137,6 @@ export class ProjectSourceSelectorController {
// update list of templates to redraw the projects section
this.projectTemplates = this.projectSourceSelectorService.getProjectTemplates();
this.updateData({buttonState: true, actionType: ActionType.ADD_PROJECT});
}
@ -149,7 +179,7 @@ export class ProjectSourceSelectorController {
this.activeActionType = actionType;
this.selectedProjectTemplate = angular.copy(template);
this.$scope.updateWidget(this.activeButtonId, this.scrollToBottom);
this.scrollToBottom = false;
}

View File

@ -34,7 +34,9 @@ export class ProjectSourceSelector implements ng.IDirective {
bindToController: boolean = true;
scope = {};
scope = {
devfile: '='
};
private $timeout: ng.ITimeoutService;
@ -47,43 +49,56 @@ export class ProjectSourceSelector implements ng.IDirective {
link($scope: IProjectSourceSelectorScope, $element: ng.IAugmentedJQuery): void {
$scope.updateWidget = (activeButtonId: string, scrollToBottom: boolean) => {
this.$timeout(() => {
const popover = $element.find('.project-source-selector-popover'),
arrow = popover.find('.arrow'),
selectButton = $element.find(`#${activeButtonId} button`);
if (!selectButton || !selectButton.length) {
popover.removeAttr('style');
arrow.removeAttr('style');
return;
}
const widgetHeight = $element.height();
const top = selectButton.position().top + (selectButton.height() / 2);
const popoverHeight = popover.height();
if (popoverHeight < top) {
if ((top + popoverHeight / 2) < widgetHeight) {
popover.attr('style', `top: ${top - (popoverHeight / 2 + 8)}px;`);
arrow.attr('style', 'top: 50%;');
} else {
popover.attr('style', `top: ${top - popoverHeight}px;`);
arrow.attr('style', `top: ${popoverHeight}px;`);
}
} else {
popover.attr('style', 'top: 0px;');
arrow.attr('style', `top: ${top}px;`);
}
if (scrollToBottom === false) {
return;
}
// scroll to bottom of the page
// to make 'Create' button visible
const mdContent = $element.closest('md-content'),
mdContentHeight = mdContent.height();
mdContent.scrollTop(mdContentHeight);
});
let timeoutPeriod = 0;
this.updateWidget($element, activeButtonId, scrollToBottom, timeoutPeriod);
};
}
private updateWidget($element: ng.IAugmentedJQuery, activeButtonId: string, scrollToBottom: boolean, timeoutPeriod: number): void {
this.$timeout(() => {
const popover = $element.find('.project-source-selector-popover'),
arrow = popover.find('.arrow'),
selectButton = $element.find(`#${activeButtonId} button`);
if (!selectButton || !selectButton.length) {
popover.removeAttr('style');
arrow.removeAttr('style');
return;
}
const widgetHeight = $element.height();
const top = selectButton.position().top + (selectButton.height() / 2);
const popoverHeight = popover.height();
// With popover height lower than zero - wait to be drawn:
if (popoverHeight <= 0) {
timeoutPeriod = 500;
this.updateWidget($element, activeButtonId, scrollToBottom, timeoutPeriod);
return;
}
if (popoverHeight < top) {
if ((top + popoverHeight / 2) < widgetHeight) {
popover.attr('style', `top: ${top - (popoverHeight / 2 + 8)}px;`);
arrow.attr('style', 'top: 50%;');
} else {
popover.attr('style', `top: ${top - popoverHeight}px;`);
arrow.attr('style', `top: ${popoverHeight}px;`);
}
} else {
popover.attr('style', 'top: 0px;');
arrow.attr('style', `top: ${top}px;`);
}
if (scrollToBottom === false) {
return;
}
// scroll to bottom of the page
// to make 'Create' button visible
const mdContent = $element.closest('md-content'),
mdContentHeight = mdContent.height();
mdContent.scrollTop(mdContentHeight);
}, timeoutPeriod);
}
}

View File

@ -18,7 +18,6 @@
che-state="projectSourceSelectorController.buttonState[projectTemplate.name]"
uib-tooltip="{{projectTemplate.name}}"></toggle-single-button>
</div>
<!-- Popover content -->
<div class="project-source-selector-popover popover right"
ng-show="projectSourceSelectorController.activeActionType">
@ -26,7 +25,9 @@
<div ng-if="projectSourceSelectorController.activeActionType === projectSourceSelectorController.actionType.ADD_PROJECT">
<add-import-project is-project-name-unique="projectSourceSelectorController.isProjectNameUnique(name)"
project-on-add="projectSourceSelectorController.projectTemplateOnAdd(templates)"></add-import-project>
project-on-add="projectSourceSelectorController.projectTemplateOnAdd(templates)"
devfile="projectSourceSelectorController.devfile">
</add-import-project>
</div>
<div ng-if="projectSourceSelectorController.activeActionType === projectSourceSelectorController.actionType.EDIT_PROJECT">

View File

@ -89,17 +89,12 @@ export class ProjectSourceSelectorService {
const origName = projectTemplate.name;
if (this.isProjectTemplateNameUnique(origName) === false) {
// update name, displayName and path
// update name and path
const newName = this.getUniqueName(origName);
projectTemplate.name = newName;
projectTemplate.displayName = newName;
projectTemplate.path = '/' + newName.replace(/[^\w-_]/g, '_');
}
if (!projectTemplate.type && projectTemplate.projectType) {
projectTemplate.type = projectTemplate.projectType;
}
this.projectTemplates.push(projectTemplate);
}

View File

@ -108,7 +108,6 @@ export class WorkspaceConfigService {
namespaceIdDefer.promise,
workspacesDefer.promise,
this.stackSelectorSvc.getOrFetchStacks(),
this.templateSelectorSvc.getOrFetchTemplates(),
githubRepositoriesPromise
]);
}

View File

@ -39,7 +39,7 @@
aria-label="project-item"
class="che-list-item-name">
<span class="che-xs-header noselect" hide-gt-xs>Description</span>
<span class="che-hover project-item-description">{{projectItemCtrl.project.description}}</span>
<span class="che-hover project-item-description">{{projectItemCtrl.project.source.location}}</span>
</div>
<div flex-gt-xs="20" layout="column" layout-align="center end">
<span class="che-xs-header noselect" hide-gt-xs>Actions</span>

View File

@ -12,7 +12,6 @@
'use strict';
import {ConfirmDialogService} from '../../../../components/service/confirm-dialog/confirm-dialog.service';
import {CheAPI} from '../../../../components/api/che-api.factory';
import {StackSelectorSvc} from '../../create-workspace/stack-selector/stack-selector.service';
import {RandomSvc} from '../../../../components/utils/random.service';
import {WorkspaceDetailsProjectsService} from './workspace-details-projects.service';
import {WorkspaceDetailsService} from '../workspace-details.service';
@ -30,7 +29,7 @@ import {WorkspaceDataManager} from '../../../../components/api/workspace/workspa
*/
export class WorkspaceDetailsProjectsCtrl {
static $inject = ['cheAPI', '$mdDialog', 'confirmDialogService', '$scope', 'cheListHelperFactory', 'stackSelectorSvc', 'randomSvc', 'createWorkspaceSvc', 'workspaceDetailsService', 'workspaceDetailsProjectsService'];
static $inject = ['cheAPI', '$mdDialog', 'confirmDialogService', '$scope', 'cheListHelperFactory', 'randomSvc', 'createWorkspaceSvc', 'workspaceDetailsService', 'workspaceDetailsProjectsService'];
/**
* Material design Dialog service.
@ -44,10 +43,6 @@ export class WorkspaceDetailsProjectsCtrl {
* List helper.
*/
private cheListHelper: che.widget.ICheListHelper;
/**
* Stack selector service.
*/
private stackSelectorSvc: StackSelectorSvc;
/**
* Generator for random strings.
*/
@ -86,14 +81,12 @@ export class WorkspaceDetailsProjectsCtrl {
confirmDialogService: ConfirmDialogService,
$scope: ng.IScope,
cheListHelperFactory: che.widget.ICheListHelperFactory,
stackSelectorSvc: StackSelectorSvc,
randomSvc: RandomSvc,
createWorkspaceSvc: CreateWorkspaceSvc,
workspaceDetailsService: WorkspaceDetailsService,
workspaceDetailsProjectsService: WorkspaceDetailsProjectsService) {
this.$mdDialog = $mdDialog;
this.confirmDialogService = confirmDialogService;
this.stackSelectorSvc = stackSelectorSvc;
this.randomSvc = randomSvc;
this.workspaceDetailsProjectsService = workspaceDetailsProjectsService;
this.createWorkspaceSvc = createWorkspaceSvc;
@ -151,7 +144,6 @@ export class WorkspaceDetailsProjectsCtrl {
return;
}
this.workspaceDetails = workspaceDetails;
this.stackSelectorSvc.setStackId(this.workspaceDetails.attributes.stackId);
this.projects = this.workspaceDataManager.getProjects(this.workspaceDetails);
this.cheListHelper.setList(this.projects, 'name');
}
@ -183,7 +175,6 @@ export class WorkspaceDetailsProjectsCtrl {
// update name, displayName and path
const newName = this.getUniqueName(origName);
projectTemplate.name = newName;
projectTemplate.displayName = newName;
projectTemplate.path = '/' + newName.replace(/[^\w-_]/g, '_');
}

View File

@ -65,14 +65,6 @@ export class CheAPIBuilder {
return new CheProjectDetailsBuilder();
}
/***
* The Che Project Template builder
* @returns {CheProjectTemplateBuilder}
*/
getProjectTemplateBuilder() {
return new CheProjectTemplateBuilder();
}
/***
* The Che Project Type builder
* @returns {CheProjectTypeBuilder}

View File

@ -13,7 +13,6 @@
import {CheAPI} from './che-api.factory';
import {CheWorkspace} from './workspace/che-workspace.factory';
import {CheProjectTemplate} from './che-project-template.factory';
import {CheFactory} from './che-factory.factory';
import {CheStack} from './che-stack.factory';
import {CheWebsocket} from './che-websocket.factory';
@ -49,7 +48,6 @@ export class ApiConfig {
constructor(register: che.IRegisterService) {
register.factory('cheWorkspace', CheWorkspace);
register.factory('cheProjectTemplate', CheProjectTemplate);
register.factory('cheFactory', CheFactory);
register.factory('cheProfile', CheProfile);
register.factory('chePreferences', ChePreferences);

View File

@ -16,7 +16,6 @@ import {CheProfile} from './che-profile.factory';
import {CheFactory} from './che-factory.factory';
import {CheFactoryTemplate} from './che-factory-template.factory';
import {ChePreferences} from './che-preferences.factory';
import {CheProjectTemplate} from './che-project-template.factory';
import {CheService} from './che-service.factory';
import {CheStack} from './che-stack.factory';
import {CheOAuthProvider} from './che-o-auth-provider.factory';
@ -32,14 +31,12 @@ import {CheUser} from './che-user.factory';
export class CheAPI {
static $inject = ['cheWorkspace', 'cheFactory', 'cheFactoryTemplate',
'cheProfile', 'chePreferences', 'cheProjectTemplate',
'cheService', 'cheStack', 'cheOAuthProvider', 'cheAgent',
'cheProfile', 'chePreferences', 'cheService', 'cheStack', 'cheOAuthProvider', 'cheAgent',
'cheSsh', 'cheUser', 'chePermissions', 'cheOrganization'];
private cheWorkspace: CheWorkspace;
private cheProfile: CheProfile;
private chePreferences: ChePreferences;
private cheProjectTemplate: CheProjectTemplate;
private cheFactory: CheFactory;
private cheFactoryTemplate: CheFactoryTemplate;
private cheService: CheService;
@ -55,15 +52,13 @@ export class CheAPI {
* Default constructor that is using resource
*/
constructor(cheWorkspace: CheWorkspace, cheFactory: CheFactory, cheFactoryTemplate: CheFactoryTemplate,
cheProfile: CheProfile, chePreferences: ChePreferences, cheProjectTemplate: CheProjectTemplate,
cheService: CheService, cheStack: CheStack, cheOAuthProvider: CheOAuthProvider, cheAgent: CheAgent,
cheProfile: CheProfile, chePreferences: ChePreferences, cheService: CheService, cheStack: CheStack, cheOAuthProvider: CheOAuthProvider, cheAgent: CheAgent,
cheSsh: CheSsh, cheUser: CheUser, chePermissions: che.api.IChePermissions, cheOrganization: che.api.ICheOrganization) {
this.cheWorkspace = cheWorkspace;
this.cheProfile = cheProfile;
this.cheFactory = cheFactory;
this.cheFactoryTemplate = cheFactoryTemplate;
this.chePreferences = chePreferences;
this.cheProjectTemplate = cheProjectTemplate;
this.cheService = cheService;
this.cheStack = cheStack;
this.cheOAuthProvider = cheOAuthProvider;
@ -106,14 +101,6 @@ export class CheAPI {
return this.chePreferences;
}
/**
* The Che Project Template API
* @returns {CheProjectTemplate}
*/
getProjectTemplate(): CheProjectTemplate {
return this.cheProjectTemplate;
}
/**
* The Che Services API
* @returns {CheService}

View File

@ -1,108 +0,0 @@
/*
* Copyright (c) 2015-2018 Red Hat, Inc.
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Red Hat, Inc. - initial API and implementation
*/
'use strict';
/**
* This class is handling the projects template retrieval
* It sets to the array project templates
* @author Florent Benoit
*/
export class CheProjectTemplate {
static $inject = ['$resource'];
$resource: ng.resource.IResourceService;
templatesPerCategory: {
[category: string]: Array<che.IProjectTemplate>;
};
templates: Array<che.IProjectTemplate>;
remoteProjectTemplateAPI: ng.resource.IResourceClass<any>;
/**
* Default constructor that is using resource
*/
constructor ($resource: ng.resource.IResourceService) {
// keep resource
this.$resource = $resource;
// types per category
this.templatesPerCategory = {};
// project templates
this.templates = [];
// remote call
this.remoteProjectTemplateAPI = <ng.resource.IResourceClass<any>> this.$resource('/api/project-template/all');
}
/**
* Fetch the project templates
*
* @return {IPromise<any>}
*/
fetchTemplates(): ng.IPromise<any> {
const promise = this.remoteProjectTemplateAPI.query().$promise;
const updatedPromise = promise.then((projectTemplates: Array<che.IProjectTemplate>) => {
// reset global list
this.templates.length = 0;
for (const member in this.templatesPerCategory) {
if (this.templatesPerCategory.hasOwnProperty(member)) {
delete this.templatesPerCategory[member];
}
}
projectTemplates.forEach((projectTemplate: che.IProjectTemplate) => {
// get attributes
const category = projectTemplate.category;
// get list
if (!this.templatesPerCategory[category]) {
this.templatesPerCategory[category] = [];
}
// add element on the list
this.templatesPerCategory[category].push(projectTemplate);
this.templates.push(projectTemplate);
});
});
return updatedPromise;
}
/**
* Gets all project templates
*
* @return {Array<che.IProjectTemplate>}
*/
getAllProjectTemplates(): Array<che.IProjectTemplate> {
return this.templates;
}
/**
* The templates per category
*
* @return {{[p: string]: Array<che.IProjectTemplate>}}
*/
getTemplatesByCategory(): {[category: string]: Array<che.IProjectTemplate>} {
return this.templatesPerCategory;
}
}

View File

@ -1,161 +0,0 @@
/*
* Copyright (c) 2015-2018 Red Hat, Inc.
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Red Hat, Inc. - initial API and implementation
*/
'use strict';
import {CheProjectTemplate} from './che-project-template.factory';
import {CheAPIBuilder} from './builder/che-api-builder.factory';
import {CheHttpBackend} from './test/che-http-backend';
/**
* Test of the CheProjectTemplate
*/
describe('CheProjectTemplate', function(){
/**
* Project Template Factory for the test
*/
let factory;
/**
* API builder.
*/
let apiBuilder;
/**
* Backend for handling http operations
*/
let httpBackend;
/**
* Che backend
*/
let cheBackend;
/**
* setup module
*/
beforeEach(angular.mock.module('userDashboard'));
/**
* Inject factory and http backend
*/
beforeEach(inject(function(cheProjectTemplate: CheProjectTemplate,
cheAPIBuilder: CheAPIBuilder,
cheHttpBackend: CheHttpBackend) {
factory = cheProjectTemplate;
apiBuilder = cheAPIBuilder;
cheBackend = cheHttpBackend;
httpBackend = cheHttpBackend.getHttpBackend();
}));
/**
* Check assertion after the test
*/
afterEach(function () {
httpBackend.verifyNoOutstandingExpectation();
httpBackend.verifyNoOutstandingRequest();
});
/**
* Check that we're able to fetch project types
*/
it('Fetch project types', function() {
// setup tests objects
var templateHelloWorldCli = apiBuilder.getProjectTemplateBuilder()
.withProjectType('maven')
.withCategory('Samples - Hello World')
.withSourceLocation('https://github.com/eclipse/dummy-che-templates/desktop-console-java.git')
.withSourceType('git')
.withSourceParameters({branch: '3.1.0', keepVcs: 'false'})
.withDescription('A simple JAR project based on Maven.')
.withDisplayname('Maven Java Console')
.build();
var templateSwing = apiBuilder.getProjectTemplateBuilder()
.withProjectType('maven')
.withCategory('Samples - Hello World')
.withSourceLocation('https://github.com/eclipse/dummy-che-templates/desktop-swing-java-basic.git')
.withSourceType('git')
.withSourceParameters({branch: '3.1.0', keepVcs: 'false'})
.withDescription('A simple Java Swing application.')
.withDisplayname('Swing')
.build();
var templateUD = apiBuilder.getProjectTemplateBuilder()
.withProjectType('AngularJS')
.withCategory('Samples - Che')
.withSourceLocation('https://github.com/eclipse/che-dashboard.git')
.withSourceType('git')
.withSourceParameters({branch: '3.1.0', keepVcs: 'false'})
.withDescription('Che Dashboard example..')
.withDisplayname('Che Dashboard')
.build();
// providing request
// add workspaces on Http backend
cheBackend.addProjectTemplates([templateHelloWorldCli, templateSwing, templateUD]);
// setup backend
cheBackend.setup();
// no templates now on factory
expect(factory.getAllProjectTemplates().length).toEqual(0);
// fetch types
factory.fetchTemplates();
// expecting a GET
httpBackend.expectGET('/api/project-template/all');
// flush command
httpBackend.flush();
// now, check types
var projectTemplates = factory.getAllProjectTemplates();
var templatesByCategory = factory.getTemplatesByCategory();
// check we have 3 types
expect(projectTemplates.length).toEqual(3);
// check category hello world
var helloWorldCategory = templatesByCategory['Samples - Hello World'];
expect(helloWorldCategory).not.toBeNull();
expect(helloWorldCategory.length).toEqual(2);
var firstType = helloWorldCategory[0];
expect(firstType.projectType).toEqual(templateHelloWorldCli.projectType);
expect(firstType.displayName).toEqual(templateHelloWorldCli.displayName);
var secondType = helloWorldCategory[1];
expect(secondType.projectType).toEqual(templateSwing.projectType);
expect(secondType.displayName).toEqual(templateSwing.displayName);
// check category samples
var sampleCategory = templatesByCategory['Samples - Che'];
expect(sampleCategory).not.toBeNull();
expect(sampleCategory.length).toEqual(1);
var anotherType = sampleCategory[0];
expect(anotherType.projectType).toEqual(templateUD.projectType);
expect(anotherType.displayName).toEqual(templateUD.displayName);
}
);
});

View File

@ -17,6 +17,7 @@ export interface IDevfileMetaData {
globalMemoryLimit: string;
icon: string;
links: any;
tags: Array<string>;
}