[ADF-3540] Start Task Cloud by cli (#3954)

* [ADF-3538] start creating new folder for cloud components

* [ADF-3538] added new package to the script and the builds

* [ADF-3538] added some more changes to scripts

* [ADF-3538] - starting the new package

* Add a cloud component as example

* Skip the scss style

* remove useless codes

* Add i18n example

* remove useless code

* Simplify the hello component
Fix the wrong path

* add the app-list-cloud-component
add the app-details-cloud-component

* Fix process service cloud path

* Remove useless file

* [ADF-3540] Start Task Component APS2

* Generated startTaskCloudModule
* Generated start-task-cloud  component by cli
* Used  start-task-cloud in the demo shell

* * Added FormModule instead of  CoreModule
* Added StartTaskTestingModule
* Refactored startTaskCloud Component

* * Used FlexLayout
* Used Keylock api to fetch users
* Removed all css
* Refactored start-task-cloud component
* Removed form field from the starttask form
* Created UsedCloud model
* Added unit test to the recent changes
* Added mock objects

* * Added  unit test to the startTaskCloud component

* * Added documentation about startTaskCLoud component
* Changed runtimeBundle to appName
* Changed defaultTaskName to name

* * Generated people-cloud component
* Created initialUserName pipe

* * Fetching roles by userId
* Filtering Users with required roles
* Removed duplicates
* Generated initial-user-name pipe
* Generated people-cloud component
* Created roleCloud module

* * Rebased with development branch
* Created start-task-cloud-demo component in the demo-shell

* * Added unit tests to the start-task-cloud service
* Added unit test to the people-cloud component and userInitail pipe
* Updated start-task unit tests to the recent changes
* Created mock data

* * Updated people-cloud component with  error message* Updated unit tests

* * Included StartTaskCloud component in the demoShell* Created startTask demo component* Added create task button in the tasklist demo component

* * Added lodash

* * Fixed lodash import

* Add the start task into the cloud demo

* Fix the lodash import and @type

* Show the My task once the task has been created

* first change

* [ADF-3540] Improved start task component

* [ADF-3540] Fixed expression changed error

* [ADF-3540] Refactored code

* [ADF-3540] Fix lodash import error

* [ADF-3540] Remove lodash dependency

* [ADF-3540] Refatored code

* tmp

* [ADF-3540] Show/Hide current user as part of list

* [ADF-3340] Assign new task to current user when no assignee selected

* [ADF-3540] Rebased the latest changes

* [ADF-3540] Refactored code

* [ADF-3540] Improved user search logic for people component

* [ADF-3540] Moved user services to Core module

* [ADF-3540] Modified translation keys

* Add process-services-cloud into the license rule

* Fix wrong import for prod build

* Add license header

* Fix unit tests

* Fix proxy karma for content

* Fix proxy karma for process services

* Fix proxy karma for process cloud
This commit is contained in:
Deepak Paul 2018-11-29 20:06:51 +05:30 committed by Eugenio Romano
parent 5bea17fa6c
commit 2f0f33643b
49 changed files with 1961 additions and 94 deletions

View File

@ -121,7 +121,8 @@ import { AppExtensionsModule } from './extensions/extensions.module';
ReportIssueComponent,
TaskListCloudDemoComponent,
ProcessListCloudExampleComponent,
TreeViewSampleComponent
TreeViewSampleComponent,
TaskListCloudDemoComponent
],
providers: [
{

View File

@ -141,11 +141,13 @@ export const appRoutes: Routes = [
children: [
{
path: '',
component: CloudComponent
component: CloudComponent,
canActivate: [AuthGuard]
},
{
path: ':applicationName/tasks',
component: TaskListCloudDemoComponent
component: TaskListCloudDemoComponent,
canActivate: [AuthGuard]
}
]
},

View File

@ -15,21 +15,18 @@
* limitations under the License.
*/
import { Component, OnInit } from '@angular/core';
import { Component } from '@angular/core';
import { Router } from '@angular/router';
@Component({
selector: 'app-cloud',
templateUrl: './cloud.component.html',
styleUrls: ['./cloud.component.scss']
})
export class CloudComponent implements OnInit {
export class CloudComponent {
constructor(private router: Router) {
}
ngOnInit() {
}
onAppClick(app) {
this.router.navigate([`/cloud/${app.name}/tasks/`]);
}

View File

@ -1,15 +1,31 @@
<h2>TASK LIST CLOUD DEMO</h2>
<adf-cloud-task-filters
[appName]="applicationName"
[showIcons]="true"
(filterClick)="onFilterSelected($event)">
</adf-cloud-task-filters>
<div fxLayout="row" fxLayout.xs="column">
<div fxFlexOrder="1" fxFlex="15%">
<adf-sidebar-action-menu [expanded]="true" [width]="205" title="{{'ADF_SIDEBAR_ACTION_MENU.BUTTON.CREATE' | translate}}">
<mat-icon sidebar-menu-title-icon>arrow_drop_down</mat-icon>
<div sidebar-menu-options>
<button mat-menu-item data-automation-id="btn-start-task" (click)="onStartTask()">
<mat-icon>assessment</mat-icon>
<span>{{'ADF_SIDEBAR_ACTION_MENU.BUTTON.NEW_TASK' | translate}}</span>
</button>
</div>
</adf-sidebar-action-menu>
</div>
</div>
<div *ngIf="showStartTask">
<adf-cloud-start-task [appName]="applicationName" (success)="onStartTaskSuccess()" (cancel)="onCancelStartTask()">
</adf-cloud-start-task>
</div>
<div *ngIf="!showStartTask">
<adf-cloud-task-filters [appName]="applicationName" [filterParam]="filterTaskParam" [showIcons]="true" (filterClick)="onFilterSelected($event)">
</adf-cloud-task-filters>
<mat-accordion>
<mat-expansion-panel>
<mat-expansion-panel-header>
<mat-panel-title>
{{filterName}}
{{filterName | translate}}
</mat-panel-title>
<mat-panel-description>
Customise your filter
@ -62,25 +78,21 @@
</mat-form-field>
</div>
</mat-expansion-panel>
</mat-accordion>
<div>
<adf-cloud-task-list *ngIf="isFilterLoaded" #taskCloud
[applicationName]="applicationName"
[status]="status"
[landingTaskId]="selectTask"
[sorting]="sortArray"
(rowClick)="onRowClick($event)">
<data-columns>
<data-column key="entry.id" title="Id"></data-column>
<data-column key="entry.name" title="Name"></data-column>
<data-column key="entry.status" title="Status"></data-column>
<data-column key="entry.processDefinitionId" title="Process Definition Id"></data-column>
<data-column key="entry.priority" title="Priority"></data-column>
<data-column key="entry.createdDate" title="Created Date"></data-column>
</data-columns>
</adf-cloud-task-list>
<adf-pagination [target]="taskCloud"
(changePageSize)="onChangePageSize($event)">
</adf-pagination>
</mat-expansion-panel>
</mat-accordion>
<div *ngIf="isFilterLoaded">
<adf-cloud-task-list #taskCloud [applicationName]="applicationName" [status]="status"
[landingTaskId]="selectTask" [sorting]="sortArray" (rowClick)="onRowClick($event)">
<data-columns>
<data-column key="entry.id" title="Id"></data-column>
<data-column key="entry.name" title="Name" class="full-width name-column ellipsis-cell"></data-column>
<data-column key="entry.status" title="Status"></data-column>
<data-column key="entry.processDefinitionId" title="Process Definition Id"></data-column>
<data-column key="entry.priority" title="Priority"></data-column>
<data-column key="entry.createdDate" type="date" title="Created Date" [format]="'timeAgo'"></data-column>
</data-columns>
</adf-cloud-task-list>
<adf-pagination [target]="taskCloud" (changePageSize)="onChangePageSize($event)">
</adf-pagination>
</div>
</div>

View File

@ -21,6 +21,7 @@ import { UserPreferencesService } from '@alfresco/adf-core';
import { Observable } from 'rxjs';
import { ActivatedRoute, Router } from '@angular/router';
import { FormControl } from '@angular/forms';
import { MatDialog } from '@angular/material';
@Component({
selector: 'app-task-list-cloud-demo',
@ -36,14 +37,16 @@ export class TaskListCloudDemoComponent implements OnInit {
sortDirectionFormControl: FormControl;
appDefinitionList: Observable<any>;
applicationName: string = '';
applicationName;
status: string = '';
sort: string = '';
isFilterLoaded = false;
showStartTask = false;
sortDirection: string = 'ASC';
filterName: string;
clickedRow: string = '';
selectTask: string = '';
filterTaskParam;
sortArray: TaskListCloudSortingModel [];
columns = [
@ -55,13 +58,13 @@ export class TaskListCloudDemoComponent implements OnInit {
];
constructor(
public dialog: MatDialog,
private route: ActivatedRoute,
private router: Router,
private userPreference: UserPreferencesService) {
}
ngOnInit() {
this.isFilterLoaded = false;
this.route.params.subscribe(params => {
this.applicationName = params.applicationName;
});
@ -117,6 +120,19 @@ export class TaskListCloudDemoComponent implements OnInit {
this.router.navigate([`/cloud/${this.applicationName}/tasks/`], {queryParams: queryParams});
}
onStartTask() {
this.showStartTask = true;
}
onStartTaskSuccess() {
this.showStartTask = false;
this.filterTaskParam = { name: 'My tasks'};
}
onCancelStartTask() {
this.showStartTask = false;
}
onChangePageSize(event) {
this.userPreference.paginationSize = event.maxItems;
}
@ -124,5 +140,4 @@ export class TaskListCloudDemoComponent implements OnInit {
onRowClick($event) {
this.clickedRow = $event;
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

View File

@ -0,0 +1,37 @@
---
Added: v3.0.0
Status: Active
Last reviewed: 2018-23-10
---
# Start Task Cloud Component
Creates/Starts new task for the specified app
![adf-cloud-start-task](../docassets/images/adf-cloud-start-task.png)
## Basic Usage
```html
<adf-cloud-start-task
[appName]="YOUR_APP_NAME">
</adf-cloud-start-task>
```
## Class members
### Properties
| Name | Type | Default value | Description |
| ---- | ---- | ------------- | ----------- |
| appName | `string` | | (required) The name of the app. |
| name | `string` | | Name of the task. |
| maxNameLength | `number` | 255 | Maximum length of the task name. |
### Events
| Name | Type | Description |
| ---- | ---- | ----------- |
| cancel | [`EventEmitter`](https://angular.io/api/core/EventEmitter)`<void>` | Emitted when the cancel button is clicked by the user. |
| error | [`EventEmitter`](https://angular.io/api/core/EventEmitter)`<any>` | Emitted when an error occurs. |
| success | [`EventEmitter`](https://angular.io/api/core/EventEmitter)`<any>` | Emitted when the task is successfully created. |

View File

@ -3,32 +3,32 @@
module.exports = function (config) {
config.set({
basePath: '',
basePath: '../../',
files: [
{pattern: '../../node_modules/core-js/client/core.js', included: true, watched: false},
{pattern: '../../node_modules/tslib/tslib.js', included: true, watched: false},
{pattern: '../../node_modules/hammerjs/hammer.min.js', included: true, watched: false},
{pattern: '../../node_modules/hammerjs/hammer.min.js.map', included: false, watched: false},
{pattern: 'node_modules/core-js/client/core.js', included: true, watched: false},
{pattern: 'node_modules/tslib/tslib.js', included: true, watched: false},
{pattern: 'node_modules/hammerjs/hammer.min.js', included: true, watched: false},
{pattern: 'node_modules/hammerjs/hammer.min.js.map', included: false, watched: false},
// pdf-js
{pattern: '../../node_modules/pdfjs-dist/build/pdf.js', included: true, watched: false},
{pattern: '../../node_modules/pdfjs-dist/build/pdf.worker.js', included: true, watched: false},
{pattern: '../../node_modules/pdfjs-dist/web/pdf_viewer.js', included: true, watched: false},
{pattern: 'node_modules/pdfjs-dist/build/pdf.js', included: true, watched: false},
{pattern: 'node_modules/pdfjs-dist/build/pdf.worker.js', included: true, watched: false},
{pattern: 'node_modules/pdfjs-dist/web/pdf_viewer.js', included: true, watched: false},
{
pattern: '../../node_modules/@angular/material/prebuilt-themes/indigo-pink.css',
pattern: 'node_modules/@angular/material/prebuilt-themes/indigo-pink.css',
included: true,
watched: false
},
{pattern: '../../node_modules/alfresco-js-api/dist/alfresco-js-api.min.js', included: true, watched: false},
{pattern: '../../node_modules/moment/min/moment.min.js', included: true, watched: false},
{pattern: 'node_modules/alfresco-js-api/dist/alfresco-js-api.min.js', included: true, watched: false},
{pattern: 'node_modules/moment/min/moment.min.js', included: true, watched: false},
{pattern: '../content-services/i18n/**/en.json', included: false, served: true, watched: false},
{pattern: '../core/i18n/**/en.json', included: false, served: true, watched: false},
{pattern: '../content-services/**/*.ts', included: false, served: true, watched: false},
{pattern: '../config/app.config.json', included: false, served: true, watched: false},
{pattern: 'lib/content-services/i18n/**/en.json', included: false, served: true, watched: false},
{pattern: 'lib/core/i18n/**/en.json', included: false, served: true, watched: false},
{pattern: 'lib/content-services/**/*.ts', included: false, served: true, watched: false},
{pattern: 'lib/config/app.config.json', included: false, served: true, watched: false},
],
frameworks: ['jasmine-ajax', 'jasmine', '@angular-devkit/build-angular'],

View File

@ -0,0 +1,28 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export class IdentityRoleModel {
id: string;
name: string;
constructor(obj?: any) {
if (obj) {
this.id = obj.id || null;
this.name = obj.name || null;
}
}
}

View File

@ -16,7 +16,6 @@
*/
export class IdentityUserModel {
id: string;
firstName: string;
lastName: string;

View File

@ -22,5 +22,6 @@ export * from './services/identity-user.service';
export * from './models/bpm-user.model';
export * from './models/ecm-user.model';
export * from './models/identity-user.model';
export * from './models/identity-role.model';
export * from './userinfo.module';

View File

@ -16,13 +16,31 @@
*/
import { TestBed } from '@angular/core/testing';
import { HttpErrorResponse } from '@angular/common/http';
import { throwError, of } from 'rxjs';
import { IdentityUserService } from '../services/identity-user.service';
import { setupTestBed } from '../../testing/setupTestBed';
import { CoreModule } from '../../core.module';
import { mockToken } from './../../mock/jwt-helper.service.spec';
import { IdentityUserModel } from '../models/identity-user.model';
import { IdentityRoleModel } from '../models/identity-role.model';
describe('IdentityUserService', () => {
const mockUsers = [
{ id: 'fake-id-1', username: 'first-name-1 last-name-1', firstName: 'first-name-1', lastName: 'last-name-1', email: 'abc@xyz.com' },
{ id: 'fake-id-2', username: 'first-name-2 last-name-2', firstName: 'first-name-2', lastName: 'last-name-2', email: 'abcd@xyz.com'},
{ id: 'fake-id-3', username: 'first-name-3 last-name-3', firstName: 'first-name-3', lastName: 'last-name-3', email: 'abcde@xyz.com' }
];
const mockRoles = [
{ id: 'id-1', name: 'MOCK-ADMIN-ROLE'},
{ id: 'id-2', name: 'MOCK-USER-ROLE'},
{ id: 'id-3', name: 'MOCK_MODELER-ROLE' },
{ id: 'id-4', name: 'MOCK-ROLE-1' },
{ id: 'id-5', name: 'MOCK-ROLE-2'}
];
let service: IdentityUserService;
setupTestBed({
@ -46,7 +64,7 @@ describe('IdentityUserService', () => {
});
});
it('should able to fetch identity user info from Jwt token', (done) => {
it('should fetch identity user info from Jwt token', (done) => {
localStorage.setItem('access_token', mockToken);
service.getCurrentUserInfo().subscribe(
(user) => {
@ -58,4 +76,149 @@ describe('IdentityUserService', () => {
done();
});
});
it('should fetch users ', (done) => {
spyOn(service, 'getUsers').and.returnValue(of(mockUsers));
service.getUsers().subscribe(
(res: IdentityUserModel[]) => {
expect(res).toBeDefined();
expect(res[0].id).toEqual('fake-id-1');
expect(res[0].username).toEqual('first-name-1 last-name-1');
expect(res[1].id).toEqual('fake-id-2');
expect(res[1].username).toEqual('first-name-2 last-name-2');
expect(res[2].id).toEqual('fake-id-3');
expect(res[2].username).toEqual('first-name-3 last-name-3');
done();
}
);
});
it('Should not fetch users if error occurred', (done) => {
const errorResponse = new HttpErrorResponse({
error: 'Mock Error',
status: 404, statusText: 'Not Found'
});
spyOn(service, 'getUsers').and.returnValue(throwError(errorResponse));
service.getUsers()
.subscribe(
() => {
fail('expected an error, not users');
},
(error) => {
expect(error.status).toEqual(404);
expect(error.statusText).toEqual('Not Found');
expect(error.error).toEqual('Mock Error');
done();
}
);
});
it('should fetch roles by userId', (done) => {
spyOn(service, 'getUserRoles').and.returnValue(of(mockRoles));
service.getUserRoles('mock-user-id').subscribe(
(res: IdentityRoleModel[]) => {
expect(res).toBeDefined();
expect(res[0].name).toEqual('MOCK-ADMIN-ROLE');
expect(res[1].name).toEqual('MOCK-USER-ROLE');
expect(res[4].name).toEqual('MOCK-ROLE-2');
done();
}
);
});
it('Should not fetch roles if error occurred', (done) => {
const errorResponse = new HttpErrorResponse({
error: 'Mock Error',
status: 404, statusText: 'Not Found'
});
spyOn(service, 'getUserRoles').and.returnValue(throwError(errorResponse));
service.getUserRoles('mock-user-id')
.subscribe(
() => {
fail('expected an error, not users');
},
(error) => {
expect(error.status).toEqual(404);
expect(error.statusText).toEqual('Not Found');
expect(error.error).toEqual('Mock Error');
done();
}
);
});
it('should fetch users by roles', (done) => {
spyOn(service, 'getUsers').and.returnValue(of(mockUsers));
spyOn(service, 'getUserRoles').and.returnValue(of(mockRoles));
service.getUsersByRolesWithCurrentUser([mockRoles[0].name]).then(
(res: IdentityUserModel[]) => {
expect(res).toBeDefined();
expect(res[0].id).toEqual('fake-id-1');
expect(res[0].username).toEqual('first-name-1 last-name-1');
expect(res[1].id).toEqual('fake-id-2');
expect(res[1].username).toEqual('first-name-2 last-name-2');
expect(res[2].id).toEqual('fake-id-3');
expect(res[2].username).toEqual('first-name-3 last-name-3');
done();
}
);
});
it('Should not fetch users by roles if error occurred', (done) => {
const errorResponse = new HttpErrorResponse({
error: 'Mock Error',
status: 404, statusText: 'Not Found'
});
spyOn(service, 'getUsers').and.returnValue(throwError(errorResponse));
service.getUsersByRolesWithCurrentUser([mockRoles[0].name])
.catch(
(error) => {
expect(error.status).toEqual(404);
expect(error.statusText).toEqual('Not Found');
expect(error.error).toEqual('Mock Error');
done();
}
);
});
it('should fetch users by roles without current user', (done) => {
spyOn(service, 'getUsers').and.returnValue(of(mockUsers));
spyOn(service, 'getUserRoles').and.returnValue(of(mockRoles));
spyOn(service, 'getCurrentUserInfo').and.returnValue(of(mockUsers[0]));
service.getUsersByRolesWithoutCurrentUser([mockRoles[0].name]).then(
(res: IdentityUserModel[]) => {
expect(res).toBeDefined();
expect(res[0].id).toEqual('fake-id-2');
expect(res[0].username).toEqual('first-name-2 last-name-2');
expect(res[1].id).toEqual('fake-id-3');
expect(res[1].username).toEqual('first-name-3 last-name-3');
done();
}
);
});
it('Should not fetch users by roles without current user if error occurred', (done) => {
const errorResponse = new HttpErrorResponse({
error: 'Mock Error',
status: 404, statusText: 'Not Found'
});
spyOn(service, 'getCurrentUserInfo').and.returnValue(throwError(errorResponse));
service.getUsersByRolesWithoutCurrentUser([mockRoles[0].name])
.catch(
(error) => {
expect(error.status).toEqual(404);
expect(error.statusText).toEqual('Not Found');
expect(error.error).toEqual('Mock Error');
done();
}
);
});
});

View File

@ -16,9 +16,14 @@
*/
import { Injectable } from '@angular/core';
import { of, Observable } from 'rxjs';
import { of, Observable, from } from 'rxjs';
import { map } from 'rxjs/operators';
import { IdentityUserModel } from '../models/identity-user.model';
import { JwtHelperService } from './../../services/jwt-helper.service';
import { JwtHelperService } from '../../services/jwt-helper.service';
import { AppConfigService } from '../../app-config/app-config.service';
import { AlfrescoApiService } from '../../services/alfresco-api.service';
import { IdentityRoleModel } from '../models/identity-role.model';
@Injectable({
providedIn: 'root'
@ -30,7 +35,10 @@ export class IdentityUserService {
static USER_ACCESS_TOKEN = 'access_token';
static USER_PREFERRED_USERNAME = 'preferred_username';
constructor(private helper: JwtHelperService) {}
constructor(
private helper: JwtHelperService,
private apiService: AlfrescoApiService,
private appConfigService: AppConfigService) {}
getCurrentUserInfo(): Observable<IdentityUserModel> {
const fullName = this.getValueFromToken<string>(IdentityUserService.USER_NAME);
@ -50,4 +58,93 @@ export class IdentityUserService {
}
return <T> value;
}
getUsers(): Observable<IdentityUserModel[]> {
const url = this.buildUserUrl();
const httpMethod = 'GET', pathParams = {}, queryParams = {}, bodyParam = {}, headerParams = {},
formParams = {}, authNames = [], contentTypes = ['application/json'], accepts = ['application/json'];
return from(this.apiService.getInstance().oauth2Auth.callCustomApi(
url, httpMethod, pathParams, queryParams,
headerParams, formParams, bodyParam, authNames,
contentTypes, accepts, Object, null, null)
).pipe(
map((response: IdentityUserModel[]) => {
return response;
})
);
}
getUserRoles(userId: string): Observable<IdentityRoleModel[]> {
const url = this.buildRolesUrl(userId);
const httpMethod = 'GET', pathParams = {}, queryParams = {}, bodyParam = {}, headerParams = {},
formParams = {}, authNames = [], contentTypes = ['application/json'], accepts = ['application/json'];
return from(this.apiService.getInstance().oauth2Auth.callCustomApi(
url, httpMethod, pathParams, queryParams,
headerParams, formParams, bodyParam, authNames,
contentTypes, accepts, Object, null, null)
).pipe(
map((response: IdentityRoleModel[]) => {
return response;
})
);
}
async getUsersByRolesWithCurrentUser(roleNames: string[]): Promise<IdentityUserModel[]> {
const filteredUsers: IdentityUserModel[] = [];
if (roleNames && roleNames.length > 0) {
const users = await this.getUsers().toPromise();
for (let i = 0; i < users.length; i++) {
const hasAnyRole = await this.userHasAnyRole(users[i].id, roleNames);
if (hasAnyRole) {
filteredUsers.push(users[i]);
}
}
}
return filteredUsers;
}
async getUsersByRolesWithoutCurrentUser(roleNames: string[]): Promise<IdentityUserModel[]> {
const filteredUsers: IdentityUserModel[] = [];
if (roleNames && roleNames.length > 0) {
const currentUser = await this.getCurrentUserInfo().toPromise();
let users = await this.getUsers().toPromise();
users = users.filter((user) => { return user.username !== currentUser.username; });
for (let i = 0; i < users.length; i++) {
const hasAnyRole = await this.userHasAnyRole(users[i].id, roleNames);
if (hasAnyRole) {
filteredUsers.push(users[i]);
}
}
}
return filteredUsers;
}
private async userHasAnyRole(userId: string, roleNames: string[]): Promise<boolean> {
const userRoles = await this.getUserRoles(userId).toPromise();
const hasAnyRole = roleNames.some((roleName) => {
const filteredRoles = userRoles.filter((userRole) => {
return userRole.name.toLocaleLowerCase() === roleName.toLocaleLowerCase();
});
return filteredRoles.length > 0;
});
return hasAnyRole;
}
private buildUserUrl(): any {
return `${this.appConfigService.get('bpmHost')}/auth/admin/realms/springboot/users`;
}
private buildRolesUrl(userId: string): any {
return `${this.appConfigService.get('bpmHost')}/auth/admin/realms/springboot/users/${userId}/role-mappings/realm/composite`;
}
}

View File

@ -3,8 +3,17 @@
module.exports = function (config) {
config.set({
basePath: '',
basePath: '../../',
frameworks: ['jasmine', '@angular-devkit/build-angular'],
files: [
{pattern: 'lib/core/i18n/**/en.json', included: false, served: true, watched: false},
{pattern: 'lib/config/app.config.json', included: false, served: true, watched: false},
],
proxies: {
'/base/assets/': '/base/lib/process-services-cloud/src/lib/assets/',
'/assets/adf-core/i18n/en.json': '/base/lib/core/i18n/en.json',
'/app.config.json': '/base/lib/config/app.config.json'
},
plugins: [
require('karma-jasmine'),
require('karma-chrome-launcher'),

View File

@ -1,3 +1,19 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { AppListCloudModule } from './app-list-cloud.module';
describe('AppListCloudModule', () => {

View File

@ -14,5 +14,13 @@
"ALL_PROCESSES": "All Processes",
"RUNNING_PROCESSES": "Running Processes",
"COMPLETED_PROCESSES": "Completed Processes"
},
"ADF_CLOUD_START_TASK": {
"ERROR": {
"MESSAGE": "Enter a different value",
"REQUIRED": "Field required",
"DATE": "Date format DD/MM/YYYY",
"MAXIMUM_LENGTH": "Length exceeded, {{characters}} characters max."
}
}
}

View File

@ -1,3 +1,19 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Component, ViewEncapsulation, OnChanges, AfterContentInit, ContentChild, Output, EventEmitter, SimpleChanges, SimpleChange, Input } from '@angular/core';
import { DataTableSchema, PaginatedComponent,
EmptyCustomContentDirective, AppConfigService,

View File

@ -2,14 +2,14 @@
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the License);
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an AS IS BASIS,
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.

View File

@ -1,3 +1,19 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { ProcessListCloudModule } from './process-list-cloud.module';
describe('ProcessListCloudModule', () => {

View File

@ -1,3 +1,20 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { ProcessListCloudComponent } from './components/process-list-cloud.component';

View File

@ -22,6 +22,7 @@ import { TaskListCloudModule } from './task-list-cloud/task-list-cloud.module';
import { TaskCloudModule } from './task-cloud/task-cloud.module';
import { ProcessListCloudModule } from './process-list-cloud/process-list-cloud.module';
import { ProcessCloudModule } from './process-cloud/process-cloud.module';
import { StartTaskCloudModule } from './start-task-cloud/start-task-cloud.module';
@NgModule({
imports: [
@ -29,7 +30,8 @@ import { ProcessCloudModule } from './process-cloud/process-cloud.module';
TaskListCloudModule,
TaskCloudModule,
ProcessListCloudModule,
ProcessCloudModule
ProcessCloudModule,
StartTaskCloudModule
],
providers: [
{
@ -41,12 +43,13 @@ import { ProcessCloudModule } from './process-cloud/process-cloud.module';
}
}
],
declarations: [],
exports: [
AppListCloudModule,
TaskListCloudModule,
TaskCloudModule,
ProcessListCloudModule,
ProcessCloudModule]
ProcessCloudModule,
StartTaskCloudModule
]
})
export class ProcessServicesCloudModule { }

View File

@ -0,0 +1,29 @@
<form>
<mat-form-field class="adf-people-cloud">
<mat-label id="assignee-id">{{'ADF_TASK_LIST.START_TASK.FORM.LABEL.ASSIGNEE' | translate}}</mat-label>
<input #inputValue
matInput
class="adf-cloud-input"
data-automation-id="adf-people-cloud-search-input"
type="text"
[formControl]="searchUser"
[matAutocomplete]="auto">
<mat-autocomplete autoActiveFirstOption class="adf-people-cloud-list"
#auto="matAutocomplete"
(optionSelected)="onSelect($event.option.value)"
[displayWith]="getDisplayName">
<mat-option *ngFor="let user of users$ | async; let i = index" [value]="user">
<div class="adf-people-cloud-row" id="adf-people-cloud-user-{{i}}">
<div [outerHTML]="user | usernameInitials:'adf-people-widget-pic'"></div>
<span class="adf-people-label-name"> {{user | fullName}}</span>
</div>
</mat-option>
</mat-autocomplete>
</mat-form-field>
<div class="adf-start-task-cloud-error">
<div *ngIf="dataError" fxLayout="row" fxLayoutAlign="start start" [@transitionMessages]="_subscriptAnimationState">
<div class="adf-start-task-cloud-error-message">{{ 'ADF_CLOUD_START_TASK.ERROR.MESSAGE' | translate }}</div>
<mat-icon class="adf-start-task-cloud-error-icon">warning</mat-icon>
</div>
</div>
</form>

View File

@ -0,0 +1,62 @@
@mixin adf-cloud-people-theme($theme) {
$warn: map-get($theme, warn);
$primary: map-get($theme, primary);
$background: map-get($theme, background);
$foreground: map-get($theme, foreground);
.adf {
&-people-cloud {
width: 100%;
padding-top: 8px;
}
&-people-cloud-list {
margin: 5px 0;
padding: 10px 0;
}
&-people-cloud-row {
display: flex;
align-items: center;
}
&-people-cloud-pic {
background: mat-color($primary);
display: flex;
justify-content: center;
align-items: center;
width: 40px;
height: 40px;
border-radius: 100px;
color: mat-color($foreground, text);
font-weight: bolder;
font-size: 18px;
text-transform: uppercase;
}
&-start-task-cloud-error {
margin-top: -10px;
}
&-start-task-cloud-error {
position: absolute;
height: 20px;
&-message {
padding-right: 8px;
height: 16px;
font-size: 12px;
line-height: 1.33;
color: mat-color($warn);
width: auto;
}
&-icon {
font-size: 17px;
color: mat-color($warn);
}
}
}
}

View File

@ -0,0 +1,138 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { ComponentFixture, TestBed, async } from '@angular/core/testing';
import { By } from '@angular/platform-browser';
import { PeopleCloudComponent } from './people-cloud.component';
import { StartTaskCloudTestingModule } from '../../testing/start-task-cloud.testing.module';
import { LogService, setupTestBed, IdentityUserService, IdentityUserModel } from '@alfresco/adf-core';
import { mockUsers, mockRoles } from '../../mock/user-cloud.mock';
import { of } from 'rxjs';
import { ProcessServiceCloudTestingModule } from '../../../testing/process-service-cloud.testing.module';
describe('PeopleCloudComponent', () => {
let component: PeopleCloudComponent;
let fixture: ComponentFixture<PeopleCloudComponent>;
let element: HTMLElement;
let identityService: IdentityUserService;
let getRolesByUserIdSpy: jasmine.Spy;
let getUserSpy: jasmine.Spy;
setupTestBed({
imports: [ProcessServiceCloudTestingModule, StartTaskCloudTestingModule],
providers: [IdentityUserService, LogService]
});
beforeEach(() => {
fixture = TestBed.createComponent(PeopleCloudComponent);
component = fixture.componentInstance;
element = fixture.nativeElement;
identityService = TestBed.get(IdentityUserService);
getRolesByUserIdSpy = spyOn(identityService, 'getUserRoles').and.returnValue(of(mockRoles));
getUserSpy = spyOn(identityService, 'getUsers').and.returnValue(of(mockUsers));
});
it('should create PeopleCloudComponent', () => {
expect(component instanceof PeopleCloudComponent).toBeTruthy();
});
it('should able to fetch users', () => {
fixture.detectChanges();
expect(getUserSpy).toHaveBeenCalled();
});
it('should able to fetch roles by user id', async(() => {
fixture.detectChanges();
fixture.whenStable().then(() => {
expect(getRolesByUserIdSpy).toHaveBeenCalled();
});
}));
it('should not list the current logged in user when showCurrentUser is false', async(() => {
spyOn(identityService, 'getCurrentUserInfo').and.returnValue(of(mockUsers[1]));
component.showCurrentUser = false;
fixture.detectChanges();
fixture.whenStable().then(() => {
const currentUser = component.users.find((user) => {
return user.username === mockUsers[1].username;
});
expect(currentUser).toBeUndefined();
});
}));
it('should show the users if the typed result match', async(() => {
component.users$ = of(<IdentityUserModel[]> mockUsers);
fixture.detectChanges();
let inputHTMLElement: HTMLInputElement = <HTMLInputElement> element.querySelector('input');
inputHTMLElement.focus();
inputHTMLElement.dispatchEvent(new Event('input'));
inputHTMLElement.dispatchEvent(new Event('keyup'));
inputHTMLElement.dispatchEvent(new Event('keydown'));
inputHTMLElement.value = 'M';
fixture.detectChanges();
fixture.whenStable().then(() => {
fixture.detectChanges();
expect(fixture.debugElement.query(By.css('mat-option'))).toBeDefined();
});
}));
it('should hide result list if input is empty', () => {
fixture.detectChanges();
let inputHTMLElement: HTMLInputElement = <HTMLInputElement> element.querySelector('input');
inputHTMLElement.focus();
inputHTMLElement.value = '';
inputHTMLElement.dispatchEvent(new Event('keyup'));
inputHTMLElement.dispatchEvent(new Event('input'));
fixture.detectChanges();
fixture.whenStable().then(() => {
fixture.detectChanges();
expect(fixture.debugElement.query(By.css('mat-option'))).toBeNull();
expect(fixture.debugElement.query(By.css('#adf-people-cloud-user-0'))).toBeNull();
});
});
it('should emit selectedUser if option is valid', async() => {
fixture.detectChanges();
let selectEmitSpy = spyOn(component.selectedUser, 'emit');
component.onSelect(new IdentityUserModel({ username: 'username'}));
fixture.whenStable().then(() => {
fixture.detectChanges();
expect(selectEmitSpy).toHaveBeenCalled();
});
});
it('should show an error message if the user is invalid', async(() => {
getUserSpy.and.returnValue(of([]));
getRolesByUserIdSpy.and.returnValue(of([]));
component.dataError = true;
fixture.detectChanges();
let inputHTMLElement: HTMLInputElement = <HTMLInputElement> element.querySelector('input');
inputHTMLElement.focus();
inputHTMLElement.dispatchEvent(new Event('input'));
inputHTMLElement.dispatchEvent(new Event('keyup'));
inputHTMLElement.dispatchEvent(new Event('keydown'));
inputHTMLElement.value = 'ZZZ';
fixture.detectChanges();
fixture.whenStable().then(() => {
fixture.detectChanges();
const errorMessage = element.querySelector('.adf-start-task-cloud-error-message');
expect(element.querySelector('.adf-start-task-cloud-error')).not.toBeNull();
expect(errorMessage.textContent).toContain('ADF_CLOUD_START_TASK.ERROR.MESSAGE');
});
}));
});

View File

@ -0,0 +1,110 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { FormControl } from '@angular/forms';
import { Component, OnInit, Output, EventEmitter, ViewEncapsulation, Input } from '@angular/core';
import { Observable, of } from 'rxjs';
import { FullNamePipe, IdentityUserModel, IdentityUserService } from '@alfresco/adf-core';
import { trigger, state, style, transition, animate } from '@angular/animations';
@Component({
selector: 'adf-cloud-people',
templateUrl: './people-cloud.component.html',
styleUrls: ['./people-cloud.component.scss'],
animations: [
trigger('transitionMessages', [
state('enter', style({opacity: 1, transform: 'translateY(0%)'})),
transition('void => enter', [
style({opacity: 0, transform: 'translateY(-100%)'}),
animate('300ms cubic-bezier(0.55, 0, 0.55, 0.2)')
])
])
],
providers: [FullNamePipe],
encapsulation: ViewEncapsulation.None
})
export class PeopleCloudComponent implements OnInit {
static ROLE_ACTIVITI_ADMIN = 'ACTIVITI_ADMIN';
static ROLE_ACTIVITI_USER = 'ACTIVITI_USER';
static ROLE_ACTIVITI_MODELER = 'ACTIVITI_MODELER';
/** Show current user in the list or not. */
@Input()
showCurrentUser: boolean = true;
/** Emitted when a user is selected. */
@Output()
selectedUser: EventEmitter<IdentityUserModel> = new EventEmitter<IdentityUserModel>();
/** Emitted when an error occurs. */
@Output()
error: EventEmitter<any> = new EventEmitter<any>();
users$: Observable<IdentityUserModel[]>;
searchUser: FormControl = new FormControl();
_subscriptAnimationState: string = 'enter';
users: IdentityUserModel[] = [];
dataError = false;
currentUser: IdentityUserModel;
constructor(private identityUserService: IdentityUserService) { }
ngOnInit() {
this.loadUsers();
this.initSearch();
}
initSearch() {
this.searchUser.valueChanges.subscribe((keyword) => {
this.users$ = this.searchUsers(keyword);
});
}
private async loadUsers() {
const roles = [PeopleCloudComponent.ROLE_ACTIVITI_ADMIN, PeopleCloudComponent.ROLE_ACTIVITI_MODELER, PeopleCloudComponent.ROLE_ACTIVITI_USER];
if (this.showCurrentUser) {
this.users = await this.identityUserService.getUsersByRolesWithCurrentUser(roles);
} else {
this.users = await this.identityUserService.getUsersByRolesWithoutCurrentUser(roles);
}
}
private searchUsers(keyword: string): Observable<IdentityUserModel[]> {
const filteredUsers = this.users.filter((user) => {
return user.username.toLowerCase().indexOf(keyword.toString().toLowerCase()) !== -1;
});
this.dataError = filteredUsers.length === 0;
return of(filteredUsers);
}
onSelect(selectedUser: IdentityUserModel) {
this.selectedUser.emit(selectedUser);
this.dataError = false;
}
getDisplayName(user): string {
return FullNamePipe.prototype.transform(user);
}
}

View File

@ -0,0 +1,89 @@
<mat-card>
<mat-card-header fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="10px" class="adf-cloud-start-task-heading">
<mat-card-title>{{'ADF_TASK_LIST.START_TASK.FORM.TITLE' | translate}}</mat-card-title>
</mat-card-header>
<form [formGroup]="taskForm" fxLayout="column" (ngSubmit)="saveTask()">
<mat-card-content>
<div class="adf-task-name">
<mat-form-field fxFlex>
<mat-label>{{'ADF_TASK_LIST.START_TASK.FORM.LABEL.NAME' | translate }}</mat-label>
<input
matInput
id="name_id"
class="form-control"
formControlName="name">
<mat-error *ngIf="nameController.hasError('required')">
{{ 'ADF_CLOUD_START_TASK.ERROR.REQUIRED' | translate }}
</mat-error>
<mat-error *ngIf="nameController.hasError('maxlength')">
{{ 'ADF_CLOUD_START_TASK.ERROR.MAXIMUM_LENGTH' | translate : { characters : maxNameLength } }}
</mat-error>
</mat-form-field>
</div>
<div class="input-row" fxLayout="row" fxLayout.lt-md="column" fxLayoutGap="20px" fxLayoutGap.lt-md="0px">
<mat-form-field fxFlex>
<mat-label>{{'ADF_TASK_LIST.START_TASK.FORM.LABEL.DESCRIPTION' | translate}}</mat-label>
<textarea
matInput
class="form-control"
id="description_id"
formControlName="description">
</textarea>
</mat-form-field>
<mat-form-field fxFlex>
<div style="height: 40px;">
<input matInput type="number" placeholder="Priority" formControlName="priority">
</div>
</mat-form-field>
</div>
<div class="input-row" fxLayout="row" fxLayout.lt-md="column" fxLayoutGap="20px" fxLayoutGap.lt-md="0px">
<mat-form-field fxFlex>
<input matInput
[matDatepicker]="taskDatePicker"
(keydown)="true"
(focusout)="onDateChanged($event.srcElement.value)"
placeholder="{{'ADF_TASK_LIST.START_TASK.FORM.LABEL.DATE'|translate}}"
[(ngModel)]="dueDate"
[ngModelOptions]="{standalone: true}"
id="date_id">
<mat-datepicker-toggle matSuffix [for]="taskDatePicker"></mat-datepicker-toggle>
<mat-datepicker #taskDatePicker
[touchUi]="true"
(dateChanged)="onDateChanged($event)">
</mat-datepicker>
<div class="adf-error-text-container">
<div *ngIf="dateError">
<div class="adf-error-text">{{'ADF_CLOUD_START_TASK.ERROR.DATE' | translate}}</div>
<mat-icon class="adf-error-icon">warning</mat-icon>
</div>
</div>
</mat-form-field>
<div fxFlex>
<adf-cloud-people (selectedUser)="onAssigneeSelect($event)"></adf-cloud-people>
</div>
</div>
</mat-card-content>
<mat-card-actions>
<div class="adf-cloud-start-task-footer" fxLayout="row" fxLayoutAlign="end end" >
<button
mat-button
type="button"
(click)="onCancel()"
id="button-cancel">
{{'ADF_TASK_LIST.START_TASK.FORM.ACTION.CANCEL'|translate}}
</button>
<button
color="primary"
type="submit" [disabled]="dateError || !taskForm.valid || submitted"
mat-button
id="button-start">
{{'ADF_TASK_LIST.START_TASK.FORM.ACTION.START'|translate}}
</button>
</div>
</mat-card-actions>
</form>
</mat-card>

View File

@ -0,0 +1,34 @@
@mixin adf-start-task-cloud-theme($theme) {
$primary: map-get($theme, primary);
$accent: map-get($theme, accent);
$warn: map-get($theme, warn);
$foreground: map-get($theme, foreground);
$header-border: 1px solid mat-color($foreground, divider);
.adf-cloud-start-task-heading {
border-bottom: $header-border;
margin-bottom: 10px;
.mat-card-title {
font-weight: bold;
font-size: 18px;
}
}
.adf-cloud-start-task-footer {
padding: 4px;
font-size: 18px;
border-top: 1px solid #eee;
}
adf-cloud-start-task {
.adf {
&-cloud-start-task-footer {
.mat-button {
text-transform: uppercase !important;
}
}
}
}
}

View File

@ -0,0 +1,240 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { setupTestBed, IdentityUserService } from '@alfresco/adf-core';
import {
AlfrescoApiService,
AppConfigService,
LogService,
StorageService,
UserPreferencesService,
IdentityUserModel
} from '@alfresco/adf-core';
import { StartTaskCloudService } from '../services/start-task-cloud.service';
import { StartTaskCloudComponent } from './start-task-cloud.component';
import { of, throwError } from 'rxjs';
import { taskDetailsMock } from '../mock/task-details.mock';
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import { ProcessServiceCloudTestingModule } from './../../testing/process-service-cloud.testing.module';
import { StartTaskCloudTestingModule } from '../testing/start-task-cloud.testing.module';
import { mockRoles, mockUsers } from '../mock/user-cloud.mock';
import { TaskDetailsCloudModel } from '../models/task-details-cloud.model';
describe('StartTaskCloudComponent', () => {
let component: StartTaskCloudComponent;
let fixture: ComponentFixture<StartTaskCloudComponent>;
let service: StartTaskCloudService;
let identityService: IdentityUserService;
let element: HTMLElement;
let createNewTaskSpy: jasmine.Spy;
let getRolesByUserIdSpy: jasmine.Spy;
let getUserSpy: jasmine.Spy;
setupTestBed({
imports: [ProcessServiceCloudTestingModule, StartTaskCloudTestingModule],
providers: [StartTaskCloudService, AlfrescoApiService, AppConfigService, LogService, StorageService, UserPreferencesService],
schemas: [ CUSTOM_ELEMENTS_SCHEMA ]
});
beforeEach(async(() => {
fixture = TestBed.createComponent(StartTaskCloudComponent);
component = fixture.componentInstance;
element = fixture.nativeElement;
service = TestBed.get(StartTaskCloudService);
identityService = TestBed.get(IdentityUserService);
createNewTaskSpy = spyOn(service, 'createNewTask').and.returnValue(of(taskDetailsMock));
getRolesByUserIdSpy = spyOn(identityService, 'getUserRoles').and.returnValue(of(mockRoles));
getUserSpy = spyOn(identityService, 'getUsers').and.returnValue(of(mockUsers));
spyOn(identityService, 'getCurrentUserInfo').and.returnValue(of(new IdentityUserModel({username: 'currentUser'})));
fixture.detectChanges();
}));
it('should create instance of StartTaskCloudComponent', () => {
expect(component instanceof StartTaskCloudComponent).toBe(true, 'should create StartTaskCloudComponent');
});
it('should defined adf-cloud-people and fetch users ', () => {
component.ngOnInit();
fixture.detectChanges();
const peopleElement = fixture.debugElement.nativeElement.querySelector('adf-cloud-people');
expect(peopleElement).toBeDefined();
expect(getRolesByUserIdSpy).toHaveBeenCalled();
expect(getUserSpy).toHaveBeenCalled();
});
describe('create task', () => {
beforeEach(() => {
createNewTaskSpy.and.returnValue(of(
{
id: 91,
name: 'fakeName',
assignee: 'fake-assignee'
}
));
});
it('should create new task when start button is clicked', async(() => {
let successSpy = spyOn(component.success, 'emit');
component.taskForm.controls['name'].setValue('fakeName');
fixture.detectChanges();
let createTaskButton = <HTMLElement> element.querySelector('#button-start');
createTaskButton.click();
fixture.detectChanges();
fixture.whenStable().then(() => {
expect(createNewTaskSpy).toHaveBeenCalled();
expect(successSpy).toHaveBeenCalled();
});
}));
it('should send on success event when the task is started', async(() => {
let successSpy = spyOn(component.success, 'emit');
component.taskForm.controls['name'].setValue('fakeName');
component.assigneeName = 'fake-assignee';
fixture.detectChanges();
let createTaskButton = <HTMLElement> element.querySelector('#button-start');
createTaskButton.click();
fixture.detectChanges();
fixture.whenStable().then(() => {
expect(successSpy).toHaveBeenCalledWith({
id: 91,
name: 'fakeName',
assignee: 'fake-assignee'
});
});
}));
it('should send on success event when only name is given', async(() => {
let successSpy = spyOn(component.success, 'emit');
component.taskForm.controls['name'].setValue('fakeName');
fixture.detectChanges();
let createTaskButton = <HTMLElement> element.querySelector('#button-start');
createTaskButton.click();
fixture.detectChanges();
fixture.whenStable().then(() => {
expect(successSpy).toHaveBeenCalled();
});
}));
it('should not emit success event when data not present', () => {
let successSpy = spyOn(component.success, 'emit');
component.taskForm.controls['name'].setValue('');
fixture.detectChanges();
let createTaskButton = <HTMLElement> element.querySelector('#button-start');
createTaskButton.click();
expect(createNewTaskSpy).not.toHaveBeenCalled();
expect(successSpy).not.toHaveBeenCalled();
});
it('should assign task when an assignee is selected', async(() => {
let successSpy = spyOn(component.success, 'emit');
component.taskForm.controls['name'].setValue('fakeName');
component.assigneeName = 'fake-assignee';
fixture.detectChanges();
let createTaskButton = <HTMLElement> element.querySelector('#button-start');
createTaskButton.click();
fixture.detectChanges();
fixture.whenStable().then(() => {
expect(successSpy).toHaveBeenCalledWith({
id: 91,
name: 'fakeName',
assignee: 'fake-assignee'
});
});
}));
it('should assign task to the logged in user when assignee is not selected', async(() => {
component.taskForm.controls['name'].setValue('fakeName');
fixture.detectChanges();
let createTaskButton = <HTMLElement> element.querySelector('#button-start');
createTaskButton.click();
fixture.detectChanges();
fixture.whenStable().then(() => {
const taskRequest = new TaskDetailsCloudModel({ name: 'fakeName', assignee: 'currentUser'});
expect(createNewTaskSpy).toHaveBeenCalledWith(taskRequest);
});
}));
});
it('should show start task button', () => {
component.taskForm.controls['name'].setValue('fakeName');
fixture.detectChanges();
expect(element.querySelector('#button-start')).toBeDefined();
expect(element.querySelector('#button-start')).not.toBeNull();
expect(element.querySelector('#button-start').textContent).toContain('ADF_TASK_LIST.START_TASK.FORM.ACTION.START');
});
it('should disable start button if name is empty', () => {
component.taskForm.controls['name'].setValue('');
fixture.detectChanges();
let createTaskButton = fixture.nativeElement.querySelector('#button-start');
expect(createTaskButton.disabled).toBeTruthy();
});
it('should cancel start task on cancel button click', () => {
fixture.detectChanges();
let emitSpy = spyOn(component.cancel, 'emit');
let cancelTaskButton = fixture.nativeElement.querySelector('#button-cancel');
cancelTaskButton.click();
expect(emitSpy).not.toBeNull();
expect(emitSpy).toHaveBeenCalled();
});
it('should enable start button if name is filled out', () => {
component.taskForm.controls['name'].setValue('fakeName');
fixture.detectChanges();
let createTaskButton = fixture.nativeElement.querySelector('#button-start');
expect(createTaskButton.disabled).toBeFalsy();
});
it('should emit error when there is an error while creating task', () => {
component.taskForm.controls['name'].setValue('fakeName');
let errorSpy = spyOn(component.error, 'emit');
createNewTaskSpy.and.returnValue(throwError({}));
let createTaskButton = <HTMLElement> element.querySelector('#button-start');
fixture.detectChanges();
createTaskButton.click();
expect(errorSpy).toHaveBeenCalled();
});
it('should emit error when task name exceeds maximum length', () => {
component.maxNameLength = 2;
component.ngOnInit();
fixture.detectChanges();
let name = component.taskForm.controls['name'];
name.setValue('task');
fixture.detectChanges();
expect(name.valid).toBeFalsy();
name.setValue('ta');
fixture.detectChanges();
expect(name.valid).toBeTruthy();
});
it('should emit error when task name field is empty', () => {
fixture.detectChanges();
let name = component.taskForm.controls['name'];
name.setValue('');
fixture.detectChanges();
expect(name.valid).toBeFalsy();
name.setValue('task');
fixture.detectChanges();
expect(name.valid).toBeTruthy();
});
});

View File

@ -0,0 +1,197 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Component, EventEmitter, Input, OnInit, Output, ViewEncapsulation, OnDestroy } from '@angular/core';
import { DateAdapter, MAT_DATE_FORMATS } from '@angular/material/core';
import { MOMENT_DATE_FORMATS, MomentDateAdapter } from '@alfresco/adf-core';
import moment from 'moment-es6';
import { Moment } from 'moment';
import { Observable, Subscription } from 'rxjs';
import { FormBuilder, AbstractControl, Validators, FormGroup, FormControl } from '@angular/forms';
import { StartTaskCloudService } from '../services/start-task-cloud.service';
import { TaskDetailsCloudModel } from '../models/task-details-cloud.model';
import {
LogService,
UserPreferencesService,
IdentityUserService,
IdentityUserModel
} from '@alfresco/adf-core';
@Component({
selector: 'adf-cloud-start-task',
templateUrl: './start-task-cloud.component.html',
styleUrls: ['./start-task-cloud.component.scss'],
providers: [
{ provide: DateAdapter, useClass: MomentDateAdapter },
{ provide: MAT_DATE_FORMATS, useValue: MOMENT_DATE_FORMATS }],
encapsulation: ViewEncapsulation.None
})
export class StartTaskCloudComponent implements OnInit, OnDestroy {
static MAX_NAME_LENGTH = 255;
public DATE_FORMAT: string = 'DD/MM/YYYY';
/** (required) Name of the app. */
@Input()
appName: string;
/** Maximum length of the task name. */
@Input()
maxNameLength: number = StartTaskCloudComponent.MAX_NAME_LENGTH;
/** Name of the task. */
@Input()
name: string = '';
/** Emitted when the task is successfully created. */
@Output()
success: EventEmitter<any> = new EventEmitter<any>();
/** Emitted when the cancel button is clicked by the user. */
@Output()
cancel: EventEmitter<void> = new EventEmitter<void>();
/** Emitted when an error occurs. */
@Output()
error: EventEmitter<any> = new EventEmitter<any>();
users$: Observable<any[]>;
taskId: string;
dueDate: Date;
submitted = false;
assigneeName: string;
dateError: boolean;
taskForm: FormGroup;
currentUser: IdentityUserModel;
private localeSub: Subscription;
private createTaskSub: Subscription;
constructor(private taskService: StartTaskCloudService,
private dateAdapter: DateAdapter<Moment>,
private preferences: UserPreferencesService,
private formBuilder: FormBuilder,
private identityUserService: IdentityUserService,
private logService: LogService) {
}
ngOnInit() {
this.localeSub = this.preferences.locale$.subscribe((locale) => {
this.dateAdapter.setLocale(locale);
});
this.loadCurrentUser();
this.buildForm();
}
ngOnDestroy() {
if (this.localeSub) {
this.localeSub.unsubscribe();
}
if (this.createTaskSub) {
this.createTaskSub.unsubscribe();
}
}
buildForm() {
this.taskForm = this.formBuilder.group({
name: new FormControl(this.name, [Validators.required, Validators.maxLength(this.getMaxNameLength())]),
priority: new FormControl(),
description: new FormControl()
});
}
private getMaxNameLength(): number {
return this.maxNameLength > StartTaskCloudComponent.MAX_NAME_LENGTH ?
StartTaskCloudComponent.MAX_NAME_LENGTH : this.maxNameLength;
}
private async loadCurrentUser() {
this.currentUser = await this.identityUserService.getCurrentUserInfo().toPromise();
}
public saveTask() {
this.submitted = true;
const newTask = Object.assign(this.taskForm.value);
newTask.appName = this.getAppName();
newTask.dueDate = this.getDueDate();
newTask.assignee = this.getAssigneeName();
this.createNewTask(new TaskDetailsCloudModel(newTask));
}
private createNewTask(newTask: TaskDetailsCloudModel) {
this.createTaskSub = this.taskService.createNewTask(newTask)
.subscribe(
(res: any) => {
this.submitted = false;
this.success.emit(res);
},
(err) => {
this.submitted = false;
this.error.emit(err);
this.logService.error('An error occurred while creating new task');
});
}
public onCancel() {
this.cancel.emit();
}
private getDueDate(): Date {
return this.dueDate;
}
private getAppName(): string {
return this.appName ? this.appName : '';
}
private getAssigneeName(): string {
return this.assigneeName ? this.assigneeName : this.currentUser.username;
}
onDateChanged(newDateValue) {
this.dateError = false;
if (newDateValue) {
let momentDate = moment(newDateValue, this.DATE_FORMAT, true);
if (!momentDate.isValid()) {
this.dateError = true;
}
}
}
onAssigneeSelect(assignee: IdentityUserModel) {
this.assigneeName = assignee ? assignee.username : '';
}
get nameController(): AbstractControl {
return this.taskForm.get('name');
}
get priorityController(): AbstractControl {
return this.taskForm.get('priority');
}
}

View File

@ -0,0 +1,20 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { TaskDetailsCloudModel } from '../models/task-details-cloud.model';
export let taskDetailsMock = new TaskDetailsCloudModel({ assignee: 'fake-assigne', name: 'fake-name' });

View File

@ -0,0 +1,30 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export const mockUsers = [
{ id: 'fake-id-1', username: 'first-name-1 last-name-1', firstName: 'first-name-1', lastName: 'last-name-1', email: 'abc@xyz.com' },
{ id: 'fake-id-2', username: 'first-name-2 last-name-2', firstName: 'first-name-2', lastName: 'last-name-2', email: 'abcd@xyz.com'},
{ id: 'fake-id-3', username: 'first-name-3 last-name-3', firstName: 'first-name-3', lastName: 'last-name-3', email: 'abcde@xyz.com' }
];
export const mockRoles = [
{ id: 'id-1', name: 'MOCK-ADMIN-ROLE'},
{ id: 'id-2', name: 'MOCK-USER-ROLE'},
{ id: 'id-3', name: 'MOCK_MODELER-ROLE' },
{ id: 'id-4', name: 'MOCK-ROLE-1' },
{ id: 'id-5', name: 'MOCK-ROLE-2'}
];

View File

@ -0,0 +1,36 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export class StartTaskCloudRequestModel {
name: string;
description: string;
assignee: string;
priority: string;
dueDate: Date;
payloadType: string;
constructor(obj?: any) {
if (obj) {
this.name = obj.name || null;
this.description = obj.description || null;
this.assignee = obj.assignee || null;
this.priority = obj.priority || null;
this.dueDate = obj.dueDate || null;
this.payloadType = 'CreateTaskPayload';
}
}
}

View File

@ -0,0 +1,79 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export class TaskDetailsCloudModel {
id: string;
name: string;
appName: string;
assignee: string;
appVersion: string;
createdDate: Date;
claimedDate: Date;
formKey: any;
category: any;
description: string;
dueDate: Date;
lastModified: Date;
lastModifiedTo: Date;
lastModifiedFrom: Date;
owner: any;
parentTaskId: number;
priority: number;
processDefinitionId: string;
processInstanceId: string;
serviceType: any;
status: string;
standAlone: boolean;
serviceName: string;
serviceFullName: string;
serviceVersion: string;
constructor(obj?: any) {
if (obj) {
this.id = obj.id || null;
this.name = obj.name || null;
this.appName = obj.appName || null;
this.assignee = obj.assignee || null;
this.appVersion = obj.appVersion || null;
this.createdDate = obj.createdDate || null;
this.claimedDate = obj.claimedDate || null;
this.formKey = obj.formKey || null;
this.description = obj.description || null;
this.dueDate = obj.dueDate || null;
this.lastModified = obj.lastModified || null;
this.lastModifiedTo = obj.lastModifiedTo || null;
this.lastModifiedFrom = obj.lastModifiedFrom || null;
this.owner = obj.owner || null;
this.parentTaskId = obj.parentTaskId || null;
this.priority = obj.priority || null;
this.processDefinitionId = obj.processDefinitionId || null;
this.processInstanceId = obj.processInstanceId || null;
this.serviceType = obj.serviceType || null;
this.status = obj.status || null;
this.standAlone = obj.standAlone || null;
this.serviceName = obj.serviceName || null;
this.serviceName = obj.serviceName || null;
this.serviceFullName = obj.serviceFullName || null;
this.serviceVersion = obj.serviceVersion || null;
}
}
}
export interface StartTaskCloudResponseModel {
entry: TaskDetailsCloudModel;
}

View File

@ -0,0 +1,22 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export * from './models/task-details-cloud.model';
export * from './services/start-task-cloud.service';
export * from './components/start-task-cloud.component';
export * from './components/people-cloud/people-cloud.component';
export * from './start-task-cloud.module';

View File

@ -0,0 +1,78 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { TestBed } from '@angular/core/testing';
import { setupTestBed } from '@alfresco/adf-core';
import { StartTaskCloudService } from './start-task-cloud.service';
import { StartTaskCloudTestingModule } from '../testing/start-task-cloud.testing.module';
import { of, throwError } from 'rxjs';
import { taskDetailsMock } from '../mock/task-details.mock';
import { TaskDetailsCloudModel } from '../models/task-details-cloud.model';
import { HttpErrorResponse } from '@angular/common/http';
import {
AlfrescoApiService,
AppConfigService,
LogService,
StorageService
} from '@alfresco/adf-core';
describe('StartTaskCloudService', () => {
let service: StartTaskCloudService;
setupTestBed({
imports: [StartTaskCloudTestingModule],
providers: [StartTaskCloudService, AlfrescoApiService, AppConfigService, LogService, StorageService]
});
beforeEach(() => {
service = TestBed.get(StartTaskCloudService);
});
it('should able to create a new task ', (done) => {
spyOn(service, 'createNewTask').and.returnValue(of({id: 'fake-id', name: 'fake-name'}));
service.createNewTask(taskDetailsMock).subscribe(
(res: TaskDetailsCloudModel) => {
expect(res).toBeDefined();
expect(res.id).toEqual('fake-id');
expect(res.name).toEqual('fake-name');
done();
}
);
});
it('Should not able to create a task if error occurred', () => {
const errorResponse = new HttpErrorResponse({
error: 'Mock Error',
status: 404, statusText: 'Not Found'
});
spyOn(service, 'createNewTask').and.returnValue(throwError(errorResponse));
service.createNewTask(taskDetailsMock)
.subscribe(
() => {
fail('expected an error, not applications');
},
(error) => {
expect(error.status).toEqual(404);
expect(error.statusText).toEqual('Not Found');
expect(error.error).toEqual('Mock Error');
}
);
});
});

View File

@ -0,0 +1,71 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Injectable } from '@angular/core';
import {
AlfrescoApiService,
AppConfigService,
LogService
} from '@alfresco/adf-core';
import { from, Observable, throwError } from 'rxjs';
import { StartTaskCloudRequestModel } from '../models/start-task-cloud-request.model';
import { TaskDetailsCloudModel, StartTaskCloudResponseModel } from '../models/task-details-cloud.model';
import { map, catchError } from 'rxjs/operators';
@Injectable()
export class StartTaskCloudService {
constructor(
private apiService: AlfrescoApiService,
private appConfigService: AppConfigService,
private logService: LogService
) {}
createNewTask(taskDetails: TaskDetailsCloudModel): Observable<TaskDetailsCloudModel> {
let queryUrl = this.buildCreateTaskUrl(taskDetails.appName);
const bodyParam = JSON.stringify(this.buildRequestBody(taskDetails));
const httpMethod = 'POST', pathParams = {}, queryParams = {}, headerParams = {},
formParams = {}, authNames = [], contentTypes = ['application/json'], accepts = ['application/json'];
return from(
this.apiService
.getInstance()
.oauth2Auth.callCustomApi(
queryUrl, httpMethod, pathParams, queryParams,
headerParams, formParams, bodyParam, authNames,
contentTypes, accepts, Object, null, null)
).pipe(
map((response: StartTaskCloudResponseModel) => {
return new TaskDetailsCloudModel(response.entry);
}),
catchError((err) => this.handleError(err))
);
}
private buildCreateTaskUrl(appName: string): any {
return `${this.appConfigService.get('bpmHost')}/${appName}-rb/v1/tasks`;
}
private buildRequestBody(taskDetails: any) {
return new StartTaskCloudRequestModel(taskDetails);
}
private handleError(error: any) {
this.logService.error(error);
return throwError(error || 'Server error');
}
}

View File

@ -0,0 +1,55 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FlexLayoutModule } from '@angular/flex-layout';
import { MaterialModule } from '../material.module';
import { TranslateModule, TranslateLoader } from '@ngx-translate/core';
import { TemplateModule, TranslateLoaderService, FormModule, PipeModule } from '@alfresco/adf-core';
import { StartTaskCloudComponent } from './components/start-task-cloud.component';
import { StartTaskCloudService } from './services/start-task-cloud.service';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { PeopleCloudComponent } from './components/people-cloud/people-cloud.component';
@NgModule({
imports: [
CommonModule,
PipeModule,
TranslateModule.forRoot({
loader: {
provide: TranslateLoader,
useClass: TranslateLoaderService
}
}),
TemplateModule,
FlexLayoutModule,
MaterialModule,
FormsModule,
ReactiveFormsModule,
FormModule
],
declarations: [StartTaskCloudComponent, PeopleCloudComponent],
providers: [
StartTaskCloudService
],
exports: [
StartTaskCloudComponent,
PeopleCloudComponent
]
})
export class StartTaskCloudModule { }

View File

@ -0,0 +1,49 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { NgModule } from '@angular/core';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { CommonModule } from '@angular/common';
import { FlexLayoutModule } from '@angular/flex-layout';
import { MaterialModule } from '../../material.module';
import { TranslateModule, TranslateLoader } from '@ngx-translate/core';
import { TemplateModule, TranslateLoaderService, FormModule, PipeModule } from '@alfresco/adf-core';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { StartTaskCloudModule } from '../start-task-cloud.module';
@NgModule({
imports: [
CommonModule,
NoopAnimationsModule,
TranslateModule.forRoot({
loader: {
provide: TranslateLoader,
useClass: TranslateLoaderService
}
}),
TemplateModule,
FlexLayoutModule,
MaterialModule,
FormsModule,
ReactiveFormsModule,
FormModule,
PipeModule,
StartTaskCloudModule
]
})
export class StartTaskCloudTestingModule { }

View File

@ -2,10 +2,15 @@
@import './../app-list-cloud/components/app-list-cloud.component';
@import './../task-cloud/task-filters-cloud/task-filters-cloud.component.scss';
@import './../process-list-cloud/components/process-list-cloud.component.scss';
@import './../start-task-cloud/components/start-task-cloud.component';
@import './../start-task-cloud/components/people-cloud/people-cloud.component';
@mixin adf-process-services-cloud-theme($theme) {
@include adf-cloud-app-list-theme($theme);
@include adf-cloud-app-details-theme($theme);
@include adf-cloud-task-filters-theme($theme);
@include adf-process-filters-cloud-theme($theme);
@include adf-start-task-cloud-theme($theme);
@include adf-cloud-people-theme($theme);
}

View File

@ -1,3 +1,20 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { TaskCloudModule } from './task-cloud.module';
describe('TaskCloudModule', () => {

View File

@ -1,3 +1,20 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { TaskFiltersCloudComponent } from './task-filters-cloud/task-filters-cloud.component';

View File

@ -2,18 +2,19 @@
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the License);
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an AS IS BASIS,
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { ObjectDataColumn } from '@alfresco/adf-core';
export const fakeTaskCloudList = {

View File

@ -1,3 +1,20 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { TaskListCloudModule } from './task-list-cloud.module';
describe('TaskListCloudModule', () => {

View File

@ -1,3 +1,20 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { MaterialModule } from '../material.module';

View File

@ -20,3 +20,4 @@ export * from './lib/app-list-cloud/public-api';
export * from './lib/task-list-cloud/public-api';
export * from './lib/task-cloud/public-api';
export * from './lib/process-list-cloud/public_api';
export * from './lib/start-task-cloud/public-api';

View File

@ -1,3 +1,19 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// This file is required by karma.conf.js and loads recursively all the .spec and framework files
import 'core-js/es7/reflect';

View File

@ -3,45 +3,45 @@
module.exports = function (config) {
config.set({
basePath: '',
basePath: '../../',
files: [
{ pattern: '../../node_modules/core-js/client/core.js', included: true, watched: false },
{ pattern: '../../node_modules/tslib/tslib.js', included: true, watched: false },
{ pattern: '../../node_modules/hammerjs/hammer.min.js', included: true, watched: false },
{ pattern: '../../node_modules/hammerjs/hammer.min.js.map', included: false, watched: false },
{ pattern: 'node_modules/core-js/client/core.js', included: true, watched: false },
{ pattern: 'node_modules/tslib/tslib.js', included: true, watched: false },
{ pattern: 'node_modules/hammerjs/hammer.min.js', included: true, watched: false },
{ pattern: 'node_modules/hammerjs/hammer.min.js.map', included: false, watched: false },
// pdf-js
{ pattern: '../../node_modules/pdfjs-dist/build/pdf.js', included: true, watched: false },
{ pattern: '../../node_modules/pdfjs-dist/build/pdf.worker.js', included: true, watched: false },
{ pattern: '../../node_modules/pdfjs-dist/web/pdf_viewer.js', included: true, watched: false },
{ pattern: 'node_modules/pdfjs-dist/build/pdf.js', included: true, watched: false },
{ pattern: 'node_modules/pdfjs-dist/build/pdf.worker.js', included: true, watched: false },
{ pattern: 'node_modules/pdfjs-dist/web/pdf_viewer.js', included: true, watched: false },
{
pattern: '../../node_modules/@angular/material/prebuilt-themes/indigo-pink.css',
pattern: 'node_modules/@angular/material/prebuilt-themes/indigo-pink.css',
included: true,
watched: false
},
{ pattern: '../../node_modules/chart.js/dist/Chart.js', included: true, watched: false },
{ pattern: '../../node_modules/raphael/raphael.min.js', included: true, watched: false },
{ pattern: 'node_modules/chart.js/dist/Chart.js', included: true, watched: false },
{ pattern: 'node_modules/raphael/raphael.min.js', included: true, watched: false },
{
pattern: '../../node_modules/ng2-charts/bundles/ng2-charts.umd.js',
pattern: 'node_modules/ng2-charts/bundles/ng2-charts.umd.js',
included: false,
served: true,
watched: false
},
{ pattern: '../../node_modules/alfresco-js-api/dist/alfresco-js-api.min.js', included: true, watched: false },
{ pattern: '../../node_modules/moment/min/moment.min.js', included: true, watched: false },
{ pattern: 'node_modules/alfresco-js-api/dist/alfresco-js-api.min.js', included: true, watched: false },
{ pattern: 'node_modules/moment/min/moment.min.js', included: true, watched: false },
{ pattern: '../core/i18n/**/en.json', included: false, served: true, watched: false },
{ pattern: '../content-services/i18n/**/en.json', included: false, served: true, watched: false },
{ pattern: '../process-services/i18n/**/en.json', included: false, served: true, watched: false },
{ pattern: '../process-services/**/*.ts', included: false, served: true, watched: false },
{ pattern: '../config/app.config.json', included: false, served: true, watched: false }
{ pattern: 'lib/core/i18n/**/en.json', included: false, served: true, watched: false },
{ pattern: 'lib/content-services/i18n/**/en.json', included: false, served: true, watched: false },
{ pattern: 'lib/process-services/i18n/**/en.json', included: false, served: true, watched: false },
{ pattern: 'lib/process-services/**/*.ts', included: false, served: true, watched: false },
{ pattern: 'lib/config/app.config.json', included: false, served: true, watched: false }
],
frameworks: ['jasmine-ajax', 'jasmine', '@angular-devkit/build-angular'],
proxies: {
'/base/assets/': '/base/assets/',
'/base/assets/': '/base/lib/process-services/assets/',
'/assets/adf-core/i18n/en.json': '/base/lib/core/i18n/en.json',
'/assets/adf-content-services/i18n/en.json': '/base/lib/content-services/i18n/en.json',
'/assets/adf-process-services/i18n/en.json': '/base/lib/process-services/i18n/en.json',

View File

@ -9,7 +9,7 @@
"rules": {
"adf-license-banner": [
true,
"lib/+(core|content-services|process-services|insights)/**/*.ts",
"lib/+(core|content-services|process-services-cloud|process-services|insights)/**/*.ts",
"./license-community.txt"
],
"align": [

14
package-lock.json generated
View File

@ -4910,7 +4910,7 @@
},
"css-select": {
"version": "1.2.0",
"resolved": "http://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz",
"resolved": "https://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz",
"integrity": "sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg=",
"dev": true,
"requires": {
@ -5408,7 +5408,7 @@
"dependencies": {
"domelementtype": {
"version": "1.1.3",
"resolved": "http://registry.npmjs.org/domelementtype/-/domelementtype-1.1.3.tgz",
"resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.1.3.tgz",
"integrity": "sha1-vSh3PiZCiBrsUVRJJCmcXNgiGFs=",
"dev": true
}
@ -6544,7 +6544,7 @@
},
"file-loader": {
"version": "1.1.11",
"resolved": "http://registry.npmjs.org/file-loader/-/file-loader-1.1.11.tgz",
"resolved": "https://registry.npmjs.org/file-loader/-/file-loader-1.1.11.tgz",
"integrity": "sha512-TGR4HU7HUsGg6GCOPJnFk06RhWgEWFLAGWiT6rcD+GRC2keU3s9RGJ+b3Z6/U73jwwNb2gKLJ7YCrp+jvU4ALg==",
"dev": true,
"requires": {
@ -8336,7 +8336,7 @@
},
"html-webpack-plugin": {
"version": "3.2.0",
"resolved": "http://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-3.2.0.tgz",
"resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-3.2.0.tgz",
"integrity": "sha1-sBq71yOsqqeze2r0SS69oD2d03s=",
"dev": true,
"requires": {
@ -8365,7 +8365,7 @@
},
"htmlparser2": {
"version": "3.3.0",
"resolved": "http://registry.npmjs.org/htmlparser2/-/htmlparser2-3.3.0.tgz",
"resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.3.0.tgz",
"integrity": "sha1-zHDQWln2VC5D8OaFyYLhTJJKnv4=",
"dev": true,
"requires": {
@ -8392,7 +8392,7 @@
},
"readable-stream": {
"version": "1.0.34",
"resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz",
"integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=",
"dev": true,
"requires": {
@ -13622,7 +13622,7 @@
},
"postcss-import": {
"version": "11.1.0",
"resolved": "http://registry.npmjs.org/postcss-import/-/postcss-import-11.1.0.tgz",
"resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-11.1.0.tgz",
"integrity": "sha512-5l327iI75POonjxkXgdRCUS+AlzAdBx4pOvMEhTKTCjb1p8IEeVR9yx3cPbmN7LIWJLbfnIXxAhoB4jpD0c/Cw==",
"dev": true,
"requires": {