Merge pull request #1573 from Alfresco/development

1.1.0
This commit is contained in:
Denys Vuika
2017-01-30 16:50:08 +00:00
committed by GitHub
384 changed files with 6997 additions and 4811 deletions
+1
View File
@@ -10,6 +10,7 @@ Please ask before on our gitter channel https://gitter.im/Alfresco/alfresco-ng2-
- [ ] New feature request - [ ] New feature request
- [ ] Bug - [ ] Bug
- [ ] Support request - [ ] Support request
- [ ] Documentation
``` ```
**Current behavior:** **Current behavior:**
<!-- Describe the current behavior. --> <!-- Describe the current behavior. -->
+14 -7
View File
@@ -32,24 +32,31 @@ env:
before_script: before_script:
- if ([ "$MODULE" != "ng2-alfresco-core" ]); then - if ([ "$MODULE" != "ng2-alfresco-core" ]); then
(cd ng2-components/ng2-alfresco-core; if [ ! -d "dist" ]; then npm install; fi; npm link); (cd ng2-components/ng2-alfresco-core; if [ ! -d "bundles" ]; then npm link; fi);
fi fi
- if ([ "$MODULE" == "ng2-alfresco-documentlist" ] || [ "$MODULE" == "ng2-alfresco-webscript" ] || [ "$MODULE" == "ng2-activiti-processlist" ] || [ "$MODULE" == "ng2-activiti-tasklist" ]); then - if ([ "$MODULE" == "ng2-alfresco-documentlist" ] || [ "$MODULE" == "ng2-alfresco-webscript" ] || [ "$MODULE" == "ng2-activiti-processlist" ] || [ "$MODULE" == "ng2-activiti-tasklist" ]); then
(cd ng2-components/ng2-alfresco-datatable; npm link ng2-alfresco-core; if [ ! -d "dist" ]; then npm install; fi; npm link); (cd ng2-components/ng2-alfresco-datatable; npm link ng2-alfresco-core; if [ ! -d "bundles" ]; then npm link; fi);
fi fi
- if ([ "$MODULE" == "ng2-activiti-tasklist" ] || [ "$MODULE" == "ng2-activiti-processlist" ]); then - if ([ "$MODULE" == "ng2-activiti-tasklist" ] || [ "$MODULE" == "ng2-activiti-processlist" ]); then
(cd ng2-components/ng2-activiti-form; npm link ng2-alfresco-core; if [ ! -d "dist" ]; then npm install; fi; npm link); (cd ng2-components/ng2-alfresco-datatable; npm link ng2-alfresco-core; if [ ! -d "bundles" ]; then npm link; fi);
(cd ng2-components/ng2-activiti-form; npm link ng2-alfresco-core; if [ ! -d "bundles" ]; then npm link; fi);
fi fi
- if ([ "$MODULE" == "ng2-activiti-processlist" ]); then - if ([ "$MODULE" == "ng2-activiti-processlist" ]); then
(cd ng2-components/ng2-activiti-tasklist; npm link ng2-alfresco-core; npm link ng2-alfresco-datatable; npm link ng2-activiti-form; if [ ! -d "dist" ]; then npm install; fi; npm link); (cd ng2-components/ng2-alfresco-datatable; npm link ng2-alfresco-core; if [ ! -d "bundles" ]; then npm link; fi);
(cd ng2-components/ng2-activiti-form; npm link ng2-alfresco-core; if [ ! -d "bundles" ]; then npm link; fi);
(cd ng2-components/ng2-activiti-tasklist; npm link ng2-alfresco-core; npm link ng2-alfresco-datatable; npm link ng2-activiti-form; if [ ! -d "bundles" ]; then npm link; fi);
fi fi
- if ([ "$MODULE" == "ng2-activiti-analytics" ]); then - if ([ "$MODULE" == "ng2-activiti-analytics" ]); then
(cd ng2-components/ng2-activiti-diagrams; npm link ng2-alfresco-core; if [ ! -d "dist" ]; then npm install; fi; npm link); (cd ng2-components/ng2-activiti-diagrams; npm link ng2-alfresco-core; if [ ! -d "bundles" ]; then npm link; fi);
fi
- if ([ "$MODULE" == "ng2-alfresco-search" ]); then
(cd ng2-components/ng2-alfresco-datatable; npm link ng2-alfresco-core; if [ ! -d "bundles" ]; then npm link; fi);
(cd ng2-components/ng2-alfresco-documentlist; npm link ng2-alfresco-core; npm link ng2-alfresco-datatable; if [ ! -d "bundles" ]; then npm link; fi);
fi fi
- cd ng2-components/$MODULE; - cd ng2-components/$MODULE;
- npm run travis; - npm run travis;
- npm install; #npm insall run install, build and tests
script: npm run test script: npm install
# Send coverage data to Coveralls # Send coverage data to Coveralls
after_success: after_success:
bash <(curl -s https://codecov.io/bash) -X gcov bash <(curl -s https://codecov.io/bash) -X gcov
+4 -2
View File
@@ -6,6 +6,8 @@
"**/.hg": true, "**/.hg": true,
"**/.DS_Store": true, "**/.DS_Store": true,
"**/*.js": { "when": "$(basename).ts"}, "**/*.js": { "when": "$(basename).ts"},
"**/*.js.map": { "when": "$(basename)"} "**/*.js.map": { "when": "$(basename)"},
} "**/*.d.ts": { "when": "$(basename).ts"}
},
"editor.renderIndentGuides": true
} }
+6
View File
@@ -56,6 +56,12 @@ To view the complete list of all the components that you can use to build your c
You can browse all the components at the following [page](http://devproducts.alfresco.com/). You can browse all the components at the following [page](http://devproducts.alfresco.com/).
## Demo Application
A separate application showcasing integration of components can be found [here](https://github.com/Alfresco/alfresco-ng2-components/tree/master/demo-shell-ng2).
You will find examples of basic interaction for both BPM and ECM sets of widgets.
## Yeoman generators ## Yeoman generators
To speed up the development of your Alfresco Angular 2 application, or Alfresco Angular 2 component, use one of the Yeoman generators. To speed up the development of your Alfresco Angular 2 application, or Alfresco Angular 2 component, use one of the Yeoman generators.
+17 -15
View File
@@ -1,4 +1,9 @@
# Test against this version of Node.js # Test against this version of Node.js
branches:
only:
- master
- development
- dev-build-test
environment: environment:
nodejs_version: "5" nodejs_version: "5"
@@ -25,22 +30,19 @@ install:
# Get the latest stable version of Node.js or io.js # Get the latest stable version of Node.js or io.js
- ps: Install-Product node $env:nodejs_version - ps: Install-Product node $env:nodejs_version
# install module # install module
- IF %COMPONENT_NAME% NEQ ng2-alfresco-core (cd ng2-components/ng2-alfresco-core && npm install && npm link && cd ../../) - IF %COMPONENT_NAME% NEQ ng2-alfresco-core (cd ng2-components/ng2-alfresco-core && npm link && cd ../../)
- IF %COMPONENT_NAME% EQU ng2-alfresco-documentlist (cd ng2-components/ng2-alfresco-datatable && npm link ng2-alfresco-core && npm install && npm link && cd ../../) - IF %COMPONENT_NAME% EQU ng2-alfresco-documentlist (cd ng2-components/ng2-alfresco-datatable && npm link ng2-alfresco-core && npm link && cd ../../)
- IF %COMPONENT_NAME% EQU ng2-activiti-processlist (cd ng2-components/ng2-alfresco-datatable && npm link ng2-alfresco-core && npm install && npm link && cd ../../) - IF %COMPONENT_NAME% EQU ng2-alfresco-search (cd ng2-components/ng2-alfresco-datatable && npm link ng2-alfresco-core && npm link && cd ../../)
- IF %COMPONENT_NAME% EQU ng2-activiti-processlist (cd ng2-components/ng2-activiti-form && npm link ng2-alfresco-core && npm install && npm link && cd ../../) - IF %COMPONENT_NAME% EQU ng2-alfresco-search (cd ng2-components/ng2-alfresco-documentlist && npm link ng2-alfresco-core && npm link ng2-alfresco-datatable && npm link && cd ../../)
- IF %COMPONENT_NAME% EQU ng2-activiti-processlist (cd ng2-components/ng2-activiti-tasklist && npm link ng2-alfresco-core && npm link ng2-alfresco-datatable && npm link ng2-activiti-form && npm install && npm link && cd ../../) - IF %COMPONENT_NAME% EQU ng2-activiti-processlist (cd ng2-components/ng2-alfresco-datatable && npm link ng2-alfresco-core && npm link && cd ../../)
- IF %COMPONENT_NAME% EQU ng2-activiti-tasklist (cd ng2-components/ng2-alfresco-datatable && npm link ng2-alfresco-core && npm install && npm link && cd ../../) - IF %COMPONENT_NAME% EQU ng2-activiti-processlist (cd ng2-components/ng2-activiti-form && npm link ng2-alfresco-core && npm link && cd ../../)
- IF %COMPONENT_NAME% EQU ng2-activiti-tasklist (cd ng2-components/ng2-activiti-form && npm link ng2-alfresco-core && npm install && npm link && cd ../../) - IF %COMPONENT_NAME% EQU ng2-activiti-processlist (cd ng2-components/ng2-activiti-tasklist && npm link ng2-alfresco-core && npm link ng2-alfresco-datatable && npm link ng2-activiti-form && npm link && cd ../../)
- IF %COMPONENT_NAME% EQU ng2-alfresco-webscript (cd ng2-components/ng2-alfresco-datatable && npm link ng2-alfresco-core && npm install && npm link && cd ../../) - IF %COMPONENT_NAME% EQU ng2-activiti-tasklist (cd ng2-components/ng2-alfresco-datatable && npm link ng2-alfresco-core && npm link && cd ../../)
- IF %COMPONENT_NAME% EQU ng2-activiti-analytics (cd ng2-components/ng2-activiti-diagrams && npm link link ng2-alfresco-core && npm install && npm link && cd ../../) - IF %COMPONENT_NAME% EQU ng2-activiti-tasklist (cd ng2-components/ng2-activiti-form && npm link ng2-alfresco-core && npm link && cd ../../)
- IF %COMPONENT_NAME% EQU ng2-alfresco-webscript (cd ng2-components/ng2-alfresco-datatable && npm link ng2-alfresco-core && npm link && cd ../../)
- IF %COMPONENT_NAME% EQU ng2-activiti-analytics (cd ng2-components/ng2-activiti-diagrams && npm link link ng2-alfresco-core && npm link && cd ../../)
- cd ng2-components/%COMPONENT_NAME% - cd ng2-components/%COMPONENT_NAME%
- IF %COMPONENT_NAME% NEQ ng2-alfresco-core (npm link ng2-alfresco-core) - npm run travis
- IF %COMPONENT_NAME% EQU ng2-alfresco-documentlist (npm link ng2-alfresco-datatable)
- IF %COMPONENT_NAME% EQU ng2-activiti-processlist (npm link ng2-alfresco-datatable && npm link ng2-activiti-form && npm link ng2-activiti-tasklist)
- IF %COMPONENT_NAME% EQU ng2-activiti-tasklist (npm link ng2-alfresco-datatable && npm link ng2-activiti-form)
- IF %COMPONENT_NAME% EQU ng2-alfresco-webscript (npm link ng2-alfresco-datatable)
- IF %COMPONENT_NAME% EQU ng2-activiti-analytics (npm link ng2-activiti-diagrams)
# Post-install test scripts. # Post-install test scripts.
test_script: test_script:
+12 -6
View File
@@ -12,9 +12,11 @@
## Installing ## Installing
To correctly use this demo check that on your machine is running Node version 6.9.2 LTS or higher. To correctly use this demo check that on your machine is running [Node](https://nodejs.org/en/) version 6.9.2 LTS or higher.
```sh ```sh
git clone https://github.com/Alfresco/alfresco-ng2-components.git
cd alfresco-ng2-components/demo-shell-ng2/
npm install npm install
``` ```
@@ -28,22 +30,26 @@ This command compiles and starts the project in watch mode.
Browser will automatically reload upon changes. Browser will automatically reload upon changes.
Upon start you can navigate to `http://localhost:3000` with your preferred browser. Upon start you can navigate to `http://localhost:3000` with your preferred browser.
### Important notes
This script is recommended for development environment and not suited for headless servers and network access.
## Production build ## Production build
```sh ```sh
npm run build npm run build
npm run start:dist
``` ```
This command builds broject in `production` mode. This command builds broject in `production` mode.
All output is placed to `dist` folder and can be served your preferred web server. All output is placed to `dist` folder and can be served your preferred web server.
You should need no additional files outside the `dist` folder. You should need no additional files outside the `dist` folder.
In order to quickly test the output you can use the [wsrv](https://www.npmjs.com/package/wsrv) tool (lightweight web server): ### Important notes
```sh By default demo application is configured to use [wsrv](https://www.npmjs.com/package/wsrv) tool (lightweight web server)
npm install -g wsrv to serve production build output. It will be running at `0.0.0.0` address with port `3000` and allow you accessing your application
wsrv -s -o dist/ via network. However you can use any web server of your choice in production.
```
## Development branch build ## Development branch build
+1 -1
View File
@@ -1,6 +1,6 @@
<!-- Always shows a header, even in smaller screens. --> <!-- Always shows a header, even in smaller screens. -->
<div mdl class="mdl-layout mdl-js-layout mdl-layout--fixed-header"> <div mdl class="mdl-layout mdl-js-layout mdl-layout--fixed-header">
<header class="mdl-layout__header main_header_adf" [ngClass]="{hide: isLoginPage()}"> <header class="mdl-layout__header main_header_adf" [ngClass]="{hide: isAPageWithHeaderBar()}">
<div class="mdl-button-padding mdl-layout__header-row"> <div class="mdl-button-padding mdl-layout__header-row">
<!-- User Info --> <!-- User Info -->
+21 -37
View File
@@ -17,13 +17,7 @@
import { Component } from '@angular/core'; import { Component } from '@angular/core';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { AlfrescoTranslationService, AlfrescoAuthenticationService, AlfrescoSettingsService, StorageService, LogService } from 'ng2-alfresco-core';
import {
AlfrescoTranslationService,
AlfrescoAuthenticationService,
AlfrescoSettingsService,
StorageService
} from 'ng2-alfresco-core';
declare var document: any; declare var document: any;
@@ -38,44 +32,34 @@ export class AppComponent {
ecmHost: string = 'http://' + window.location.hostname + ':8080'; ecmHost: string = 'http://' + window.location.hostname + ':8080';
bpmHost: string = 'http://' + window.location.hostname + ':9999'; bpmHost: string = 'http://' + window.location.hostname + ':9999';
constructor(public auth: AlfrescoAuthenticationService, constructor(private authService: AlfrescoAuthenticationService,
public router: Router, private router: Router,
public alfrescoSettingsService: AlfrescoSettingsService, private settingsService: AlfrescoSettingsService,
private translate: AlfrescoTranslationService, private translateService: AlfrescoTranslationService,
private storage: StorageService) { private storage: StorageService,
private logService: LogService) {
this.setEcmHost(); this.setEcmHost();
this.setBpmHost(); this.setBpmHost();
this.setProvider(); this.setProvider();
if (translate) { if (translateService) {
if (process.env.ENV === 'production') { if (process.env.ENV === 'production') {
translate.addTranslationFolder('custom', 'i18n/custom-translation'); translateService.addTranslationFolder('custom', 'i18n/custom-translation');
translate.addTranslationFolder('ng2-alfresco-login', 'i18n/custom-translation/alfresco-login'); translateService.addTranslationFolder('ng2-alfresco-login', 'i18n/custom-translation/alfresco-login');
} else { } else {
translate.addTranslationFolder('custom', 'custom-translation'); translateService.addTranslationFolder('custom', 'custom-translation');
translate.addTranslationFolder('ng2-alfresco-login', 'custom-translation/alfresco-login'); translateService.addTranslationFolder('ng2-alfresco-login', 'custom-translation/alfresco-login');
} }
} }
} }
isLoggedIn(): boolean { isAPageWithHeaderBar(): boolean {
this.redirectToLoginPageIfNotLoggedIn();
return this.auth.isLoggedIn();
}
redirectToLoginPageIfNotLoggedIn(): void {
if (!this.isLoginPage() && !this.auth.isLoggedIn()) {
this.router.navigate(['/login']);
}
}
isLoginPage(): boolean {
return location.pathname === '/login' || location.pathname === '/settings'; return location.pathname === '/login' || location.pathname === '/settings';
} }
onLogout(event) { onLogout(event) {
event.preventDefault(); event.preventDefault();
this.auth.logout() this.authService.logout()
.subscribe( .subscribe(
() => { () => {
this.navigateToLogin(); this.navigateToLogin();
@@ -84,7 +68,7 @@ export class AppComponent {
if (error && error.response && error.response.status === 401) { if (error && error.response && error.response.status === 401) {
this.navigateToLogin(); this.navigateToLogin();
} else { } else {
console.error('An unknown error occurred while logging out', error); this.logService.error('An unknown error occurred while logging out', error);
this.navigateToLogin(); this.navigateToLogin();
} }
} }
@@ -107,7 +91,7 @@ export class AppComponent {
} }
changeLanguage(lang: string) { changeLanguage(lang: string) {
this.translate.use(lang); this.translateService.use(lang);
this.hideDrawer(); this.hideDrawer();
} }
@@ -118,25 +102,25 @@ export class AppComponent {
private setEcmHost() { private setEcmHost() {
if (this.storage.hasItem(`ecmHost`)) { if (this.storage.hasItem(`ecmHost`)) {
this.alfrescoSettingsService.ecmHost = this.storage.getItem(`ecmHost`); this.settingsService.ecmHost = this.storage.getItem(`ecmHost`);
this.ecmHost = this.storage.getItem(`ecmHost`); this.ecmHost = this.storage.getItem(`ecmHost`);
} else { } else {
this.alfrescoSettingsService.ecmHost = this.ecmHost; this.settingsService.ecmHost = this.ecmHost;
} }
} }
private setBpmHost() { private setBpmHost() {
if (this.storage.hasItem(`bpmHost`)) { if (this.storage.hasItem(`bpmHost`)) {
this.alfrescoSettingsService.bpmHost = this.storage.getItem(`bpmHost`); this.settingsService.bpmHost = this.storage.getItem(`bpmHost`);
this.bpmHost = this.storage.getItem(`bpmHost`); this.bpmHost = this.storage.getItem(`bpmHost`);
} else { } else {
this.alfrescoSettingsService.bpmHost = this.bpmHost; this.settingsService.bpmHost = this.bpmHost;
} }
} }
private setProvider() { private setProvider() {
if (this.storage.hasItem(`providers`)) { if (this.storage.hasItem(`providers`)) {
this.alfrescoSettingsService.setProviders(this.storage.getItem(`providers`)); this.settingsService.setProviders(this.storage.getItem(`providers`));
} }
} }
} }
+3 -4
View File
@@ -32,7 +32,6 @@ import { ActivitiTaskListModule } from 'ng2-activiti-tasklist';
import { ActivitiProcessListModule } from 'ng2-activiti-processlist'; import { ActivitiProcessListModule } from 'ng2-activiti-processlist';
import { UserInfoComponentModule } from 'ng2-alfresco-userinfo'; import { UserInfoComponentModule } from 'ng2-alfresco-userinfo';
import { AnalyticsModule } from 'ng2-activiti-analytics'; import { AnalyticsModule } from 'ng2-activiti-analytics';
import { AppComponent } from './app.component'; import { AppComponent } from './app.component';
import { routing } from './app.routes'; import { routing } from './app.routes';
import { CustomEditorsModule } from './components/activiti/custom-editor/custom-editor.component'; import { CustomEditorsModule } from './components/activiti/custom-editor/custom-editor.component';
@@ -59,13 +58,13 @@ import {
BrowserModule, BrowserModule,
routing, routing,
CoreModule.forRoot(), CoreModule.forRoot(),
LoginModule, LoginModule.forRoot(),
SearchModule.forRoot(), SearchModule.forRoot(),
DataTableModule, DataTableModule.forRoot(),
DocumentListModule.forRoot(), DocumentListModule.forRoot(),
UploadModule.forRoot(), UploadModule.forRoot(),
TagModule.forRoot(), TagModule.forRoot(),
WebScriptModule, WebScriptModule.forRoot(),
ViewerModule.forRoot(), ViewerModule.forRoot(),
ActivitiFormModule.forRoot(), ActivitiFormModule.forRoot(),
ActivitiTaskListModule.forRoot(), ActivitiTaskListModule.forRoot(),
@@ -18,6 +18,7 @@
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core';
import { Http } from '@angular/http'; import { Http } from '@angular/http';
import { ObjectDataTableAdapter } from 'ng2-alfresco-datatable'; import { ObjectDataTableAdapter } from 'ng2-alfresco-datatable';
import { LogService } from 'ng2-alfresco-core';
@Component({ @Component({
selector: 'about-page', selector: 'about-page',
@@ -27,31 +28,31 @@ export class AboutComponent implements OnInit {
data: ObjectDataTableAdapter; data: ObjectDataTableAdapter;
constructor(private http: Http) { constructor(private http: Http,
private logService: LogService) {
} }
ngOnInit() { ngOnInit() {
this.http.get('/versions.json').subscribe(response => { this.http.get('/versions.json').subscribe(response => {
var regexp = new RegExp("^(ng2-activiti|ng2-alfresco|alfresco-)", 'g'); let regexp = new RegExp('^(ng2-activiti|ng2-alfresco|alfresco-)', 'g');
var alfrescoPackages = Object.keys(response.json().dependencies).filter(function (val) { let alfrescoPackages = Object.keys(response.json().dependencies).filter((val) => {
console.log(val); this.logService.log(val);
return regexp.test(val); return regexp.test(val);
}); });
let alfrescoPackagesTableRappresentation = []; let alfrescoPackagesTableRappresentation = [];
alfrescoPackages.forEach((val)=> { alfrescoPackages.forEach((val) => {
console.log(response.json().dependencies[val]); this.logService.log(response.json().dependencies[val]);
alfrescoPackagesTableRappresentation.push({name:val,version:response.json().dependencies[val].version}); alfrescoPackagesTableRappresentation.push({name: val, version: response.json().dependencies[val].version});
}); });
console.log(alfrescoPackagesTableRappresentation); this.logService.log(alfrescoPackagesTableRappresentation);
this.data = new ObjectDataTableAdapter(alfrescoPackagesTableRappresentation, [ this.data = new ObjectDataTableAdapter(alfrescoPackagesTableRappresentation, [
{type: 'text', key: 'name', title: 'Name', sortable: true}, {type: 'text', key: 'name', title: 'Name', sortable: true},
{type: 'text', key: 'version', title: 'Version', sortable: true} {type: 'text', key: 'version', title: 'Version', sortable: true}
]); ]);
}); });
} }
} }
@@ -5,9 +5,9 @@
<!-- TABS --> <!-- TABS -->
<div class="mdl-layout__tab-bar mdl-js-ripple-effect"> <div class="mdl-layout__tab-bar mdl-js-ripple-effect">
<a id="tasks-header" href="#tasks" class="mdl-layout__tab is-active">TASKS</a> <a id="tasks-header" href="#tasks" class="mdl-layout__tab" [class.is-active]="activeTab === 'tasks'" (click)="activeTab = 'tasks'">TASKS</a>
<a id="processes-header" href="#processes" class="mdl-layout__tab" (click)="activeProcess()">PROCESSES</a> <a id="processes-header" href="#processes" class="mdl-layout__tab" [class.is-active]="activeTab === 'processes'" (click)="activeTab = 'processes'">PROCESSES</a>
<a id="report-header" href="#report" class="mdl-layout__tab" (click)="activeReports()">REPORTS</a> <a id="report-header" href="#report" class="mdl-layout__tab" [class.is-active]="activeTab === 'reports'" (click)="activeTab = 'reports'">REPORTS</a>
</div> </div>
</header> </header>
@@ -15,7 +15,7 @@
<!-- TASKS COMPONENT --> <!-- TASKS COMPONENT -->
<section class="mdl-layout__tab-panel is-active" id="tasks"> <section class="mdl-layout__tab-panel" [class.is-active]="activeTab === 'tasks'" id="tasks">
<div class="page-content"> <div class="page-content">
<div class="mdl-grid"> <div class="mdl-grid">
<div class="mdl-cell mdl-cell--2-col task-column mdl-shadow--2dp"> <div class="mdl-cell mdl-cell--2-col task-column mdl-shadow--2dp">
@@ -36,6 +36,7 @@
[state]="taskFilter.filter.state" [state]="taskFilter.filter.state"
[sort]="taskFilter.filter.sort" [sort]="taskFilter.filter.sort"
[data]="dataTasks" [data]="dataTasks"
[landingTaskId]="currentTaskId"
(rowClick)="onTaskRowClick($event)" (onSuccess)="onSuccessTaskList($event)" (rowClick)="onTaskRowClick($event)" (onSuccess)="onSuccessTaskList($event)"
#activititasklist></activiti-tasklist> #activititasklist></activiti-tasklist>
</div> </div>
@@ -43,6 +44,7 @@
<span><h5>Task Details</h5></span> <span><h5>Task Details</h5></span>
<hr> <hr>
<activiti-task-details [taskId]="currentTaskId" (formCompleted)="onFormCompleted($event)" <activiti-task-details [taskId]="currentTaskId" (formCompleted)="onFormCompleted($event)"
(taskCreated)="onTaskCreated($event)"
#activitidetails></activiti-task-details> #activitidetails></activiti-task-details>
</div> </div>
</div> </div>
@@ -52,8 +54,8 @@
<!-- PROCESS COMPONENT --> <!-- PROCESS COMPONENT -->
<section class="mdl-layout__tab-panel" id="processes"> <section class="mdl-layout__tab-panel" [class.is-active]="activeTab === 'processes'" id="processes">
<div class="page-content" *ngIf="processTabActivie"> <div class="page-content" *ngIf="activeTab === 'processes'">
<div class="page-content"> <div class="page-content">
<div class="mdl-grid"> <div class="mdl-grid">
<div class="mdl-cell mdl-cell--2-col task-column mdl-shadow--2dp"> <div class="mdl-cell mdl-cell--2-col task-column mdl-shadow--2dp">
@@ -66,7 +68,7 @@
(filterClick)="onProcessFilterClick($event)" (filterClick)="onProcessFilterClick($event)"
(onSuccess)="onSuccessProcessFilterList($event)"></activiti-process-instance-filters> (onSuccess)="onSuccessProcessFilterList($event)"></activiti-process-instance-filters>
</div> </div>
<div class="mdl-cell mdl-cell--3-col task-column list-column mdl-shadow--2dp"> <div class="mdl-cell mdl-cell--3-col task-column list-column mdl-shadow--2dp" *ngIf="processFilter && !isStartProcessMode()">
<span><h5>Process List</h5></span> <span><h5>Process List</h5></span>
<hr> <hr>
<activiti-process-instance-list *ngIf="processFilter?.hasFilter()" [appId]="processFilter.appId" <activiti-process-instance-list *ngIf="processFilter?.hasFilter()" [appId]="processFilter.appId"
@@ -78,15 +80,19 @@
(rowClick)="onProcessRowClick($event)" (rowClick)="onProcessRowClick($event)"
(onSuccess)="onSuccessProcessList($event)"></activiti-process-instance-list> (onSuccess)="onSuccessProcessList($event)"></activiti-process-instance-list>
</div> </div>
<div class="mdl-cell mdl-cell--7-col task-column mdl-shadow--2dp" *ngIf="!isStartProcessMode()"> <div class="mdl-cell task-column mdl-shadow--2dp" *ngIf="!isStartProcessMode()"
[class.mdl-cell--7-col]="processFilter && !isStartProcessMode()"
[class.mdl-cell--10-col]="!processFilter || isStartProcessMode()">
<span><h5>Process Details</h5></span> <span><h5>Process Details</h5></span>
<hr> <hr>
<activiti-process-instance-details [processInstanceId]="currentProcessInstanceId" <activiti-process-instance-details
(activitiprocesslist)="taskFormCompleted()" [processInstanceId]="currentProcessInstanceId"
(processCancelled)="processCancelled()"></activiti-process-instance-details> (processCancelled)="processCancelled()"
(taskClick)="onProcessDetailsTaskClick($event)">
</activiti-process-instance-details>
</div> </div>
<div class="mdl-cell mdl-cell--7-col task-column" *ngIf="isStartProcessMode()"> <div class="mdl-cell mdl-cell--10-col task-column mdl-shadow--2dp" *ngIf="isStartProcessMode()">
<span>Start Process</span> <span><h5>Start Process</h5></span>
<hr> <hr>
<activiti-start-process [appId]="appId" (start)="onStartProcessInstance($event)"></activiti-start-process> <activiti-start-process [appId]="appId" (start)="onStartProcessInstance($event)"></activiti-start-process>
</div> </div>
@@ -98,8 +104,8 @@
<!-- ANALYTICS COMPONENT --> <!-- ANALYTICS COMPONENT -->
<section class="mdl-layout__tab-panel" id="report"> <section class="mdl-layout__tab-panel" [class.is-active]="activeTab === 'reports'" id="report">
<div class="page-content" *ngIf="reportsTabActivie"> <div class="page-content" *ngIf="activeTab === 'reports'">
<div class="mdl-grid"> <div class="mdl-grid">
<div class="mdl-cell mdl-cell--4-col task-column mdl-shadow--2dp"> <div class="mdl-cell mdl-cell--4-col task-column mdl-shadow--2dp">
<span><h5>Report List</h5></span> <span><h5>Report List</h5></span>
@@ -19,15 +19,16 @@ import { AfterViewInit, Component, ElementRef, Input, ViewChild } from '@angular
import { import {
ActivitiApps, ActivitiApps,
ActivitiFilters, ActivitiFilters,
ActivitiTaskDetails,
ActivitiTaskList, ActivitiTaskList,
FilterRepresentationModel FilterRepresentationModel,
TaskDetailsEvent
} from 'ng2-activiti-tasklist'; } from 'ng2-activiti-tasklist';
import { import {
ActivitiProcessFilters, ActivitiProcessFilters,
ActivitiProcessInstanceDetails, ActivitiProcessInstanceDetails,
ActivitiProcessInstanceListComponent, ActivitiProcessInstanceListComponent,
ActivitiStartProcessInstance, ActivitiStartProcessInstance,
FilterProcessRepresentationModel,
ProcessInstance ProcessInstance
} from 'ng2-activiti-processlist'; } from 'ng2-activiti-processlist';
import { AnalyticsReportListComponent } from 'ng2-activiti-analytics'; import { AnalyticsReportListComponent } from 'ng2-activiti-analytics';
@@ -35,10 +36,11 @@ import { ActivatedRoute } from '@angular/router';
import { Subscription } from 'rxjs/Rx'; import { Subscription } from 'rxjs/Rx';
import { import {
ObjectDataTableAdapter, ObjectDataTableAdapter,
ObjectDataRow,
DataSorting DataSorting
} from 'ng2-alfresco-datatable'; } from 'ng2-alfresco-datatable';
import { AlfrescoApiService } from 'ng2-alfresco-core'; import { AlfrescoApiService } from 'ng2-alfresco-core';
import { FormRenderingService } from 'ng2-activiti-form'; import { FormService, FormRenderingService, FormEvent, FormFieldEvent } from 'ng2-activiti-form';
import { /*CustomEditorComponent*/ CustomStencil01 } from './custom-editor/custom-editor.component'; import { /*CustomEditorComponent*/ CustomStencil01 } from './custom-editor/custom-editor.component';
declare var componentHandler; declare var componentHandler;
@@ -52,18 +54,12 @@ const currentProcessIdNew = '__NEW__';
}) })
export class ActivitiDemoComponent implements AfterViewInit { export class ActivitiDemoComponent implements AfterViewInit {
@ViewChild(ActivitiApps)
activitiapps: ActivitiApps;
@ViewChild(ActivitiFilters) @ViewChild(ActivitiFilters)
activitifilter: ActivitiFilters; activitifilter: ActivitiFilters;
@ViewChild(ActivitiTaskList) @ViewChild(ActivitiTaskList)
activititasklist: ActivitiTaskList; activititasklist: ActivitiTaskList;
@ViewChild(ActivitiTaskDetails)
activitidetails: ActivitiTaskDetails;
@ViewChild(ActivitiProcessFilters) @ViewChild(ActivitiProcessFilters)
activitiprocessfilter: ActivitiProcessFilters; activitiprocessfilter: ActivitiProcessFilters;
@@ -80,7 +76,7 @@ export class ActivitiDemoComponent implements AfterViewInit {
analyticsreportlist: AnalyticsReportListComponent; analyticsreportlist: AnalyticsReportListComponent;
@Input() @Input()
appId: number; appId: number = null;
layoutType: string; layoutType: string;
currentTaskId: string; currentTaskId: string;
@@ -89,13 +85,11 @@ export class ActivitiDemoComponent implements AfterViewInit {
taskSchemaColumns: any [] = []; taskSchemaColumns: any [] = [];
processSchemaColumns: any [] = []; processSchemaColumns: any [] = [];
processTabActivie: boolean = false; activeTab: string = 'tasks'; // tasks|processes|reports
reportsTabActivie: boolean = false;
taskFilter: FilterRepresentationModel; taskFilter: FilterRepresentationModel;
report: any; report: any;
processFilter: FilterRepresentationModel; processFilter: FilterProcessRepresentationModel;
sub: Subscription; sub: Subscription;
@@ -105,7 +99,8 @@ export class ActivitiDemoComponent implements AfterViewInit {
constructor(private elementRef: ElementRef, constructor(private elementRef: ElementRef,
private route: ActivatedRoute, private route: ActivatedRoute,
private apiService: AlfrescoApiService, private apiService: AlfrescoApiService,
private formRenderingService: FormRenderingService) { private formRenderingService: FormRenderingService,
private formService: FormService) {
this.dataTasks = new ObjectDataTableAdapter( this.dataTasks = new ObjectDataTableAdapter(
[], [],
[ [
@@ -122,12 +117,21 @@ export class ActivitiDemoComponent implements AfterViewInit {
{type: 'text', key: 'started', title: 'Started', cssClass: 'hidden', sortable: true} {type: 'text', key: 'started', title: 'Started', cssClass: 'hidden', sortable: true}
] ]
); );
this.dataProcesses.setSorting(new DataSorting('started', 'desc'));
// Uncomment this line to replace all 'text' field editors with custom component // Uncomment this line to replace all 'text' field editors with custom component
// formRenderingService.setComponentTypeResolver('text', () => CustomEditorComponent, true); // formRenderingService.setComponentTypeResolver('text', () => CustomEditorComponent, true);
// Uncomment this line to map 'custom_stencil_01' to local editor component // Uncomment this line to map 'custom_stencil_01' to local editor component
formRenderingService.setComponentTypeResolver('custom_stencil_01', () => CustomStencil01, true); formRenderingService.setComponentTypeResolver('custom_stencil_01', () => CustomStencil01, true);
formService.formLoaded.subscribe((e: FormEvent) => {
console.log(`Form loaded: ${e.form.id}`);
});
formService.formFieldValueChanged.subscribe((e: FormFieldEvent) => {
console.log(`Field value changed. Form: ${e.form.id}, Field: ${e.field.id}, Value: ${e.field.value}`);
});
} }
ngOnInit() { ngOnInit() {
@@ -150,7 +154,9 @@ export class ActivitiDemoComponent implements AfterViewInit {
} }
onTaskFilterClick(event: FilterRepresentationModel) { onTaskFilterClick(event: FilterRepresentationModel) {
this.taskFilter = event; if(event){
this.taskFilter = event;
}
} }
onReportClick(event: any) { onReportClick(event: any) {
@@ -171,11 +177,12 @@ export class ActivitiDemoComponent implements AfterViewInit {
this.currentTaskId = this.activititasklist.getCurrentId(); this.currentTaskId = this.activititasklist.getCurrentId();
} }
onProcessFilterClick(event: FilterRepresentationModel) { onProcessFilterClick(event: FilterProcessRepresentationModel) {
this.currentProcessInstanceId = null;
this.processFilter = event; this.processFilter = event;
} }
onSuccessProcessFilterList(event: any) { onSuccessProcessFilterList() {
this.processFilter = this.activitiprocessfilter.getCurrentFilter(); this.processFilter = this.activitiprocessfilter.getCurrentFilter();
} }
@@ -196,13 +203,15 @@ export class ActivitiDemoComponent implements AfterViewInit {
} }
navigateStartProcess() { navigateStartProcess() {
this.resetProcessFilters();
this.reloadProcessFilters();
this.currentProcessInstanceId = currentProcessIdNew; this.currentProcessInstanceId = currentProcessIdNew;
} }
onStartProcessInstance(instance: ProcessInstance) { onStartProcessInstance(instance: ProcessInstance) {
this.currentProcessInstanceId = instance.id; this.currentProcessInstanceId = instance.id;
this.activitiStartProcess.reset(); this.activitiStartProcess.reset();
this.activitiprocesslist.reload(); this.resetProcessFilters();
} }
isStartProcessMode() { isStartProcessMode() {
@@ -218,15 +227,16 @@ export class ActivitiDemoComponent implements AfterViewInit {
this.activitiprocesslist.reload(); this.activitiprocesslist.reload();
} }
taskFormCompleted(data: any) {
this.activitiprocesslist.reload();
}
onFormCompleted(form) { onFormCompleted(form) {
this.activititasklist.reload(); this.activititasklist.reload();
this.currentTaskId = null; this.currentTaskId = null;
} }
onTaskCreated(data: any) {
this.currentTaskId = data.parentTaskId;
this.activititasklist.reload();
}
ngAfterViewInit() { ngAfterViewInit() {
// workaround for MDL issues with dynamic components // workaround for MDL issues with dynamic components
if (componentHandler) { if (componentHandler) {
@@ -236,14 +246,6 @@ export class ActivitiDemoComponent implements AfterViewInit {
this.loadStencilScriptsInPageFromActiviti(); this.loadStencilScriptsInPageFromActiviti();
} }
activeProcess() {
this.processTabActivie = true;
}
activeReports() {
this.reportsTabActivie = true;
}
loadStencilScriptsInPageFromActiviti() { loadStencilScriptsInPageFromActiviti() {
this.apiService.getInstance().activiti.scriptFileApi.getControllers().then(response => { this.apiService.getInstance().activiti.scriptFileApi.getControllers().then(response => {
if (response) { if (response) {
@@ -255,4 +257,26 @@ export class ActivitiDemoComponent implements AfterViewInit {
}); });
} }
onProcessDetailsTaskClick(event: TaskDetailsEvent) {
event.preventDefault();
this.activeTab = 'tasks';
let processTaskDataRow = new ObjectDataRow({
id: event.value.id,
name: event.value.name || 'No name',
created: event.value.created
});
this.activitifilter.selectFilter(null);
this.dataTasks.setRows([processTaskDataRow]);
this.activititasklist.selectTask(event.value.id);
this.currentTaskId = event.value.id;
}
private resetProcessFilters() {
this.processFilter = null;
}
private reloadProcessFilters() {
this.activitiprocessfilter.selectFilter(null);
}
} }
@@ -1,12 +1,11 @@
<div class="container"> <div class="container">
<alfresco-upload-drag-area <alfresco-upload-drag-area
[rootFolderId]="uploadRootFolderId" [rootFolderId]="documentList.currentFolderId"
[currentFolderPath]="uploadFolderPath"
[versioning] = "versioning" [versioning] = "versioning"
(onSuccess)="documentList.reload()"> (onSuccess)="documentList.reload()">
<alfresco-document-list-breadcrumb <alfresco-document-list-breadcrumb
[currentFolderPath]="currentPath" [target]="documentList"
(pathChanged)="onBreadcrumbPathChanged($event)" *ngIf="!currentFolderId"> [folderNode]="documentList.folderNode">
</alfresco-document-list-breadcrumb> </alfresco-document-list-breadcrumb>
<div *ngIf="errorMessage" class="error-message"> <div *ngIf="errorMessage" class="error-message">
<button (click)="resetError()" class="mdl-button mdl-js-button mdl-button--icon"> <button (click)="resetError()" class="mdl-button mdl-js-button mdl-button--icon">
@@ -16,22 +15,12 @@
</div> </div>
<alfresco-document-list <alfresco-document-list
#documentList #documentList
[rootFolderId]="rootFolderId"
[currentFolderPath]="currentPath"
[currentFolderId]="currentFolderId" [currentFolderId]="currentFolderId"
[contextMenuActions]="true" [contextMenuActions]="true"
[contentActions]="true" [contentActions]="true"
(error)="onNavigationError($event)" (error)="onNavigationError($event)"
(success)="resetError()" (success)="resetError()"
(preview)="showFile($event)" (preview)="showFile($event)">
(folderChange)="onFolderChanged($event)">
<!--
<empty-folder-content>
<template>
<h1>Sorry, no content here</h1>
</template>
</empty-folder-content>
-->
<content-columns> <content-columns>
<content-column key="$thumbnail" type="image"></content-column> <content-column key="$thumbnail" type="image"></content-column>
<content-column <content-column
@@ -39,13 +28,13 @@
key="name" key="name"
sortable="true" sortable="true"
class="full-width ellipsis-cell"> class="full-width ellipsis-cell">
<!-- Example of using custom column template -->
<!--
<template let-entry="$implicit">
<span>Hi! {{entry.data.getValue(entry.row, entry.col)}}</span>
</template>
-->
</content-column> </content-column>
<!--
<content-column
title="Type"
source="content.mimeType">
</content-column>
-->
<content-column <content-column
title="{{'DOCUMENT_LIST.COLUMNS.CREATED_BY' | translate}}" title="{{'DOCUMENT_LIST.COLUMNS.CREATED_BY' | translate}}"
key="createdByUser.displayName" key="createdByUser.displayName"
@@ -107,43 +96,11 @@
</content-action> </content-action>
</content-actions> </content-actions>
</alfresco-document-list> </alfresco-document-list>
<alfresco-pagination
[provider]="documentList.data"
[supportedPageSizes]="[5, 10, 15, 20]">
</alfresco-pagination>
</alfresco-upload-drag-area> </alfresco-upload-drag-area>
</div> </div>
<context-menu-holder></context-menu-holder> <context-menu-holder></context-menu-holder>
<div class="p-10">
<ul>
<li>Current path: {{currentPath}}</li>
<li>
<button (click)="currentPath = '/'">Go to root</button>
</li>
<li>
<button (click)="currentPath = '/Sites'">Go to Sites</button>
</li>
<li>
<button (click)="currentPath = '/Sites/swsdp';">Go to Web Site Design Project site</button>
</li>
<li>
<button (click)="currentPath = '/Sites/swsdp/documentLibrary';">Go to Document Library</button>
</li>
<li>
<button (click)="currentPath = '/Sites/swsdp/documentLibrary/Agency Files/Contracts'">Go to Agency Contracts</button>
</li>
<li>
<button (click)="currentPath = '!@£$%^&*()'">Go to the wrong path</button>
</li>
<li>
<button (click)="fileDialog.toggleShowDialog()">Show/Hide File Dialog</button>
</li>
</ul>
</div>
<p style="width:250px;margin: 20px;"> <p style="width:250px;margin: 20px;">
<label for="switch-multiple-file" class="mdl-switch mdl-js-switch mdl-js-ripple-effect"> <label for="switch-multiple-file" class="mdl-switch mdl-js-switch mdl-js-ripple-effect">
<input type="checkbox" id="switch-multiple-file" class="mdl-switch__input" (change)="toggleMultipleFileUpload()" > <input type="checkbox" id="switch-multiple-file" class="mdl-switch__input" (change)="toggleMultipleFileUpload()" >
@@ -183,8 +140,7 @@
</div> </div>
<div *ngIf="!acceptedFilesTypeShow"> <div *ngIf="!acceptedFilesTypeShow">
<alfresco-upload-button data-automation-id="multiple-file-upload" <alfresco-upload-button data-automation-id="multiple-file-upload"
[rootFolderId]="uploadRootFolderId" [rootFolderId]="documentList.currentFolderId"
[currentFolderPath]="uploadFolderPath"
[multipleFiles]="multipleFileUpload" [multipleFiles]="multipleFileUpload"
[uploadFolders]="folderUpload" [uploadFolders]="folderUpload"
[versioning] = "versioning" [versioning] = "versioning"
@@ -194,9 +150,8 @@
</div> </div>
<div *ngIf="acceptedFilesTypeShow"> <div *ngIf="acceptedFilesTypeShow">
<alfresco-upload-button data-automation-id="multiple-file-upload" <alfresco-upload-button data-automation-id="multiple-file-upload"
[rootFolderId]="uploadRootFolderId" [rootFolderId]="documentList.currentFolderId"
[currentFolderPath]="uploadFolderPath" [acceptedFilesType]="acceptedFilesType"
acceptedFilesType="{{acceptedFilesType}}"
[multipleFiles]="multipleFileUpload" [multipleFiles]="multipleFileUpload"
[uploadFolders]="folderUpload" [uploadFolders]="folderUpload"
[versioning] = "versioning" [versioning] = "versioning"
@@ -15,16 +15,10 @@
* limitations under the License. * limitations under the License.
*/ */
import { Component, OnInit, Optional, ViewChild } from '@angular/core'; import { Component, OnInit, Optional, ViewChild, ChangeDetectorRef } from '@angular/core';
import { ActivatedRoute, Params, Router } from '@angular/router'; import { ActivatedRoute, Params, Router } from '@angular/router';
import { AlfrescoAuthenticationService } from 'ng2-alfresco-core'; import { AlfrescoAuthenticationService, LogService } from 'ng2-alfresco-core';
import { import { DocumentActionsService, DocumentListComponent, ContentActionHandler, DocumentActionModel, FolderActionModel } from 'ng2-alfresco-documentlist';
DocumentActionsService,
DocumentList,
ContentActionHandler,
DocumentActionModel,
FolderActionModel
} from 'ng2-alfresco-documentlist';
import { FormService } from 'ng2-activiti-form'; import { FormService } from 'ng2-activiti-form';
@Component({ @Component({
@@ -33,9 +27,8 @@ import { FormService } from 'ng2-activiti-form';
styleUrls: ['./files.component.css'] styleUrls: ['./files.component.css']
}) })
export class FilesComponent implements OnInit { export class FilesComponent implements OnInit {
currentPath: string = '/Sites/swsdp/documentLibrary'; // The identifier of a node. You can also use one of these well-known aliases: -my- | -shared- | -root-
rootFolderId: string = '-root-'; currentFolderId: string = '-my-';
currentFolderId: string = null;
errorMessage: string = null; errorMessage: string = null;
fileNodeId: any; fileNodeId: any;
@@ -44,23 +37,16 @@ export class FilesComponent implements OnInit {
folderUpload: boolean = false; folderUpload: boolean = false;
acceptedFilesTypeShow: boolean = false; acceptedFilesTypeShow: boolean = false;
versioning: boolean = false; versioning: boolean = false;
acceptedFilesType: string = '.jpg,.pdf,.js'; acceptedFilesType: string = '.jpg,.pdf,.js';
get uploadRootFolderId(): string { @ViewChild(DocumentListComponent)
return this.currentFolderId || this.rootFolderId; documentList: DocumentListComponent;
}
get uploadFolderPath(): string {
return this.currentFolderId ? '/' : this.currentPath;
}
@ViewChild(DocumentList)
documentList: DocumentList;
constructor(private documentActions: DocumentActionsService, constructor(private documentActions: DocumentActionsService,
public auth: AlfrescoAuthenticationService, private authService: AlfrescoAuthenticationService,
private formService: FormService, private formService: FormService,
private logService: LogService,
private changeDetector: ChangeDetectorRef,
private router: Router, private router: Router,
@Optional() private route: ActivatedRoute) { @Optional() private route: ActivatedRoute) {
documentActions.setHandler('my-handler', this.myDocumentActionHandler.bind(this)); documentActions.setHandler('my-handler', this.myDocumentActionHandler.bind(this));
@@ -87,18 +73,6 @@ export class FilesComponent implements OnInit {
} }
} }
onFolderChanged(event?: any) {
if (event) {
this.currentPath = event.path;
}
}
onBreadcrumbPathChanged(event?: any) {
if (event) {
this.currentPath = event.value;
}
}
toggleMultipleFileUpload() { toggleMultipleFileUpload() {
this.multipleFileUpload = !this.multipleFileUpload; this.multipleFileUpload = !this.multipleFileUpload;
return this.multipleFileUpload; return this.multipleFileUpload;
@@ -123,16 +97,19 @@ export class FilesComponent implements OnInit {
ngOnInit() { ngOnInit() {
if (this.route) { if (this.route) {
this.route.params.forEach((params: Params) => { this.route.params.forEach((params: Params) => {
this.currentFolderId = params.hasOwnProperty('id') ? params['id'] : null; if (params['id']) {
this.currentFolderId = params['id'];
this.changeDetector.detectChanges();
}
}); });
} }
if (this.auth.isBpmLoggedIn()) { if (this.authService.isBpmLoggedIn()) {
this.formService.getProcessDefinitions().subscribe( this.formService.getProcessDefinitions().subscribe(
defs => this.setupBpmActions(defs || []), defs => this.setupBpmActions(defs || []),
err => console.log(err) err => this.logService.error(err)
); );
} else { } else {
console.log('You are not logged in'); this.logService.warn('You are not logged in to BPM');
} }
} }
@@ -7,11 +7,11 @@
</h2> </h2>
</div> </div>
<div class="mdl-card__supporting-text"> <div class="mdl-card__supporting-text">
Demonstrates multiple Alfresco ECM components used together to show the files of you ECM instance : Demonstrates multiple Alfresco ECM components used together to display the files of your ECM instance:
<ul class="home--feature-list"> <ul class="home--feature-list">
<li> <li>
<i class="material-icons home--feature-list__icon">brightness_1</i> <i class="material-icons home--feature-list__icon">brightness_1</i>
<span class="home--feature-list__text">Comunication with the Rest Api and core services</span> <span class="home--feature-list__text">Communication with the Rest Api and core services</span>
<a href="https://www.npmjs.com/package/ng2-alfresco-core" target="_blank">ng2-alfresco-core</a> <a href="https://www.npmjs.com/package/ng2-alfresco-core" target="_blank">ng2-alfresco-core</a>
</li> </li>
<li> <li>
@@ -43,11 +43,11 @@
</h2> </h2>
</div> </div>
<div class="mdl-card__supporting-text"> <div class="mdl-card__supporting-text">
Demonstrates multiple Alfresco BPM components used together to show your BPM prorcess and tasks: Demonstrates multiple Alfresco BPM components used together to show your BPM process and tasks:
<ul class="home--feature-list"> <ul class="home--feature-list">
<li> <li>
<i class="material-icons home--feature-list__icon">brightness_1</i> <i class="material-icons home--feature-list__icon">brightness_1</i>
<span class="home--feature-list__text">Comunication with the Rest Api and core services</span> <span class="home--feature-list__text">Communication with the Rest Api and core services</span>
<a href="https://www.npmjs.com/package/ng2-alfresco-core" target="_blank">ng2-alfresco-core</a> <a href="https://www.npmjs.com/package/ng2-alfresco-core" target="_blank">ng2-alfresco-core</a>
</li> </li>
<li> <li>
@@ -99,7 +99,7 @@
<ul class="home--feature-list"> <ul class="home--feature-list">
<li> <li>
<i class="material-icons home--feature-list__icon">brightness_1</i> <i class="material-icons home--feature-list__icon">brightness_1</i>
<span class="home--feature-list__text">Comunication with the Rest Api and core services</span> <span class="home--feature-list__text">Communication with the Rest Api and core services</span>
<a href="https://www.npmjs.com/package/ng2-alfresco-core" target="_blank">ng2-alfresco-core</a> <a href="https://www.npmjs.com/package/ng2-alfresco-core" target="_blank">ng2-alfresco-core</a>
</li> </li>
</ul> </ul>
@@ -119,7 +119,7 @@
<ul class="home--feature-list"> <ul class="home--feature-list">
<li> <li>
<i class="material-icons home--feature-list__icon">brightness_1</i> <i class="material-icons home--feature-list__icon">brightness_1</i>
<span class="home--feature-list__text">Comunication with the Rest Api and core services</span> <span class="home--feature-list__text">Communication with the Rest Api and core services</span>
<a href="https://www.npmjs.com/package/ng2-alfresco-core" target="_blank">ng2-alfresco-core</a> <a href="https://www.npmjs.com/package/ng2-alfresco-core" target="_blank">ng2-alfresco-core</a>
</li> </li>
</ul> </ul>
@@ -139,7 +139,7 @@
<ul class="home--feature-list"> <ul class="home--feature-list">
<li> <li>
<i class="material-icons home--feature-list__icon">brightness_1</i> <i class="material-icons home--feature-list__icon">brightness_1</i>
<span class="home--feature-list__text">Comunication with the Rest Api and core services</span> <span class="home--feature-list__text">Communication with the Rest Api and core services</span>
<a href="https://www.npmjs.com/package/ng2-alfresco-core" target="_blank">ng2-alfresco-core</a> <a href="https://www.npmjs.com/package/ng2-alfresco-core" target="_blank">ng2-alfresco-core</a>
</li> </li>
</ul> </ul>
@@ -155,11 +155,11 @@
</h2> </h2>
</div> </div>
<div class="mdl-card__supporting-text"> <div class="mdl-card__supporting-text">
Shows and create webscripts in your ECM instance: Displays and creates webscripts in your ECM instance:
<ul class="home--feature-list"> <ul class="home--feature-list">
<li> <li>
<i class="material-icons home--feature-list__icon">brightness_1</i> <i class="material-icons home--feature-list__icon">brightness_1</i>
<span class="home--feature-list__text">Comunication with the Rest Api and core services</span> <span class="home--feature-list__text">Communication with the Rest Api and core services</span>
<a href="https://www.npmjs.com/package/ng2-alfresco-core" target="_blank">ng2-alfresco-core</a> <a href="https://www.npmjs.com/package/ng2-alfresco-core" target="_blank">ng2-alfresco-core</a>
</li> </li>
</ul> </ul>
@@ -175,11 +175,11 @@
</h2> </h2>
</div> </div>
<div class="mdl-card__supporting-text"> <div class="mdl-card__supporting-text">
Shows and add tags to the node of your ECM instance: Displays and adds tags to the node of your ECM instance:
<ul class="home--feature-list"> <ul class="home--feature-list">
<li> <li>
<i class="material-icons home--feature-list__icon">brightness_1</i> <i class="material-icons home--feature-list__icon">brightness_1</i>
<span class="home--feature-list__text">Comunication with Rest</span> <span class="home--feature-list__text">Communication with Rest</span>
<a href="https://www.npmjs.com/package/ng2-alfresco-core" target="_blank">ng2-alfresco-core</a> <a href="https://www.npmjs.com/package/ng2-alfresco-core" target="_blank">ng2-alfresco-core</a>
</li> </li>
</ul> </ul>
@@ -18,7 +18,7 @@
import { Component, ViewChild, OnInit } from '@angular/core'; import { Component, ViewChild, OnInit } from '@angular/core';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { Validators } from '@angular/forms'; import { Validators } from '@angular/forms';
import { StorageService } from 'ng2-alfresco-core'; import { StorageService, LogService } from 'ng2-alfresco-core';
@Component({ @Component({
selector: 'login-demo', selector: 'login-demo',
@@ -37,17 +37,20 @@ export class LoginDemoComponent implements OnInit {
disableCsrf: boolean = false; disableCsrf: boolean = false;
isECM: boolean = true; isECM: boolean = true;
isBPM: boolean = false; isBPM: boolean = false;
customMinLenght: number = 2;
constructor(public router: Router, private storage: StorageService) { constructor(private router: Router,
private storage: StorageService,
private logService: LogService) {
this.customValidation = { this.customValidation = {
username: ['', Validators.compose([Validators.required, Validators.minLength(4)])], username: ['', Validators.compose([Validators.required, Validators.minLength(this.customMinLenght)])],
password: ['', Validators.required] password: ['', Validators.required]
}; };
} }
ngOnInit() { ngOnInit() {
this.alfrescologin.addCustomValidationError('username', 'required', 'LOGIN.MESSAGES.USERNAME-REQUIRED'); this.alfrescologin.addCustomValidationError('username', 'required', 'LOGIN.MESSAGES.USERNAME-REQUIRED');
this.alfrescologin.addCustomValidationError('username', 'minlength', 'LOGIN.MESSAGES.USERNAME-MIN'); this.alfrescologin.addCustomValidationError('username', 'minlength', 'LOGIN.MESSAGES.USERNAME-MIN', {customMinLenght: this.customMinLenght});
this.alfrescologin.addCustomValidationError('password', 'required', 'LOGIN.MESSAGES.PASSWORD-REQUIRED'); this.alfrescologin.addCustomValidationError('password', 'required', 'LOGIN.MESSAGES.PASSWORD-REQUIRED');
if (this.storage.hasItem('providers')) { if (this.storage.hasItem('providers')) {
@@ -75,7 +78,7 @@ export class LoginDemoComponent implements OnInit {
} }
onError($event) { onError($event) {
console.log($event); this.logService.error($event);
} }
toggleECM() { toggleECM() {
@@ -92,7 +95,7 @@ export class LoginDemoComponent implements OnInit {
this.disableCsrf = !this.disableCsrf; this.disableCsrf = !this.disableCsrf;
} }
updateProvider(){ updateProvider() {
if (this.isBPM && this.isECM) { if (this.isBPM && this.isECM) {
this.providers = 'ALL'; this.providers = 'ALL';
return this.providers; return this.providers;
@@ -34,11 +34,11 @@ export class SearchBarComponent {
expand = new EventEmitter(); expand = new EventEmitter();
constructor(public router: Router, constructor(public router: Router,
public auth: AlfrescoAuthenticationService) { public authService: AlfrescoAuthenticationService) {
} }
isLoggedIn(): boolean { isLoggedIn(): boolean {
return this.auth.isLoggedIn(); return this.authService.isLoggedIn();
} }
/** /**
@@ -1,8 +1,12 @@
<div class="search-results-container"> <div class="search-results-container">
<h1>Search results</h1> <h1>Search results</h1>
<alfresco-search (navigate)="onNavigateItem($event)"></alfresco-search> <alfresco-search (preview)="showFile($event)"></alfresco-search>
</div> </div>
<alfresco-viewer *ngIf="fileShowed" [(showViewer)]="fileShowed" [fileNodeId]="fileNodeId" [overlayMode]="true"> <div *ngIf="fileShowed">
<div class="mdl-spinner mdl-js-spinner is-active"></div> <alfresco-viewer [(showViewer)]="fileShowed"
</alfresco-viewer> [fileNodeId]="fileNodeId"
[overlayMode]="true">
<div class="mdl-spinner mdl-js-spinner is-active"></div>
</alfresco-viewer>
</div>
@@ -47,18 +47,18 @@ import { MinimalNodeEntity } from 'alfresco-js-api';
}) })
export class SearchComponent { export class SearchComponent {
fileShowed: boolean = false;
fileNodeId: string; fileNodeId: string;
fileShowed: boolean = false;
constructor(public router: Router) { constructor(public router: Router) {
} }
onNavigateItem(event: MinimalNodeEntity) { showFile(event) {
if (event.entry.isFile) { if (event.value.entry.isFile) {
this.fileNodeId = event.entry.id; this.fileNodeId = event.value.entry.id;
this.fileShowed = true; this.fileShowed = true;
} else if (event.entry.isFolder) { } else {
this.router.navigate(['/files', event.entry.id]); this.fileShowed = false;
} }
} }
} }
@@ -16,7 +16,7 @@
*/ */
import { Component } from '@angular/core'; import { Component } from '@angular/core';
import { AlfrescoSettingsService, StorageService } from 'ng2-alfresco-core'; import { AlfrescoSettingsService, StorageService, LogService } from 'ng2-alfresco-core';
@Component({ @Component({
selector: 'alfresco-setting-demo', selector: 'alfresco-setting-demo',
@@ -28,24 +28,31 @@ export class SettingComponent {
ecmHost: string; ecmHost: string;
bpmHost: string; bpmHost: string;
constructor(public alfrescoSettingsService: AlfrescoSettingsService, constructor(private settingsService: AlfrescoSettingsService,
private storage: StorageService) { private storage: StorageService,
this.ecmHost = this.alfrescoSettingsService.ecmHost; private logService: LogService) {
this.bpmHost = this.alfrescoSettingsService.bpmHost; this.ecmHost = this.settingsService.ecmHost;
this.bpmHost = this.settingsService.bpmHost;
} }
public onChangeECMHost(event: KeyboardEvent): void { public onChangeECMHost(event: KeyboardEvent): void {
console.log((<HTMLInputElement>event.target).value); let value = (<HTMLInputElement>event.target).value.trim();
this.ecmHost = (<HTMLInputElement>event.target).value; if (value) {
this.alfrescoSettingsService.ecmHost = this.ecmHost; this.logService.info(`ECM host: ${value}`);
this.storage.setItem(`ecmHost`, this.ecmHost); this.ecmHost = value;
this.settingsService.ecmHost = value;
this.storage.setItem(`ecmHost`, value);
}
} }
public onChangeBPMHost(event: KeyboardEvent): void { public onChangeBPMHost(event: KeyboardEvent): void {
console.log((<HTMLInputElement>event.target).value); let value = (<HTMLInputElement>event.target).value.trim();
this.bpmHost = (<HTMLInputElement>event.target).value; if (value) {
this.alfrescoSettingsService.bpmHost = this.bpmHost; this.logService.info(`BPM host: ${value}`);
this.storage.setItem(`bpmHost`, this.bpmHost); this.bpmHost = value;
this.settingsService.bpmHost = value;
this.storage.setItem(`bpmHost`, value);
}
} }
} }
@@ -16,42 +16,41 @@
*/ */
import { Component } from '@angular/core'; import { Component } from '@angular/core';
import { LogService } from 'ng2-alfresco-core';
@Component({ @Component({
selector: 'alfresco-webscript-demo', selector: 'alfresco-webscript-demo',
template: ` template: `
<label for="script-path"><b>Insert a scriptPath</b></label><br>
<label for="script-path"><b>Insert a scriptPath</b></label><br> <input id="script-path" type="text" size="48" [(ngModel)]="scriptPath"><br>
<input id="script-path" type="text" size="48" [(ngModel)]="scriptPath"><br> <label for="context-root"><b>Insert a contextRoot</b></label><br>
<label for="context-root"><b>Insert a contextRoot</b></label><br> <input id="context-root" type="text" size="48" [(ngModel)]="contextRoot"><br>
<input id="context-root" type="text" size="48" [(ngModel)]="contextRoot"><br> <label for="service-path"><b>Insert a servicePath</b></label><br>
<label for="service-path"><b>Insert a servicePath</b></label><br> <input id="service-path" type="text" size="48" [(ngModel)]="servicePath"><br>
<input id="service-path" type="text" size="48" [(ngModel)]="servicePath"><br> <alfresco-webscript-get
<alfresco-webscript-get [scriptPath]="scriptPath" [scriptPath]="scriptPath"
[scriptArgs]="scriptArgs" [scriptArgs]="scriptArgs"
[contextRoot]="contextRoot" [contextRoot]="contextRoot"
[servicePath]="servicePath" [servicePath]="servicePath"
[contentType]="'HTML'" [contentType]="'HTML'"
(onSuccess)= "logData($event)"></alfresco-webscript-get> (onSuccess)= "logData($event)">
</alfresco-webscript-get>
` `
}) })
export class WebscriptComponent { export class WebscriptComponent {
currentPath: string = '/'; currentPath: string = '/';
authenticated: boolean; authenticated: boolean;
host: string = 'http://127.0.0.1:8080'; host: string = 'http://127.0.0.1:8080';
scriptPath: string = 'sample/folder/Company%20Home'; scriptPath: string = 'sample/folder/Company%20Home';
contextRoot: string = 'alfresco'; contextRoot: string = 'alfresco';
servicePath: string = 'service'; servicePath: string = 'service';
scriptArgs: string = ''; scriptArgs: string = '';
constructor(private logService: LogService) {
}
logData(data) { logData(data) {
console.log(data); this.logService.log(data);
} }
} }
+4
View File
@@ -32,9 +32,13 @@ import 'dialog-polyfill/dialog-polyfill.css';
// Flags // Flags
import 'flag-icon-css/css/flag-icon.min.css'; import 'flag-icon-css/css/flag-icon.min.css';
import '../public/css/app.css'; import '../public/css/app.css';
import '../public/css/muli-font.css'; import '../public/css/muli-font.css';
// Load the Angular Material 2 stylesheet
import '@angular/material/core/theming/prebuilt/deeppurple-amber.css'
import 'ng2-activiti-form/stencils/runtime.ng1'; import 'ng2-activiti-form/stencils/runtime.ng1';
import 'ng2-activiti-form/stencils/runtime.adf'; import 'ng2-activiti-form/stencils/runtime.adf';
+3 -1
View File
@@ -10,7 +10,6 @@ module.exports = webpackMerge(commonConfig, {
output: { output: {
path: helpers.root('dist'), path: helpers.root('dist'),
publicPath: 'http://localhost:3000/',
filename: '[name].js', filename: '[name].js',
chunkFilename: '[id].chunk.js' chunkFilename: '[id].chunk.js'
}, },
@@ -60,6 +59,9 @@ module.exports = webpackMerge(commonConfig, {
], ],
devServer: { devServer: {
host: '0.0.0.0',
port: 3000,
inline: true,
historyApiFallback: true, historyApiFallback: true,
stats: 'minimal' stats: 'minimal'
} }
+14 -6
View File
@@ -1,6 +1,5 @@
var helpers = require('./helpers'); var helpers = require('./helpers');
var fs = require('fs'); var fs = require('fs');
var ExtractTextPlugin = require('extract-text-webpack-plugin');
var glob = require('glob'); var glob = require('glob');
const rootPath = helpers.root('node_modules'); const rootPath = helpers.root('node_modules');
@@ -34,12 +33,21 @@ module.exports = {
}, },
module: { module: {
preLoaders: [
{
test: /\.js$/,
include: [
...alfrescoLibs
],
loader: 'source-map-loader'
}
],
loaders: [ loaders: [
{ {
test: /\.ts$/, test: /\.ts$/,
exclude: /node_modules/, loaders: ['awesome-typescript-loader', 'angular2-template-loader', 'systemjs-loader'],
loaders: ['awesome-typescript-loader', 'angular2-template-loader'] exclude: ['node_modules','public']
}, },
{ {
test: /\.js$/, test: /\.js$/,
include: [ include: [
@@ -1,26 +1,29 @@
{ {
"LOGIN": { "LOGIN": {
"LOGO": "Alfresco", "LOGO": "Alfresco",
"LABEL": { "LABEL": {
"LOGIN": "Login", "LOGIN": "Login",
"USERNAME": "Username", "USERNAME": "Username",
"PASSWORD": "Password", "PASSWORD": "Password",
"REMEMBER": "Remember" "REMEMBER": "Remember"
}, },
"MESSAGES": { "MESSAGES": {
"USERNAME-REQUIRED": "Required", "USERNAME-REQUIRED": "Required",
"USERNAME-MIN": "Your username needs to be at least 4 characters.", "USERNAME-MIN": "Your username needs to be at least {{customMinLenght}} characters.",
"PASSWORD-REQUIRED": "Enter your password to sign in", "PASSWORD-REQUIRED": "Enter your password to sign in",
"LOGIN-ERROR-CREDENTIALS": "You have entered an invalid username or password", "LOGIN-ERROR-CREDENTIALS": "You have entered an invalid username or password",
"LOGIN-ERROR-PROVIDERS": "Providers cannot be undefined", "LOGIN-ERROR-PROVIDERS": "Providers cannot be undefined",
"LOGIN-SUCCESS": "Login successful" "LOGIN-SUCCESS": "Login successful",
}, "LOGIN-ERROR-CORS": "CORS exception, check your server configuration",
"BUTTON": { "LOGIN-ERROR-CSRF": "CSRF exception, set [disableCsrf]=\"true\" in alfresco-login component",
"LOGIN": "SIGN IN" "LOGIN-ECM-LICENSE": "The ECM is in read-only mode"
}, },
"ACTION": { "BUTTON": {
"HELP": "NEED HELP?", "LOGIN": "SIGN IN"
"REGISTER": "REGISTER" },
"ACTION": {
"HELP": "NEED HELP?",
"REGISTER": "REGISTER"
}
} }
}
} }
@@ -1,25 +1,28 @@
{ {
"LOGIN": { "LOGIN": {
"LABEL": { "LABEL": {
"LOGIN": "Autenticazione", "LOGIN": "Autenticazione",
"USERNAME": "Nome utente", "USERNAME": "Nome utente",
"PASSWORD": "Password", "PASSWORD": "Password",
"REMEMBER": "Ricordami" "REMEMBER": "Ricordami"
}, },
"MESSAGES": { "MESSAGES": {
"USERNAME-REQUIRED": "Campo obbligatorio", "USERNAME-REQUIRED": "Campo obbligatorio",
"USERNAME-MIN": "Inserire un nome utente di minimo 4 caratteri.", "USERNAME-MIN": "Inserire un nome utente di minimo 4 caratteri.",
"PASSWORD-REQUIRED": "Campo obbligatorio", "PASSWORD-REQUIRED": "Campo obbligatorio",
"LOGIN-ERROR-CREDENTIALS": "Nome utente o password non validi", "LOGIN-ERROR-CREDENTIALS": "Nome utente o password non validi",
"LOGIN-ERROR-PROVIDERS": "Providers non può essere nullo", "LOGIN-ERROR-PROVIDERS": "Providers non può essere nullo",
"LOGIN-SUCCESS": "Login effettuata con successo" "LOGIN-SUCCESS": "Login effettuata con successo",
}, "LOGIN-ERROR-CORS": "CORS exception, controlla le configurazioni del tuo server",
"BUTTON": { "LOGIN-ERROR-CSRF": "CSRF exception, set [disableCsrf]=\"true\" in alfresco-login component",
"LOGIN": "Accedi" "LOGIN-ECM-LICENSE": "l'ECM e' in read-only mode"
}, },
"ACTION": { "BUTTON": {
"HELP": "BISOGNO DI AIUTO?", "LOGIN": "Accedi"
"REGISTER": "REGISTRATI" },
"ACTION": {
"HELP": "BISOGNO DI AIUTO?",
"REGISTER": "REGISTRATI"
}
} }
} }
}
+33 -9
View File
@@ -11,10 +11,12 @@
<!-- Polyfill(s) for Safari (pre-10.x) --> <!-- Polyfill(s) for Safari (pre-10.x) -->
<script src=js/Intl.min.js></script> <script src=js/Intl.min.js></script>
<!-- IE/FF -->
<script src=js/element.scrollintoviewifneeded-polyfill.js></script>
<!--[if IE]> <!--[if IE]>
<script src=js/shim.min.js></script> <script src=js/shim.min.js></script>
<script src=//cdnjs.cloudflare.com/ajax/libs/dom4/1.8.3/dom4.js></script> <script src=//cdnjs.cloudflare.com/ajax/libs/dom4/1.8.3/dom4.js></script>
<script src=js/element.scrollintoviewifneeded-polyfill.js></script>
<script src=js/classlist-polyfill.js></script> <script src=js/classlist-polyfill.js></script>
<script src=js/web-animations.min.js></script> <script src=js/web-animations.min.js></script>
<script src=js/typedarray.js></script> <script src=js/typedarray.js></script>
@@ -31,14 +33,31 @@
<script src="js/material.min.js"></script> <script src="js/material.min.js"></script>
<link href="css/iconfont/material-icons.css" rel="stylesheet"> <link href="css/iconfont/material-icons.css" rel="stylesheet">
<style> <style>
.main_header_adf .mdl-layout__drawer-button { .loader-container {
right: 0 !important; display: -webkit-box; /* OLD - iOS 6-, Safari 3.1-6 */
left: initial !important; display: -moz-box; /* OLD - Firefox 19- (buggy but mostly works) */
left: auto; display: -webkit-flex; /* NEW - Chrome */
display: flex; /* NEW, Spec - Opera 12.1, Firefox 20+ */
-webkit-box-flex-direction: row;
-moz-box-flex-direction: row;
-webkit-flex-direction: row;
flex-direction: row;
height:100%;
} }
.loader-item {
margin: auto;
max-height:100px;
max-width:300px;
}
.loader-text{
white-space: nowrap;
text-align: center;
position: relative;
}
</style> </style>
</head> </head>
<!-- 3. Display the application --> <!-- 3. Display the application -->
@@ -46,9 +65,14 @@
<alfresco-app> <alfresco-app>
<div id="loader-container" class="loader-container"> <div id="loader-container" class="loader-container">
<div class="loader-item"> <div class="loader-item">
<div id="loader-spin" class="mdl-progress mdl-js-progress mdl-progress__indeterminate"></div> <div id="loader-spin" class="mdl-progress mdl-js-progress mdl-progress__indeterminate is-upgraded"
data-upgraded=",MaterialProgress">
<div class="progressbar bar bar1" style="width: 0%;"></div>
<div class="bufferbar bar bar2" style="width: 100%;"></div>
<div class="auxbar bar bar3" style="width: 0%;"></div>
</div>
<div id="loader-text" class="loader-text">Loading Demo Shell..</div> <div id="loader-text" class="loader-text">Loading Demo Shell..</div>
</div > </div>
</div> </div>
</alfresco-app> </alfresco-app>
</body> </body>
+19 -18
View File
@@ -1,11 +1,11 @@
{ {
"name": "Alfresco-Angular2-Demo", "name": "Alfresco-Angular2-Demo",
"description": "Demo shell for Alfresco Angular2 components", "description": "Demo shell for Alfresco Angular2 components",
"version": "1.0.0", "version": "1.1.0",
"author": "Alfresco Software, Ltd.", "author": "Alfresco Software, Ltd.",
"scripts": { "scripts": {
"clean": "npm install rimraf && npm run clean-build && rimraf dist node_modules typings dist", "clean": "npm install rimraf && npm run clean-build && rimraf dist node_modules typings dist",
"start": "npm run server-versions && webpack-dev-server --inline --progress --port 3000 --max_old_space_size=4096 --max_new_space_size=4096", "start": "npm run server-versions && webpack-dev-server --progress --max_old_space_size=4096 --max_new_space_size=4096",
"start:dist": "wsrv -s dist/ -p 3000 -a 0.0.0.0", "start:dist": "wsrv -s dist/ -p 3000 -a 0.0.0.0",
"clean-build": "rimraf 'app/{,**/}**.js' 'app/{,**/}**.js.map' 'app/{,**/}**.d.ts'", "clean-build": "rimraf 'app/{,**/}**.js' 'app/{,**/}**.js.map' 'app/{,**/}**.d.ts'",
"test": "karma start", "test": "karma start",
@@ -62,6 +62,7 @@
"@angular/platform-browser-dynamic": "2.2.2", "@angular/platform-browser-dynamic": "2.2.2",
"@angular/router": "3.2.2", "@angular/router": "3.2.2",
"@angular/upgrade": "2.2.2", "@angular/upgrade": "2.2.2",
"@angular/material": "2.0.0-beta.1",
"systemjs": "0.19.27", "systemjs": "0.19.27",
"core-js": "^2.4.1", "core-js": "^2.4.1",
"reflect-metadata": "^0.1.8", "reflect-metadata": "^0.1.8",
@@ -73,25 +74,25 @@
"pdfjs-dist": "1.5.404", "pdfjs-dist": "1.5.404",
"flag-icon-css": "2.3.0", "flag-icon-css": "2.3.0",
"moment": "2.15.1", "moment": "2.15.1",
"chart.js": "^2.1.4", "chart.js": "2.3.0",
"ng2-charts": "1.1.0", "ng2-charts": "1.1.0",
"raphael": "^2.2.6", "raphael": "^2.2.6",
"md-date-time-picker": "^2.2.0", "md-date-time-picker": "^2.2.0",
"alfresco-js-api": "^1.0.0", "alfresco-js-api": "~1.1.0",
"ng2-activiti-analytics": "1.0.0", "ng2-activiti-analytics": "1.1.0",
"ng2-alfresco-core": "1.0.0", "ng2-alfresco-core": "1.1.0",
"ng2-alfresco-datatable": "1.0.0", "ng2-alfresco-datatable": "1.1.0",
"ng2-alfresco-documentlist": "1.0.0", "ng2-alfresco-documentlist": "1.1.0",
"ng2-alfresco-login": "1.0.0", "ng2-alfresco-login": "1.1.0",
"ng2-alfresco-search": "1.0.0", "ng2-alfresco-search": "1.1.0",
"ng2-alfresco-upload": "1.0.0", "ng2-alfresco-upload": "1.1.0",
"ng2-alfresco-viewer": "1.0.0", "ng2-alfresco-viewer": "1.1.0",
"ng2-activiti-form": "1.0.0", "ng2-activiti-form": "1.1.0",
"ng2-activiti-tasklist": "1.0.0", "ng2-activiti-tasklist": "1.1.0",
"ng2-alfresco-userinfo": "1.0.0", "ng2-alfresco-userinfo": "1.1.0",
"ng2-activiti-processlist": "1.0.0", "ng2-activiti-processlist": "1.1.0",
"ng2-alfresco-webscript": "1.0.0", "ng2-alfresco-webscript": "1.1.0",
"ng2-alfresco-tag": "1.0.0", "ng2-alfresco-tag": "1.1.0",
"dialog-polyfill": "^0.4.3", "dialog-polyfill": "^0.4.3",
"element.scrollintoviewifneeded-polyfill": "^1.0.1" "element.scrollintoviewifneeded-polyfill": "^1.0.1"
}, },
+32 -9
View File
@@ -3,7 +3,6 @@ body, html {
height: 100%; height: 100%;
} }
.p-10 { .p-10 {
padding: 10px; padding: 10px;
} }
@@ -13,24 +12,24 @@ body, html {
} }
.loader-container { .loader-container {
display: -webkit-box; /* OLD - iOS 6-, Safari 3.1-6 */ display: -webkit-box; /* OLD - iOS 6-, Safari 3.1-6 */
display: -moz-box; /* OLD - Firefox 19- (buggy but mostly works) */ display: -moz-box; /* OLD - Firefox 19- (buggy but mostly works) */
display: -webkit-flex; /* NEW - Chrome */ display: -webkit-flex; /* NEW - Chrome */
display: flex; /* NEW, Spec - Opera 12.1, Firefox 20+ */ display: flex; /* NEW, Spec - Opera 12.1, Firefox 20+ */
-webkit-box-flex-direction: row; -webkit-box-flex-direction: row;
-moz-box-flex-direction: row; -moz-box-flex-direction: row;
-webkit-flex-direction: row; -webkit-flex-direction: row;
flex-direction: row; flex-direction: row;
height:100%; height: 100%;
} }
.loader-item { .loader-item {
margin: auto; margin: auto;
max-height:100px; max-height: 100px;
max-width:300px; max-width: 300px;
} }
.loader-text{ .loader-text {
white-space: nowrap; white-space: nowrap;
text-align: center; text-align: center;
position: relative; position: relative;
@@ -39,3 +38,27 @@ body, html {
._dialog_overlay { ._dialog_overlay {
position: static !important; position: static !important;
} }
.main_header_adf .mdl-layout__drawer-button {
right: 0 !important;
left: initial !important;
left: auto;
}
.mdl-layout__header {
color: rgb(255, 255, 255);
background-color: rgb(31, 188, 210);
}
.mdl-layout__header .material-icons {
color: rgb(255, 255, 255);
}
.mdl-layout__header-row .mdl-navigation__link {
color: rgb(255, 255, 255);
}
.md-simple-snackbar-action {
border: 0px;
cursor: pointer;
}
File diff suppressed because one or more lines are too long
@@ -69,6 +69,9 @@ Follow the 3 steps below:
<script src="node_modules/material-design-lite/material.min.js"></script> <script src="node_modules/material-design-lite/material.min.js"></script>
<link rel="stylesheet" href="node_modules/material-design-icons/iconfont/material-icons.css"> <link rel="stylesheet" href="node_modules/material-design-icons/iconfont/material-icons.css">
<!-- Load the Angular Material 2 stylesheet -->
<link href="node_modules/@angular/material/core/theming/prebuilt/deeppurple-amber.css" rel="stylesheet">
<!-- Polyfill(s) for Safari (pre-10.x) --> <!-- Polyfill(s) for Safari (pre-10.x) -->
<script src="node_modules/intl/dist/Intl.min.js"></script> <script src="node_modules/intl/dist/Intl.min.js"></script>
<script src="node_modules/intl/locale-data/jsonp/en.js"></script> <script src="node_modules/intl/locale-data/jsonp/en.js"></script>
@@ -39,6 +39,7 @@
"@angular/http": "2.2.2", "@angular/http": "2.2.2",
"@angular/platform-browser": "2.2.2", "@angular/platform-browser": "2.2.2",
"@angular/platform-browser-dynamic": "2.2.2", "@angular/platform-browser-dynamic": "2.2.2",
"@angular/material": "2.0.0-beta.1",
"@angular/router": "3.2.2", "@angular/router": "3.2.2",
"@angular/upgrade": "2.2.2", "@angular/upgrade": "2.2.2",
"core-js": "^2.4.1", "core-js": "^2.4.1",
@@ -57,10 +58,10 @@
"moment": "2.15.1", "moment": "2.15.1",
"raphael": "^2.2.6", "raphael": "^2.2.6",
"ng2-translate": "2.5.0", "ng2-translate": "2.5.0",
"alfresco-js-api": "^1.0.0", "alfresco-js-api": "~1.1.0",
"ng2-alfresco-core": "1.0.0", "ng2-alfresco-core": "1.1.0",
"ng2-activiti-diagrams": "1.0.0", "ng2-activiti-diagrams": "1.1.0",
"ng2-activiti-analytics": "^1.0.0" "ng2-activiti-analytics": "^1.1.0"
}, },
"devDependencies": { "devDependencies": {
"@types/jasmine": "^2.2.33", "@types/jasmine": "^2.2.33",
@@ -18,51 +18,49 @@
import { NgModule, Component, OnInit } from '@angular/core'; import { NgModule, Component, OnInit } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser'; import { BrowserModule } from '@angular/platform-browser';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { CoreModule, AlfrescoSettingsService, AlfrescoAuthenticationService, StorageService } from 'ng2-alfresco-core'; import { CoreModule, AlfrescoSettingsService, AlfrescoAuthenticationService, StorageService, LogService } from 'ng2-alfresco-core';
import { AnalyticsModule } from 'ng2-activiti-analytics'; import { AnalyticsModule } from 'ng2-activiti-analytics';
@Component({ @Component({
selector: 'alfresco-app-demo', selector: 'alfresco-app-demo',
template: ` template: `
<label for="ticket"><b>Insert a valid ticket:</b></label><br> <label for="ticket"><b>Insert a valid ticket:</b></label><br>
<input id="ticket" type="text" size="48" (change)="updateTicket()" [(ngModel)]="ticket"><br> <input id="ticket" type="text" size="48" (change)="updateTicket()" [(ngModel)]="ticket"><br>
<label for="host"><b>Insert the ip of your Activiti instance:</b></label><br> <label for="host"><b>Insert the ip of your Activiti instance:</b></label><br>
<input id="host" type="text" size="48" (change)="updateHost()" [(ngModel)]="host"><br><br> <input id="host" type="text" size="48" (change)="updateHost()" [(ngModel)]="host"><br><br>
<div *ngIf="!authenticated" style="color:#FF2323"> <div *ngIf="!authenticated" style="color:#FF2323">
Authentication failed to ip {{ host }} with user: admin, admin, you can still try to add a valid ticket to perform Authentication failed to ip {{ host }} with user: admin, admin, you can still try to add a valid ticket to perform
operations. operations.
</div> </div>
<hr> <hr>
<div class="page-content"> <div class="page-content">
<label for="appId"><b>Insert the appId:</b></label><br> <label for="appId"><b>Insert the appId:</b></label><br>
<input id="appId" size="10" type="text" [(ngModel)]="appId"> <input id="appId" size="10" type="text" [(ngModel)]="appId">
<div class="mdl-grid"> <div class="mdl-grid">
<div class="mdl-cell mdl-cell--4-col task-column mdl-shadow--2dp"> <div class="mdl-cell mdl-cell--4-col task-column mdl-shadow--2dp">
<analytics-report-list (reportClick)="onReportClick($event)"></analytics-report-list> <analytics-report-list (reportClick)="onReportClick($event)"></analytics-report-list>
</div> </div>
<div class="mdl-cell mdl-cell--8-col task-column mdl-shadow--2dp"> <div class="mdl-cell mdl-cell--8-col task-column mdl-shadow--2dp">
<activiti-analytics [appId]="appId" *ngIf="report" [reportId]="report.id"></activiti-analytics> <activiti-analytics [appId]="appId" *ngIf="report" [reportId]="report.id"></activiti-analytics>
</div>
</div> </div>
</div> </div>
</div>` `
}) })
export class AnalyticsDemoComponent implements OnInit { export class AnalyticsDemoComponent implements OnInit {
appId: number; appId: number;
report: any; report: any;
authenticated: boolean; authenticated: boolean;
host: string = 'http://localhost:9999'; host: string = 'http://localhost:9999';
ticket: string; ticket: string;
constructor(private authService: AlfrescoAuthenticationService, constructor(private authService: AlfrescoAuthenticationService,
private settingsService: AlfrescoSettingsService, private settingsService: AlfrescoSettingsService,
private storage: StorageService) { private storage: StorageService,
private logService: LogService) {
settingsService.bpmHost = this.host; settingsService.bpmHost = this.host;
settingsService.setProviders('BPM'); settingsService.setProviders('BPM');
@@ -91,12 +89,12 @@ export class AnalyticsDemoComponent implements OnInit {
login() { login() {
this.authService.login('admin', 'admin').subscribe( this.authService.login('admin', 'admin').subscribe(
ticket => { ticket => {
console.log(ticket); this.logService.log(ticket);
this.ticket = this.authService.getTicketBpm(); this.ticket = this.authService.getTicketBpm();
this.authenticated = true; this.authenticated = true;
}, },
error => { error => {
console.log(error); this.logService.error(error);
this.authenticated = false; this.authenticated = false;
}); });
} }
@@ -22,6 +22,7 @@ import { DiagramsModule } from 'ng2-activiti-diagrams';
import { AnalyticsReportListComponent } from './src/components/analytics-report-list.component'; import { AnalyticsReportListComponent } from './src/components/analytics-report-list.component';
import { AnalyticsReportParametersComponent } from './src/components/analytics-report-parameters.component'; import { AnalyticsReportParametersComponent } from './src/components/analytics-report-parameters.component';
import { AnalyticsComponent } from './src/components/analytics.component'; import { AnalyticsComponent } from './src/components/analytics.component';
import { AnalyticsGeneratorComponent } from './src/components/analytics-generator.component';
import { AnalyticsReportHeatMapComponent } from './src/components/analytics-report-heat-map.component'; import { AnalyticsReportHeatMapComponent } from './src/components/analytics-report-heat-map.component';
import { AnalyticsService } from './src/services/analytics.service'; import { AnalyticsService } from './src/services/analytics.service';
import { CHART_DIRECTIVES } from 'ng2-charts/ng2-charts'; import { CHART_DIRECTIVES } from 'ng2-charts/ng2-charts';
@@ -29,6 +30,7 @@ import { CHART_DIRECTIVES } from 'ng2-charts/ng2-charts';
import { WIDGET_DIRECTIVES } from './src/components/widgets/index'; import { WIDGET_DIRECTIVES } from './src/components/widgets/index';
export * from './src/components/analytics.component'; export * from './src/components/analytics.component';
export * from './src/components/analytics-generator.component';
export * from './src/components/analytics-report-list.component'; export * from './src/components/analytics-report-list.component';
export * from './src/components/analytics-report-parameters.component'; export * from './src/components/analytics-report-parameters.component';
export * from './src/services/analytics.service'; export * from './src/services/analytics.service';
@@ -38,6 +40,7 @@ export const ANALYTICS_DIRECTIVES: any[] = [
AnalyticsComponent, AnalyticsComponent,
AnalyticsReportListComponent, AnalyticsReportListComponent,
AnalyticsReportParametersComponent, AnalyticsReportParametersComponent,
AnalyticsGeneratorComponent,
AnalyticsReportHeatMapComponent, AnalyticsReportHeatMapComponent,
WIDGET_DIRECTIVES WIDGET_DIRECTIVES
]; ];
@@ -1,7 +1,14 @@
// Tun on full stack traces in errors to help debugging // Tun on full stack traces in errors to help debugging
Error.stackTraceLimit = Infinity; Error.stackTraceLimit = Infinity;
jasmine.DEFAULT_TIMEOUT_INTERVAL = 1000; jasmine.DEFAULT_TIMEOUT_INTERVAL = 100000;
window.componentHandler = {
upgradeAllRegistered: function () {
},
upgradeElement: function () {
}
};
__karma__.loaded = function() {}; __karma__.loaded = function() {};
@@ -39,6 +46,8 @@ var map = {
'@angular/http': 'npm:@angular/http/bundles/http.umd.js', '@angular/http': 'npm:@angular/http/bundles/http.umd.js',
'@angular/router': 'npm:@angular/router/bundles/router.umd.js', '@angular/router': 'npm:@angular/router/bundles/router.umd.js',
'@angular/forms': 'npm:@angular/forms/bundles/forms.umd.js', '@angular/forms': 'npm:@angular/forms/bundles/forms.umd.js',
'@angular/material': 'npm:@angular/material/bundles/material.umd.js',
// testing // testing
'@angular/core/testing': 'npm:@angular/core/bundles/core-testing.umd.js', '@angular/core/testing': 'npm:@angular/core/bundles/core-testing.umd.js',
'@angular/common/testing': 'npm:@angular/common/bundles/common-testing.umd.js', '@angular/common/testing': 'npm:@angular/common/bundles/common-testing.umd.js',
@@ -1,13 +1,13 @@
{ {
"name": "ng2-activiti-analytics", "name": "ng2-activiti-analytics",
"description": "Activiti Angular2 Analytics Component", "description": "Activiti Angular2 Analytics Component",
"version": "1.0.0", "version": "1.1.0",
"author": "Alfresco Software, Ltd.", "author": "Alfresco Software, Ltd.",
"scripts": { "scripts": {
"clean": "npm install rimraf && npm run clean-build && rimraf dist node_modules typings", "clean": "npm install rimraf && npm run clean-build && rimraf dist node_modules typings",
"clean-build": "rimraf index.js index.js.map index.d.ts'src/{,**/}**.js' 'src/{,**/}**.js.map' 'src/{,**/}**.d.ts' bundles", "clean-build": "rimraf index.js index.js.map index.d.ts'src/{,**/}**.js' 'src/{,**/}**.js.map' 'src/{,**/}**.d.ts' bundles",
"build": "npm run clean-build && npm run tslint && rimraf dist && tsc && license-check && npm run build.umd", "build": "npm run clean-build && npm run tslint && rimraf dist && tsc && license-check && npm run build.umd",
"build:w": "npm run clean-build && npm run tslint && rimraf dist && tsc:w && license-check npm run build.umd", "build:w": "npm run clean-build && npm run tslint && rimraf dist && npm run tsc:w && license-check && npm run build.umd",
"tslint": "tslint -c tslint.json 'src/{,**/}**.ts' 'index.ts' -e '{,**/}**.d.ts' -e './gulpfile.ts'", "tslint": "tslint -c tslint.json 'src/{,**/}**.ts' 'index.ts' -e '{,**/}**.d.ts' -e './gulpfile.ts'",
"tsc": "tsc", "tsc": "tsc",
"tsc:w": "tsc -w", "tsc:w": "tsc -w",
@@ -16,7 +16,7 @@
"test-browser": "npm run build && concurrently \"karma start karma.conf.js --reporters kjhtml\" \"npm run watch-task\"", "test-browser": "npm run build && concurrently \"karma start karma.conf.js --reporters kjhtml\" \"npm run watch-task\"",
"posttest": "remap-istanbul -i coverage/report/coverage-final.json -o coverage/report -t html && remap-istanbul -i coverage/report/coverage-final.json -o coverage/report/coverage-final.json", "posttest": "remap-istanbul -i coverage/report/coverage-final.json -o coverage/report -t html && remap-istanbul -i coverage/report/coverage-final.json -o coverage/report/coverage-final.json",
"coverage": "npm run test && wsrv -o -p 9875 ./coverage/report", "coverage": "npm run test && wsrv -o -p 9875 ./coverage/report",
"prepublish": "npm run build", "prepublish": "npm run test",
"travis": "npm link ng2-alfresco-core ng2-activiti-diagrams", "travis": "npm link ng2-alfresco-core ng2-activiti-diagrams",
"gulp": "gulp", "gulp": "gulp",
"build.umd": "gulp build.prod --color --env-config prod --build-type prod", "build.umd": "gulp build.prod --color --env-config prod --build-type prod",
@@ -51,24 +51,26 @@
"@angular/http": "2.0.0", "@angular/http": "2.0.0",
"@angular/platform-browser": "2.0.0", "@angular/platform-browser": "2.0.0",
"@angular/platform-browser-dynamic": "2.0.0", "@angular/platform-browser-dynamic": "2.0.0",
"@angular/material": "2.0.0-beta.1",
"core-js": "^2.4.1", "core-js": "^2.4.1",
"reflect-metadata": "^0.1.3", "reflect-metadata": "^0.1.3",
"rxjs": "5.0.0-beta.12", "rxjs": "5.0.0-beta.12",
"systemjs": "0.19.27", "systemjs": "0.19.27",
"zone.js": "^0.6.23", "zone.js": "^0.6.23",
"chart.js": "^2.1.4", "chart.js": "2.3.0",
"md-date-time-picker": "^2.2.0", "md-date-time-picker": "^2.2.0",
"ng2-charts": "1.1.0", "ng2-charts": "1.1.0",
"moment": "2.15.1", "moment": "2.15.1",
"raphael": "^2.2.6", "raphael": "^2.2.6",
"alfresco-js-api": "^1.0.0", "alfresco-js-api": "~1.1.0",
"ng2-translate": "2.5.0", "ng2-translate": "2.5.0",
"ng2-alfresco-core": "1.0.0", "ng2-alfresco-core": "1.1.0",
"ng2-activiti-diagrams": "1.0.0" "ng2-activiti-diagrams": "1.1.0"
}, },
"devDependencies": { "devDependencies": {
"@types/jasmine": "^2.2.33", "@types/jasmine": "^2.2.33",
"@types/node": "^6.0.42", "@types/node": "^6.0.42",
"autoprefixer": "^6.5.4",
"concurrently": "^2.2.0", "concurrently": "^2.2.0",
"cpx": "1.3.1", "cpx": "1.3.1",
"cssnano": "^3.8.1", "cssnano": "^3.8.1",
@@ -87,6 +89,7 @@
"gulp-template": "^4.0.0", "gulp-template": "^4.0.0",
"gulp-typescript": "^3.1.3", "gulp-typescript": "^3.1.3",
"gulp-uglify": "^2.0.0", "gulp-uglify": "^2.0.0",
"gulp-util": "^3.0.7",
"intl": "^1.2.5", "intl": "^1.2.5",
"jasmine-core": "2.4.1", "jasmine-core": "2.4.1",
"karma": "0.13.22", "karma": "0.13.22",
@@ -97,6 +100,7 @@
"karma-jasmine-html-reporter": "0.2.0", "karma-jasmine-html-reporter": "0.2.0",
"karma-mocha-reporter": "2.0.3", "karma-mocha-reporter": "2.0.3",
"license-check": "1.1.5", "license-check": "1.1.5",
"merge-stream": "^1.0.1",
"remap-istanbul": "0.6.3", "remap-istanbul": "0.6.3",
"rimraf": "2.5.2", "rimraf": "2.5.2",
"run-sequence": "^1.2.2", "run-sequence": "^1.2.2",
@@ -0,0 +1,5 @@
.chart {display: block; width: 100%;}
.analytics-row__entry {
cursor: pointer;
}
@@ -0,0 +1,92 @@
<div *ngIf="reports">
<div *ngFor="let report of reports">
<h4>{{report.title}}</h4>
<div [ngSwitch]="report.type">
<div *ngSwitchCase="'pie'">
<div class="col-md-6">
<div *ngIf="!report.hasData()">{{'ANALYTICS.MESSAGES.NO-DATA-FOUND' | translate}}</div>
<div *ngIf="report.hasData()">
<div *ngIf="report.hasZeroValues()">{{'ANALYTICS.MESSAGES.ZERO-DATA-FOUND' | translate}}</div>
<base-chart *ngIf="!report.hasZeroValues()" class="chart"
[data]="report.data"
[labels]="report.labels"
[chartType]="report.type"></base-chart>
</div>
</div>
</div>
<div *ngSwitchCase="'table'">
<div *ngIf="!report.hasDatasets()">{{'ANALYTICS.MESSAGES.NO-DATA-FOUND' | translate}}</div>
<div [attr.id]="'chart-table-' + report.id" *ngIf="report.hasDatasets()">
<table class="table table-responsive table-condensed" style="width: 100%">
<tr>
<th *ngFor="let label of report.labels">{{label | translate}}</th>
</tr>
<tr *ngFor="let rows of report.datasets" style="text-align: center;">
<td *ngFor="let row of rows">{{row | translate }}</td>
</tr>
</table>
</div>
</div>
<div *ngSwitchCase="'masterDetailTable'">
<div *ngIf="!report.hasDatasets()">{{'ANALYTICS.MESSAGES.NO-DATA-FOUND' | translate}}</div>
<div [attr.id]="'chart-master-detail-table-' + report.id" *ngIf="report.hasDatasets()">
<table class="table table-responsive table-condensed" style="width: 100%">
<tr>
<th *ngFor="let label of report.labels">{{label | translate}}</th>
</tr>
<tr *ngFor="let rows of report.datasets" class="analytics-row__entry" style="text-align: center;">
<td *ngFor="let row of rows" (click)="toggleDetailsTable()">{{row | translate }}</td>
</tr>
</table>
</div>
<div [attr.id]="'chart-master-detail-' + report.id" *ngIf="isShowDetails()">
<table class="table table-responsive table-condensed" style="width: 100%">
<tr>
<th *ngFor="let label of report.detailsTable.labels">{{label | translate}}</th>
</tr>
<tr *ngFor="let rows of report.detailsTable.datasets" style="text-align: center;">
<td *ngFor="let row of rows">{{row | translate }}</td>
</tr>
</table>
</div>
</div>
<div *ngSwitchCase="'bar'">
<div class="col-md-6">
<div *ngIf="!report.hasDatasets()">{{'ANALYTICS.MESSAGES.NO-DATA-FOUND' | translate}}</div>
<base-chart *ngIf="report.hasDatasets()" class="chart"
[datasets]="report.datasets"
[labels]="report.labels"
[options]="report.options"
[chartType]="report.type"></base-chart>
</div>
</div>
<div *ngSwitchCase="'multiBar'">
<div class="col-md-6">
<div *ngIf="!report.hasDatasets()">{{'ANALYTICS.MESSAGES.NO-DATA-FOUND' | translate}}</div>
<div *ngIf="report.hasDatasets()">
<label class="mdl-checkbox mdl-js-checkbox mdl-js-ripple-effect" [attr.for]="'stacked-id'">
<input type="checkbox" [attr.id]="'stacked-id'" class="mdl-checkbox__input"
[checked]="report.options.scales.xAxes[0].stacked"
[(ngModel)]="report.options.scales.xAxes[0].stacked"
(change)="refresh(report)">
<span class="mdl-checkbox__label">Stacked</span>
</label>
<base-chart class="chart"
[datasets]="report.datasets"
[labels]="report.labels"
[options]="report.options"
[chartType]="'bar'"></base-chart>
</div>
</div>
</div>
<div *ngSwitchCase="'HeatMap'">
<analytics-report-heat-map [report]="report"></analytics-report-heat-map>
</div>
<div *ngSwitchDefault>
<span>{{'ANALYTICS.MESSAGES.UNKNOWN-WIDGET-TYPE' | translate}}: {{report.type}}</span>
</div>
</div>
</div>
</div>
<br><br><br>
<div *ngIf="!reports">{{'ANALYTICS.MESSAGES.FILL-PARAMETER' | translate}}</div>
@@ -0,0 +1,230 @@
/*!
* @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 { DebugElement } from '@angular/core';
import { Observable } from 'rxjs/Rx';
import { CHART_DIRECTIVES } from 'ng2-charts/ng2-charts';
import { CoreModule, AlfrescoTranslationService } from 'ng2-alfresco-core';
import { DiagramsModule } from 'ng2-activiti-diagrams';
import { AnalyticsReportListComponent } from '../components/analytics-report-list.component';
import { AnalyticsGeneratorComponent } from '../components/analytics-generator.component';
import { AnalyticsReportParametersComponent } from '../components/analytics-report-parameters.component';
import { AnalyticsReportHeatMapComponent } from '../components/analytics-report-heat-map.component';
import { WIDGET_DIRECTIVES } from '../components/widgets/index';
import { Chart } from '../models/chart.model';
import { AnalyticsService } from '../services/analytics.service';
import { ReportQuery } from '../models/report.model';
import * as analyticMock from '../assets/analyticsComponent.mock';
export const ANALYTICS_DIRECTIVES: any[] = [
AnalyticsGeneratorComponent,
AnalyticsReportParametersComponent,
AnalyticsReportListComponent,
AnalyticsReportHeatMapComponent,
WIDGET_DIRECTIVES
];
export const ANALYTICS_PROVIDERS: any[] = [
AnalyticsService
];
declare let jasmine: any;
declare let mdDateTimePicker: any;
describe('AnalyticsGeneratorComponent', () => {
let component: any;
let fixture: ComponentFixture<AnalyticsGeneratorComponent>;
let debug: DebugElement;
let element: HTMLElement;
let componentHandler: any;
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [
CoreModule.forRoot(),
DiagramsModule.forRoot()
],
declarations: [
...ANALYTICS_DIRECTIVES,
...CHART_DIRECTIVES
],
providers: [
...ANALYTICS_PROVIDERS
]
}).compileComponents();
let translateService = TestBed.get(AlfrescoTranslationService);
spyOn(translateService, 'addTranslationFolder').and.stub();
spyOn(translateService, 'get').and.callFake((key) => { return Observable.of(key); });
}));
beforeEach(() => {
fixture = TestBed.createComponent(AnalyticsGeneratorComponent);
component = fixture.componentInstance;
debug = fixture.debugElement;
element = fixture.nativeElement;
fixture.detectChanges();
componentHandler = jasmine.createSpyObj('componentHandler', [
'upgradeAllRegistered'
]);
window['componentHandler'] = componentHandler;
});
describe('Rendering tests', () => {
beforeEach(() => {
jasmine.Ajax.install();
});
afterEach(() => {
jasmine.Ajax.uninstall();
});
it('Should render the Process definition overview report ', (done) => {
component.onSuccess.subscribe((res) => {
expect(res).toBeDefined();
expect(res.length).toEqual(3);
expect(res[0]).toBeDefined();
expect(res[0].type).toEqual('table');
expect(res[0].datasets).toBeDefined();
expect(res[0].datasets.length).toEqual(4);
expect(res[0].datasets[0][0]).toEqual('__KEY_REPORTING.DEFAULT-REPORTS.PROCESS-DEFINITION-OVERVIEW.GENERAL-TABLE-TOTAL-PROCESS-DEFINITIONS');
expect(res[0].datasets[0][1]).toEqual('9');
expect(res[0].datasets[1][0]).toEqual('__KEY_REPORTING.DEFAULT-REPORTS.PROCESS-DEFINITION-OVERVIEW.GENERAL-TABLE-TOTAL-PROCESS-INSTANCES');
expect(res[0].datasets[1][1]).toEqual('41');
expect(res[0].datasets[2][0]).toEqual('__KEY_REPORTING.DEFAULT-REPORTS.PROCESS-DEFINITION-OVERVIEW.GENERAL-TABLE-ACTIVE-PROCESS-INSTANCES');
expect(res[0].datasets[2][1]).toEqual('3');
expect(res[0].datasets[3][0]).toEqual('__KEY_REPORTING.DEFAULT-REPORTS.PROCESS-DEFINITION-OVERVIEW.GENERAL-TABLE-COMPLETED-PROCESS-INSTANCES');
expect(res[0].datasets[3][1]).toEqual('38');
expect(res[1]).toBeDefined();
expect(res[1].type).toEqual('pie');
expect(res[2]).toBeDefined();
expect(res[2].type).toEqual('table');
done();
});
let reportId = 1001;
let reportParamQuery = new ReportQuery({status: 'All'});
component.generateReport(reportId, reportParamQuery);
jasmine.Ajax.requests.mostRecent().respondWith({
status: 200,
contentType: 'json',
responseText: analyticMock.chartProcessDefOverview
});
});
it('Should render the Task overview report ', (done) => {
component.onSuccess.subscribe((res) => {
expect(res).toBeDefined();
expect(res.length).toEqual(3);
expect(res[0]).toBeDefined();
expect(res[0].type).toEqual('bar');
expect(res[0].labels).toBeDefined();
expect(res[0].labels.length).toEqual(2);
expect(res[0].labels[0]).toEqual('2016-09-30T00:00:00.000+0000');
expect(res[0].labels[1]).toEqual('2016-10-04T00:00:00.000+0000');
expect(res[0].datasets[0].label).toEqual('series1');
expect(res[0].datasets[0].data[0]).toEqual(3);
expect(res[0].datasets[0].data[1]).toEqual(1);
expect(res[1]).toBeDefined();
expect(res[1].type).toEqual('masterDetailTable');
expect(res[1].datasets).toBeDefined();
expect(res[1].datasets.length).toEqual(2);
expect(res[1].datasets[0][0]).toEqual('fake 1 user task');
expect(res[1].datasets[0][1]).toEqual('1');
expect(res[1].datasets[0][2]).toEqual('2.0');
expect(res[1].datasets[0][3]).toEqual('3.0');
expect(res[1].datasets[0][4]).toEqual('4.0');
expect(res[1].datasets[0][5]).toEqual('5.0');
expect(res[1].datasets[0][6]).toEqual('6.0');
expect(res[1].datasets[1][0]).toEqual('fake 2 user task');
expect(res[1].datasets[1][1]).toEqual('1');
expect(res[1].datasets[1][2]).toEqual('2.0');
expect(res[1].datasets[1][3]).toEqual('3.0');
expect(res[1].datasets[1][4]).toEqual('4.0');
expect(res[1].datasets[1][5]).toEqual('5.0');
expect(res[1].datasets[1][6]).toEqual('6.0');
expect(res[2]).toBeDefined();
expect(res[2].type).toEqual('multiBar');
expect(res[2].labels).toBeDefined();
expect(res[2].labels.length).toEqual(3);
expect(res[2].labels[0]).toEqual(1);
expect(res[2].labels[1]).toEqual(2);
expect(res[2].labels[2]).toEqual(3);
expect(res[2].datasets[0].label).toEqual('averages');
expect(res[2].datasets[0].data[0]).toEqual(0);
expect(res[2].datasets[0].data[1]).toEqual(5);
expect(res[2].datasets[0].data[2]).toEqual(2);
expect(res[2].datasets[1].label).toEqual('minima');
expect(res[2].datasets[1].data[0]).toEqual(0);
expect(res[2].datasets[1].data[1]).toEqual(0);
expect(res[2].datasets[1].data[2]).toEqual(0);
expect(res[2].datasets[2].label).toEqual('maxima');
expect(res[2].datasets[2].data[0]).toEqual(0);
expect(res[2].datasets[2].data[1]).toEqual(29);
expect(res[2].datasets[2].data[2]).toEqual(29);
done();
});
let reportParamQuery = new ReportQuery({status: 'All'});
component.reportId = 1;
component.generateReport(reportParamQuery);
jasmine.Ajax.requests.mostRecent().respondWith({
status: 200,
contentType: 'json',
responseText: analyticMock.chartTaskOverview
});
});
it('Should reset the reports when the onChanged is call', () => {
component.reports = [ new Chart({id: 'fake', type: 'fake-type'})];
component.reportId = 1;
component.ngOnChanges();
expect(component.reports).toBeUndefined();
});
it('Should emit onError event with a 404 response ', (done) => {
component.onError.subscribe((err) => {
expect(err).toBeDefined();
done();
});
let reportParamQuery = new ReportQuery({status: 'All'});
component.reportId = 1;
component.generateReport(reportParamQuery);
jasmine.Ajax.requests.mostRecent().respondWith({
status: 404,
contentType: 'json',
responseText: []
});
});
});
});
@@ -0,0 +1,119 @@
/*!
* @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, OnChanges, Input, Output, SimpleChanges } from '@angular/core';
import { AlfrescoTranslationService, LogService } from 'ng2-alfresco-core';
import { AnalyticsService } from '../services/analytics.service';
import { ReportQuery } from '../models/report.model';
import { Chart } from '../models/chart.model';
@Component({
moduleId: module.id,
selector: 'activiti-analytics-generator',
templateUrl: './analytics-generator.component.html',
styleUrls: ['./analytics-generator.component.css']
})
export class AnalyticsGeneratorComponent implements OnChanges {
@Input()
reportId: number;
@Input()
reportParamQuery: ReportQuery = undefined;
@Output()
onSuccess = new EventEmitter();
@Output()
onError = new EventEmitter();
reports: Chart[];
showDetails: boolean = false;
public barChartOptions: any = {
responsive: true,
scales: {
yAxes: [{
ticks: {
beginAtZero: true,
stepSize: 1
}
}],
xAxes: [{
ticks: {
},
stacked: true
}]
}
};
constructor(private translateService: AlfrescoTranslationService,
private analyticsService: AnalyticsService,
private logService: LogService) {
logService.info('AnalyticsGeneratorComponent');
if (translateService) {
translateService.addTranslationFolder('ng2-activiti-analytics', 'node_modules/ng2-activiti-analytics/src');
}
}
ngOnChanges(changes: SimpleChanges) {
if (this.reportId && this.reportParamQuery) {
this.generateReport(this.reportId, this.reportParamQuery);
} else {
this.reset();
}
}
public generateReport(reportId, reportParamQuery) {
this.analyticsService.getReportsByParams(reportId, reportParamQuery).subscribe(
(res: Chart[]) => {
this.reports = res;
this.onSuccess.emit(res);
},
(err: any) => {
this.onError.emit(err);
this.logService.error(err);
}
);
}
public reset() {
if (this.reports) {
this.reports = undefined;
}
}
public refresh(report): void {
/**
* (My guess), for Angular to recognize the change in the dataset
* it has to change the dataset variable directly,
* so one way around it, is to clone the data, change it and then
* assign it;
*/
let clone = JSON.parse(JSON.stringify(report));
report.datasets = clone.datasets;
}
toggleDetailsTable() {
this.showDetails = !this.showDetails;
}
isShowDetails(): boolean {
return this.showDetails;
}
}
@@ -15,17 +15,18 @@
* limitations under the License. * limitations under the License.
*/ */
import { DebugElement } from '@angular/core';
import { ComponentFixture, TestBed, async } from '@angular/core/testing'; import { ComponentFixture, TestBed, async } from '@angular/core/testing';
import { CoreModule } from 'ng2-alfresco-core'; import { Observable } from 'rxjs/Rx';
import { CoreModule, AlfrescoTranslationService } from 'ng2-alfresco-core';
import { DiagramsModule } from 'ng2-activiti-diagrams'; import { DiagramsModule } from 'ng2-activiti-diagrams';
import { AnalyticsReportHeatMapComponent } from '../components/analytics-report-heat-map.component'; import { AnalyticsReportHeatMapComponent } from '../components/analytics-report-heat-map.component';
import { WIDGET_DIRECTIVES } from '../components/widgets/index'; import { WIDGET_DIRECTIVES } from '../components/widgets/index';
import { AnalyticsService } from '../services/analytics.service'; import { AnalyticsService } from '../services/analytics.service';
import { DebugElement } from '@angular/core';
declare let jasmine: any; declare let jasmine: any;
describe('Test ng2-activiti-analytics-report-heat-map', () => { describe('AnalyticsReportHeatMapComponent', () => {
let componentHandler: any; let componentHandler: any;
let component: any; let component: any;
@@ -44,8 +45,8 @@ describe('Test ng2-activiti-analytics-report-heat-map', () => {
beforeEach(async(() => { beforeEach(async(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [ imports: [
CoreModule, CoreModule.forRoot(),
DiagramsModule DiagramsModule.forRoot()
], ],
declarations: [ declarations: [
AnalyticsReportHeatMapComponent, AnalyticsReportHeatMapComponent,
@@ -55,6 +56,11 @@ describe('Test ng2-activiti-analytics-report-heat-map', () => {
AnalyticsService AnalyticsService
] ]
}).compileComponents(); }).compileComponents();
let translateService = TestBed.get(AlfrescoTranslationService);
spyOn(translateService, 'addTranslationFolder').and.stub();
spyOn(translateService, 'get').and.callFake((key) => { return Observable.of(key); });
})); }));
beforeEach(() => { beforeEach(() => {
@@ -43,11 +43,11 @@ export class AnalyticsReportHeatMapComponent implements OnInit {
currentMetricColors: string; currentMetricColors: string;
metricType: string; metricType: string;
constructor(private translate: AlfrescoTranslationService, constructor(private translateService: AlfrescoTranslationService,
private analyticsService: AnalyticsService, private analyticsService: AnalyticsService,
private formBuilder: FormBuilder) { private formBuilder: FormBuilder) {
if (translate) { if (translateService) {
translate.addTranslationFolder('ng2-activiti-analytics', 'node_modules/ng2-activiti-analytics/src'); translateService.addTranslationFolder('ng2-activiti-analytics', 'node_modules/ng2-activiti-analytics/src');
} }
} }
@@ -15,15 +15,16 @@
* limitations under the License. * limitations under the License.
*/ */
import { DebugElement } from '@angular/core';
import { ComponentFixture, TestBed, async } from '@angular/core/testing'; import { ComponentFixture, TestBed, async } from '@angular/core/testing';
import { CoreModule } from 'ng2-alfresco-core'; import { Observable } from 'rxjs/Rx';
import { CoreModule, AlfrescoTranslationService } from 'ng2-alfresco-core';
import { AnalyticsReportListComponent } from '../components/analytics-report-list.component'; import { AnalyticsReportListComponent } from '../components/analytics-report-list.component';
import { AnalyticsService } from '../services/analytics.service'; import { AnalyticsService } from '../services/analytics.service';
import { DebugElement } from '@angular/core';
declare let jasmine: any; declare let jasmine: any;
describe('Test ng2-activiti-analytics Report list', () => { describe('AnalyticsReportListComponent', () => {
let reportList = [ let reportList = [
{'id': 2002, 'name': 'Fake Test Process definition heat map'}, {'id': 2002, 'name': 'Fake Test Process definition heat map'},
@@ -35,7 +36,7 @@ describe('Test ng2-activiti-analytics Report list', () => {
let reportSelected = {'id': 2003, 'name': 'Fake Test Process definition overview'}; let reportSelected = {'id': 2003, 'name': 'Fake Test Process definition overview'};
let component: any; let component: AnalyticsReportListComponent;
let fixture: ComponentFixture<AnalyticsReportListComponent>; let fixture: ComponentFixture<AnalyticsReportListComponent>;
let debug: DebugElement; let debug: DebugElement;
let element: HTMLElement; let element: HTMLElement;
@@ -43,7 +44,7 @@ describe('Test ng2-activiti-analytics Report list', () => {
beforeEach(async(() => { beforeEach(async(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [ imports: [
CoreModule CoreModule.forRoot()
], ],
declarations: [ declarations: [
AnalyticsReportListComponent AnalyticsReportListComponent
@@ -52,6 +53,10 @@ describe('Test ng2-activiti-analytics Report list', () => {
AnalyticsService AnalyticsService
] ]
}).compileComponents(); }).compileComponents();
let translateService = TestBed.get(AlfrescoTranslationService);
spyOn(translateService, 'addTranslationFolder').and.stub();
spyOn(translateService, 'get').and.callFake((key) => { return Observable.of(key); });
})); }));
beforeEach(() => { beforeEach(() => {
@@ -16,11 +16,10 @@
*/ */
import { Component, EventEmitter, OnInit, Output } from '@angular/core'; import { Component, EventEmitter, OnInit, Output } from '@angular/core';
import { AlfrescoAuthenticationService } from 'ng2-alfresco-core'; import { Observer, Observable } from 'rxjs/Rx';
import { LogService } from 'ng2-alfresco-core';
import { AnalyticsService } from '../services/analytics.service'; import { AnalyticsService } from '../services/analytics.service';
import { ReportParametersModel } from '../models/report.model'; import { ReportParametersModel } from '../models/report.model';
import { Observer } from 'rxjs/Observer';
import { Observable } from 'rxjs/Observable';
@Component({ @Component({
moduleId: module.id, moduleId: module.id,
@@ -46,9 +45,8 @@ export class AnalyticsReportListComponent implements OnInit {
reports: ReportParametersModel[] = []; reports: ReportParametersModel[] = [];
constructor(private auth: AlfrescoAuthenticationService, constructor(private analyticsService: AnalyticsService,
private analyticsService: AnalyticsService) { private logService: LogService) {
this.report$ = new Observable<ReportParametersModel>(observer => this.reportObserver = observer).share(); this.report$ = new Observable<ReportParametersModel>(observer => this.reportObserver = observer).share();
} }
@@ -85,7 +83,7 @@ export class AnalyticsReportListComponent implements OnInit {
}, },
(err: any) => { (err: any) => {
this.onError.emit(err); this.onError.emit(err);
console.log(err); this.logService.error(err);
} }
); );
} }
@@ -48,3 +48,18 @@
.icon-small:hover .material-icons { .icon-small:hover .material-icons {
display: block; display: block;
} }
.is-hide {
height: 0px;
overflow: hidden;
transition: height 0.5s;
}
.report-container {
border: solid 1px rgb(212, 212, 212);
padding: 10px 10px 10px 10px;
}
.report-container-setting {
padding-left: 10px;
}
@@ -1,75 +1,83 @@
<div class="col-md-6"> <div class="report-container">
<div *ngIf="reportParameters"> <a class="mdl-navigation__link setting-button" data-automation-id="settings">
<form [formGroup]="reportForm" novalidate> <button (click)="toggleParameters()" class="mdl-button mdl-js-button mdl-button--fab mdl-button--colored">
<div *ngIf="isEditable"> <i class="material-icons">settings</i>
<input </button>
type="text" <span class="report-container-setting">{{'ANALYTICS.MESSAGES.SETTING-TITLE' | translate}}</span>
class="mdl-textfield__input large" </a>
id="reportName" <div class="col-md-6" [class.is-hide]="isParametersHide()" >
autofocus <div *ngIf="reportParameters">
data-automation-id="reportName" <form [formGroup]="reportForm" novalidate>
[value]="reportParameters.name" <div *ngIf="isEditable">
(input)="reportParameters.name=$event.target.value" <input
(blur)="editTitle($event)" type="text"
/> class="mdl-textfield__input large"
</div> id="reportName"
<div *ngIf="!isEditable"> autofocus
<span class="icon-small"> data-automation-id="reportName"
<i class="material-icons">mode_edit</i> [value]="reportParameters.name"
<h4 (click)="editEnable()">{{reportParameters.name}}</h4> (input)="reportParameters.name=$event.target.value"
</span> (blur)="editTitle($event)"
</div><hr> />
<div *ngFor="let field of reportParameters.definition.parameters"> </div>
<div [ngSwitch]="field.type"> <div *ngIf="!isEditable">
<div *ngSwitchCase="'integer'"> <span class="icon-small">
<br> <i class="material-icons">mode_edit</i>
<number-widget [field]="field" [group]="reportForm.controls.processInstanceGroup" [controllerName]="'slowProcessInstanceInteger'" <h4 (click)="editEnable()">{{reportParameters.name}}</h4>
[required]="true"></number-widget> </span>
</div> </div><hr>
<div *ngSwitchCase="'duration'"> <div *ngFor="let field of reportParameters.definition.parameters">
<br> <div [ngSwitch]="field.type">
<duration-widget [field]="field" [group]="reportForm.controls.durationGroup" <div *ngSwitchCase="'integer'">
[controllerName]="'duration'"></duration-widget> <br>
</div> <number-widget [field]="field" [group]="reportForm.controls.processInstanceGroup" [controllerName]="'slowProcessInstanceInteger'"
<div *ngSwitchCase="'boolean'"> [required]="true"></number-widget>
<br> </div>
<checkbox-widget [field]="field" [group]="reportForm.controls.typeFilteringGroup" <div *ngSwitchCase="'duration'">
[controllerName]="'typeFiltering'"></checkbox-widget> <br>
</div> <duration-widget [field]="field" [group]="reportForm.controls.durationGroup"
<div *ngSwitchCase="'status'"> [controllerName]="'duration'"></duration-widget>
<br> </div>
<dropdown-widget [field]="field" [group]="reportForm.controls.statusGroup" [controllerName]="'status'" <div *ngSwitchCase="'boolean'">
[required]="true"></dropdown-widget> <br>
</div> <checkbox-widget [field]="field" [group]="reportForm.controls.typeFilteringGroup"
<div *ngSwitchCase="'processDefinition'"> [controllerName]="'typeFiltering'"></checkbox-widget>
<br> </div>
<dropdown-widget [field]="field" [group]="reportForm.controls.processDefGroup" [controllerName]="'processDefinitionId'" <div *ngSwitchCase="'status'">
[required]="true" (fieldChanged)="onProcessDefinitionChanges(field)"></dropdown-widget> <br>
</div> <dropdown-widget [field]="field" [group]="reportForm.controls.statusGroup" [controllerName]="'status'"
<div *ngSwitchCase="'task'"> [required]="true"></dropdown-widget>
<br> </div>
<dropdown-widget [field]="field" [group]="reportForm.controls.taskGroup" [controllerName]="'taskName'" <div *ngSwitchCase="'processDefinition'">
[required]="true"></dropdown-widget> <br>
</div> <dropdown-widget [field]="field" [group]="reportForm.controls.processDefGroup" [controllerName]="'processDefinitionId'"
<div *ngSwitchCase="'dateRange'"> [required]="true" (fieldChanged)="onProcessDefinitionChanges(field)"></dropdown-widget>
<br> </div>
<date-range-widget [field]="field" [group]="reportForm.controls.dateRange"></date-range-widget> <div *ngSwitchCase="'task'">
</div> <br>
<div *ngSwitchCase="'dateInterval'"> <dropdown-widget [field]="field" [group]="reportForm.controls.taskGroup" [controllerName]="'taskName'"
<br> [required]="true"></dropdown-widget>
<dropdown-widget [field]="field" [group]="reportForm.controls.dateIntervalGroup" [controllerName]="'dateRangeInterval'" </div>
[required]="true" [showDefaultOption]="false"></dropdown-widget> <div *ngSwitchCase="'dateRange'">
</div> <br>
<div *ngSwitchDefault> <date-range-widget [field]="field" [group]="reportForm.controls.dateRange"></date-range-widget>
<span>{{'ANALYTICS.MESSAGES.UNKNOWN-WIDGET-TYPE' | translate}}: {{field.type}}</span> </div>
<div *ngSwitchCase="'dateInterval'">
<br>
<dropdown-widget [field]="field" [group]="reportForm.controls.dateIntervalGroup" [controllerName]="'dateRangeInterval'"
[required]="true" [showDefaultOption]="false"></dropdown-widget>
</div>
<div *ngSwitchDefault>
<span>{{'ANALYTICS.MESSAGES.UNKNOWN-WIDGET-TYPE' | translate}}: {{field.type}}</span>
</div>
</div> </div>
</div> </div>
<div *ngIf="debug">
<p>ReportForm valid : {{ reportForm.valid }}</p>
<p>ReportForm status : {{ reportForm.errors | json }}</p>
<p>ReportForm FormGroup valid : {{reportForm && reportForm.controls.dateRange.valid | json }}</p>
</div>
</form>
</div> </div>
<div *ngIf="debug">
<p>ReportForm valid : {{ reportForm.valid }}</p>
<p>ReportForm status : {{ reportForm.errors | json }}</p>
<p>ReportForm FormGroup valid : {{reportForm && reportForm.controls.dateRange.valid | json }}</p>
</div>
</form>
</div> </div>
</div> </div>
@@ -16,25 +16,23 @@
*/ */
import { ComponentFixture, TestBed, async } from '@angular/core/testing'; import { ComponentFixture, TestBed, async } from '@angular/core/testing';
import { import { DebugElement, SimpleChange } from '@angular/core';
CoreModule import { Observable } from 'rxjs/Rx';
} from 'ng2-alfresco-core'; import * as moment from 'moment';
import { CoreModule, AlfrescoTranslationService } from 'ng2-alfresco-core';
import { AnalyticsReportParametersComponent } from '../components/analytics-report-parameters.component'; import { AnalyticsReportParametersComponent } from '../components/analytics-report-parameters.component';
import { WIDGET_DIRECTIVES } from '../components/widgets/index'; import { WIDGET_DIRECTIVES } from '../components/widgets/index';
import { AnalyticsService } from '../services/analytics.service'; import { AnalyticsService } from '../services/analytics.service';
import { ReportParametersModel } from '../models/report.model'; import { ReportParametersModel } from '../models/report.model';
import * as moment from 'moment';
import { DebugElement, SimpleChange } from '@angular/core';
import * as analyticParamsMock from '../assets/analyticsParamsReportComponent.mock'; import * as analyticParamsMock from '../assets/analyticsParamsReportComponent.mock';
declare let jasmine: any; declare let jasmine: any;
declare let mdDateTimePicker: any; declare let mdDateTimePicker: any;
describe('Test ng2-analytics-report-parameters Report Parameters ', () => { describe('AnalyticsReportParametersComponent', () => {
let component: any; let component: AnalyticsReportParametersComponent;
let fixture: ComponentFixture<AnalyticsReportParametersComponent>; let fixture: ComponentFixture<AnalyticsReportParametersComponent>;
let debug: DebugElement; let debug: DebugElement;
let element: HTMLElement; let element: HTMLElement;
@@ -44,7 +42,7 @@ describe('Test ng2-analytics-report-parameters Report Parameters ', () => {
beforeEach(async(() => { beforeEach(async(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [ imports: [
CoreModule CoreModule.forRoot()
], ],
declarations: [ declarations: [
AnalyticsReportParametersComponent, AnalyticsReportParametersComponent,
@@ -54,6 +52,15 @@ describe('Test ng2-analytics-report-parameters Report Parameters ', () => {
AnalyticsService AnalyticsService
] ]
}).compileComponents(); }).compileComponents();
let translateService = TestBed.get(AlfrescoTranslationService);
spyOn(translateService, 'addTranslationFolder').and.stub();
spyOn(translateService, 'get').and.callFake((key) => { return Observable.of(key); });
componentHandler = jasmine.createSpyObj('componentHandler', [
'upgradeAllRegistered'
]);
window['componentHandler'] = componentHandler;
})); }));
beforeEach(() => { beforeEach(() => {
@@ -62,10 +69,6 @@ describe('Test ng2-analytics-report-parameters Report Parameters ', () => {
debug = fixture.debugElement; debug = fixture.debugElement;
element = fixture.nativeElement; element = fixture.nativeElement;
fixture.detectChanges(); fixture.detectChanges();
componentHandler = jasmine.createSpyObj('componentHandler', [
'upgradeAllRegistered'
]);
window['componentHandler'] = componentHandler;
}); });
describe('Rendering tests', () => { describe('Rendering tests', () => {
@@ -323,9 +326,9 @@ describe('Test ng2-analytics-report-parameters Report Parameters ', () => {
responseText: analyticParamsMock.reportDefParamProcessDefOptionsApp responseText: analyticParamsMock.reportDefParamProcessDefOptionsApp
}); });
let appId = 1; let appId = '1';
component.appId = appId; component.appId = appId;
component.reportId = 1; component.reportId = '1';
let change = new SimpleChange(null, appId); let change = new SimpleChange(null, appId);
component.ngOnChanges({ 'appId': change }); component.ngOnChanges({ 'appId': change });
@@ -341,7 +344,7 @@ describe('Test ng2-analytics-report-parameters Report Parameters ', () => {
expect(res[1].name).toEqual('Fake task name 2'); expect(res[1].name).toEqual('Fake task name 2');
}); });
component.reportId = 100; component.reportId = '100';
component.reportParameters = new ReportParametersModel(analyticParamsMock.reportDefParamTask); component.reportParameters = new ReportParametersModel(analyticParamsMock.reportDefParamTask);
component.onProcessDefinitionChanges(analyticParamsMock.fieldProcessDef); component.onProcessDefinitionChanges(analyticParamsMock.fieldProcessDef);
@@ -392,10 +395,10 @@ describe('Test ng2-analytics-report-parameters Report Parameters ', () => {
responseText: [] responseText: []
}); });
}); });
});
it('Should convert a string in number', () => { it('Should convert a string in number', () => {
let numberConvert = component.convertNumber('2'); let numberConvert = component.convertNumber('2');
expect(numberConvert).toEqual(2); expect(numberConvert).toEqual(2);
});
}); });
}); });
@@ -15,12 +15,29 @@
* limitations under the License. * limitations under the License.
*/ */
import { Component, EventEmitter, OnInit, OnChanges, Input, Output, SimpleChanges } from '@angular/core'; import {
import { AlfrescoTranslationService } from 'ng2-alfresco-core'; Component,
import { AnalyticsService } from '../services/analytics.service'; EventEmitter,
import { ReportParametersModel, ReportQuery, ParameterValueModel, ReportParameterDetailsModel } from '../models/report.model'; OnInit,
OnChanges,
Input,
Output,
SimpleChanges,
OnDestroy,
AfterViewChecked
} from '@angular/core';
import { FormGroup, FormBuilder, FormControl } from '@angular/forms'; import { FormGroup, FormBuilder, FormControl } from '@angular/forms';
import * as moment from 'moment'; import * as moment from 'moment';
import { AlfrescoTranslationService, LogService } from 'ng2-alfresco-core';
import { AnalyticsService } from '../services/analytics.service';
import {
ReportParametersModel,
ReportQuery,
ParameterValueModel,
ReportParameterDetailsModel
} from '../models/report.model';
declare var componentHandler;
@Component({ @Component({
moduleId: module.id, moduleId: module.id,
@@ -28,7 +45,7 @@ import * as moment from 'moment';
templateUrl: './analytics-report-parameters.component.html', templateUrl: './analytics-report-parameters.component.html',
styleUrls: ['./analytics-report-parameters.component.css'] styleUrls: ['./analytics-report-parameters.component.css']
}) })
export class AnalyticsReportParametersComponent implements OnInit, OnChanges { export class AnalyticsReportParametersComponent implements OnInit, OnChanges, OnDestroy, AfterViewChecked {
public static FORMAT_DATE_ACTIVITI: string = 'YYYY-MM-DD'; public static FORMAT_DATE_ACTIVITI: string = 'YYYY-MM-DD';
@@ -67,12 +84,14 @@ export class AnalyticsReportParametersComponent implements OnInit, OnChanges {
private reportParamsSub; private reportParamsSub;
private paramOpts; private paramOpts;
private isEditable: boolean = false; private isEditable: boolean = false;
private hideParameters: boolean = true;
constructor(private translate: AlfrescoTranslationService, constructor(private translateService: AlfrescoTranslationService,
private analyticsService: AnalyticsService, private analyticsService: AnalyticsService,
private formBuilder: FormBuilder ) { private formBuilder: FormBuilder,
if (translate) { private logService: LogService) {
translate.addTranslationFolder('ng2-activiti-analytics', 'node_modules/ng2-activiti-analytics/src'); if (translateService) {
translateService.addTranslationFolder('ng2-activiti-analytics', 'node_modules/ng2-activiti-analytics/src');
} }
} }
@@ -146,7 +165,7 @@ export class AnalyticsReportParametersComponent implements OnInit, OnChanges {
} }
}, },
(err: any) => { (err: any) => {
console.log(err); this.logService.error(err);
this.onError.emit(err); this.onError.emit(err);
} }
); );
@@ -160,7 +179,7 @@ export class AnalyticsReportParametersComponent implements OnInit, OnChanges {
this.onSuccessParamOpt.emit(opts); this.onSuccessParamOpt.emit(opts);
}, },
(err: any) => { (err: any) => {
console.log(err); this.logService.error(err);
this.onError.emit(err); this.onError.emit(err);
} }
); );
@@ -231,9 +250,24 @@ export class AnalyticsReportParametersComponent implements OnInit, OnChanges {
this.onEdit.emit(this.reportParameters.name); this.onEdit.emit(this.reportParameters.name);
}, },
(err: any) => { (err: any) => {
console.log(err); this.logService.error(err);
this.onError.emit(err); this.onError.emit(err);
} }
); );
} }
ngAfterViewChecked() {
// workaround for MDL issues with dynamic components
if (componentHandler) {
componentHandler.upgradeAllRegistered();
}
}
toggleParameters() {
this.hideParameters = !this.hideParameters;
}
isParametersHide() {
return this.hideParameters;
}
} }
@@ -1,25 +1 @@
.chart {display: block; width: 100%;} .chart {display: block; width: 100%;}
.dropdown-widget {
width: 100%;
}
.dropdown-widget__select {
width: 100%;
}
.dropdown-widget__invalid .dropdown-widget__select {
border-color: #d50000;
}
.dropdown-widget__invalid .dropdown-widget__label {
color: #d50000;
}
.dropdown-widget__invalid .dropdown-widget__label:after {
background-color: #d50000;
}
.dropdown-widget__invalid .mdl-textfield__error {
visibility: visible !important;
}
@@ -5,83 +5,8 @@
(onEdit)="onEditReport($event)"> (onEdit)="onEditReport($event)">
</analytics-report-parameters> </analytics-report-parameters>
<div *ngIf="reports"> <activiti-analytics-generator [reportId]="reportId"
<div *ngFor="let report of reports"> [reportParamQuery]="reportParamQuery"
<h4>{{report.title}}</h4> #analyticsgenerator>
<div [ngSwitch]="report.type"> </activiti-analytics-generator>
<div *ngSwitchCase="'pie'"> </div>
<div class="col-md-6">
<div *ngIf="!report.hasData()">{{'ANALYTICS.MESSAGES.NO-DATA-FOUND' | translate}}</div>
<base-chart *ngIf="report.hasData()" class="chart"
[data]="report.data"
[labels]="report.labels"
[chartType]="report.type"></base-chart>
</div>
</div>
<div *ngSwitchCase="'table'">
<div *ngIf="!report.hasDatasets()">{{'ANALYTICS.MESSAGES.NO-DATA-FOUND' | translate}}</div>
<div [attr.id]="'chart-table-' + report.id" *ngIf="report.hasDatasets()">
<table class="table table-responsive table-condensed" style="width: 100%">
<tr>
<th *ngFor="let label of report.labels">{{label | translate}}</th>
</tr>
<tr *ngFor="let rows of report.datasets" style="text-align: center;">
<td *ngFor="let row of rows">{{row | translate }}</td>
</tr>
</table>
</div>
</div>
<div *ngSwitchCase="'masterDetailTable'">
<div *ngIf="!report.hasDatasets()">{{'ANALYTICS.MESSAGES.NO-DATA-FOUND' | translate}}</div>
<div [attr.id]="'chart-master-detail-table-' + report.id" *ngIf="report.hasDatasets()">
<table class="table table-responsive table-condensed" style="width: 100%">
<tr>
<th *ngFor="let label of report.labels">{{label | translate}}</th>
</tr>
<tr *ngFor="let rows of report.datasets" style="text-align: center;">
<td *ngFor="let row of rows">{{row | translate }}</td>
</tr>
</table>
</div>
</div>
<div *ngSwitchCase="'bar'">
<div class="col-md-6">
<div *ngIf="!report.hasDatasets()">{{'ANALYTICS.MESSAGES.NO-DATA-FOUND' | translate}}</div>
<base-chart *ngIf="report.hasDatasets()" class="chart"
[datasets]="report.datasets"
[labels]="report.labels"
[options]="report.options"
[chartType]="report.type"></base-chart>
</div>
</div>
<div *ngSwitchCase="'multiBar'">
<div class="col-md-6">
<div *ngIf="!report.hasDatasets()">{{'ANALYTICS.MESSAGES.NO-DATA-FOUND' | translate}}</div>
<div *ngIf="report.hasDatasets()">
<label class="mdl-checkbox mdl-js-checkbox mdl-js-ripple-effect" [attr.for]="'stacked-id'">
<input type="checkbox" [attr.id]="'stacked-id'" class="mdl-checkbox__input"
[checked]="report.options.scales.xAxes[0].stacked"
[(ngModel)]="report.options.scales.xAxes[0].stacked"
(change)="refresh(report)">
<span class="mdl-checkbox__label">Stacked</span>
</label>
<base-chart class="chart"
[datasets]="report.datasets"
[labels]="report.labels"
[options]="report.options"
[chartType]="'bar'"></base-chart>
</div>
</div>
</div>
<div *ngSwitchCase="'HeatMap'">
<analytics-report-heat-map [report]="report"></analytics-report-heat-map>
</div>
<div *ngSwitchDefault>
<span>{{'ANALYTICS.MESSAGES.UNKNOWN-WIDGET-TYPE' | translate}}: {{report.type}}</span>
</div>
</div>
</div>
</div>
<br><br><br>
<div *ngIf="!reports">{{'ANALYTICS.MESSAGES.FILL-PARAMETER' | translate}}</div>
</div>
@@ -16,25 +16,23 @@
*/ */
import { ComponentFixture, TestBed, async } from '@angular/core/testing'; import { ComponentFixture, TestBed, async } from '@angular/core/testing';
import { import { DebugElement } from '@angular/core';
CoreModule import { Observable } from 'rxjs/Rx';
} from 'ng2-alfresco-core'; import { CHART_DIRECTIVES } from 'ng2-charts/ng2-charts';
import { CoreModule, AlfrescoTranslationService } from 'ng2-alfresco-core';
import { DiagramsModule } from 'ng2-activiti-diagrams'; import { DiagramsModule } from 'ng2-activiti-diagrams';
import { AnalyticsReportListComponent } from '../components/analytics-report-list.component'; import { AnalyticsReportListComponent } from '../components/analytics-report-list.component';
import { AnalyticsComponent } from '../components/analytics.component'; import { AnalyticsComponent } from '../components/analytics.component';
import { AnalyticsGeneratorComponent } from '../components/analytics-generator.component';
import { AnalyticsReportParametersComponent } from '../components/analytics-report-parameters.component'; import { AnalyticsReportParametersComponent } from '../components/analytics-report-parameters.component';
import { AnalyticsReportHeatMapComponent } from '../components/analytics-report-heat-map.component'; import { AnalyticsReportHeatMapComponent } from '../components/analytics-report-heat-map.component';
import { WIDGET_DIRECTIVES } from '../components/widgets/index'; import { WIDGET_DIRECTIVES } from '../components/widgets/index';
import { CHART_DIRECTIVES } from 'ng2-charts/ng2-charts';
import { Chart } from '../models/chart.model';
import { AnalyticsService } from '../services/analytics.service'; import { AnalyticsService } from '../services/analytics.service';
import { ReportQuery } from '../models/report.model';
import { DebugElement, SimpleChange } from '@angular/core';
import * as analyticMock from '../assets/analyticsComponent.mock';
export const ANALYTICS_DIRECTIVES: any[] = [ export const ANALYTICS_DIRECTIVES: any[] = [
AnalyticsComponent, AnalyticsComponent,
AnalyticsGeneratorComponent,
AnalyticsReportParametersComponent, AnalyticsReportParametersComponent,
AnalyticsReportListComponent, AnalyticsReportListComponent,
AnalyticsReportHeatMapComponent, AnalyticsReportHeatMapComponent,
@@ -47,7 +45,7 @@ export const ANALYTICS_PROVIDERS: any[] = [
declare let jasmine: any; declare let jasmine: any;
declare let mdDateTimePicker: any; declare let mdDateTimePicker: any;
describe('Test ng2-activiti-analytics Report ', () => { describe('AnalyticsComponent', () => {
let component: any; let component: any;
let fixture: ComponentFixture<AnalyticsComponent>; let fixture: ComponentFixture<AnalyticsComponent>;
@@ -59,8 +57,8 @@ describe('Test ng2-activiti-analytics Report ', () => {
beforeEach(async(() => { beforeEach(async(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [ imports: [
CoreModule, CoreModule.forRoot(),
DiagramsModule DiagramsModule.forRoot()
], ],
declarations: [ declarations: [
...ANALYTICS_DIRECTIVES, ...ANALYTICS_DIRECTIVES,
@@ -70,6 +68,10 @@ describe('Test ng2-activiti-analytics Report ', () => {
...ANALYTICS_PROVIDERS ...ANALYTICS_PROVIDERS
] ]
}).compileComponents(); }).compileComponents();
let translateService = TestBed.get(AlfrescoTranslationService);
spyOn(translateService, 'addTranslationFolder').and.stub();
spyOn(translateService, 'get').and.callFake((key) => { return Observable.of(key); });
})); }));
beforeEach(() => { beforeEach(() => {
@@ -93,136 +95,5 @@ describe('Test ng2-activiti-analytics Report ', () => {
jasmine.Ajax.uninstall(); jasmine.Ajax.uninstall();
}); });
it('Should render the Process definition overview report ', (done) => {
component.onSuccess.subscribe((res) => {
expect(res).toBeDefined();
expect(res.length).toEqual(3);
expect(res[0]).toBeDefined();
expect(res[0].type).toEqual('table');
expect(res[0].datasets).toBeDefined();
expect(res[0].datasets.length).toEqual(4);
expect(res[0].datasets[0][0]).toEqual('__KEY_REPORTING.DEFAULT-REPORTS.PROCESS-DEFINITION-OVERVIEW.GENERAL-TABLE-TOTAL-PROCESS-DEFINITIONS');
expect(res[0].datasets[0][1]).toEqual('9');
expect(res[0].datasets[1][0]).toEqual('__KEY_REPORTING.DEFAULT-REPORTS.PROCESS-DEFINITION-OVERVIEW.GENERAL-TABLE-TOTAL-PROCESS-INSTANCES');
expect(res[0].datasets[1][1]).toEqual('41');
expect(res[0].datasets[2][0]).toEqual('__KEY_REPORTING.DEFAULT-REPORTS.PROCESS-DEFINITION-OVERVIEW.GENERAL-TABLE-ACTIVE-PROCESS-INSTANCES');
expect(res[0].datasets[2][1]).toEqual('3');
expect(res[0].datasets[3][0]).toEqual('__KEY_REPORTING.DEFAULT-REPORTS.PROCESS-DEFINITION-OVERVIEW.GENERAL-TABLE-COMPLETED-PROCESS-INSTANCES');
expect(res[0].datasets[3][1]).toEqual('38');
expect(res[1]).toBeDefined();
expect(res[1].type).toEqual('pie');
expect(res[2]).toBeDefined();
expect(res[2].type).toEqual('table');
done();
});
let reportParamQuery = new ReportQuery({status: 'All'});
component.appId = 1;
component.reportId = 1001;
component.showReport(reportParamQuery);
jasmine.Ajax.requests.mostRecent().respondWith({
status: 200,
contentType: 'json',
responseText: analyticMock.chartProcessDefOverview
});
});
it('Should render the Task overview report ', (done) => {
component.onSuccess.subscribe((res) => {
expect(res).toBeDefined();
expect(res.length).toEqual(3);
expect(res[0]).toBeDefined();
expect(res[0].type).toEqual('bar');
expect(res[0].labels).toBeDefined();
expect(res[0].labels.length).toEqual(2);
expect(res[0].labels[0]).toEqual('2016-09-30T00:00:00.000+0000');
expect(res[0].labels[1]).toEqual('2016-10-04T00:00:00.000+0000');
expect(res[0].datasets[0].label).toEqual('series1');
expect(res[0].datasets[0].data[0]).toEqual(3);
expect(res[0].datasets[0].data[1]).toEqual(1);
expect(res[1]).toBeDefined();
expect(res[1].type).toEqual('table');
expect(res[1].datasets).toBeDefined();
expect(res[1].datasets.length).toEqual(2);
expect(res[1].datasets[0][0]).toEqual('fake 1 user task');
expect(res[1].datasets[0][1]).toEqual('1');
expect(res[1].datasets[0][2]).toEqual('2.0');
expect(res[1].datasets[0][3]).toEqual('3.0');
expect(res[1].datasets[0][4]).toEqual('4.0');
expect(res[1].datasets[0][5]).toEqual('5.0');
expect(res[1].datasets[0][6]).toEqual('6.0');
expect(res[1].datasets[1][0]).toEqual('fake 2 user task');
expect(res[1].datasets[1][1]).toEqual('1');
expect(res[1].datasets[1][2]).toEqual('2.0');
expect(res[1].datasets[1][3]).toEqual('3.0');
expect(res[1].datasets[1][4]).toEqual('4.0');
expect(res[1].datasets[1][5]).toEqual('5.0');
expect(res[1].datasets[1][6]).toEqual('6.0');
expect(res[2]).toBeDefined();
expect(res[2].type).toEqual('multiBar');
expect(res[2].labels).toBeDefined();
expect(res[2].labels.length).toEqual(3);
expect(res[2].labels[0]).toEqual(1);
expect(res[2].labels[1]).toEqual(2);
expect(res[2].labels[2]).toEqual(3);
expect(res[2].datasets[0].label).toEqual('averages');
expect(res[2].datasets[0].data[0]).toEqual(0);
expect(res[2].datasets[0].data[1]).toEqual(5);
expect(res[2].datasets[0].data[2]).toEqual(2);
expect(res[2].datasets[1].label).toEqual('minima');
expect(res[2].datasets[1].data[0]).toEqual(0);
expect(res[2].datasets[1].data[1]).toEqual(0);
expect(res[2].datasets[1].data[2]).toEqual(0);
expect(res[2].datasets[2].label).toEqual('maxima');
expect(res[2].datasets[2].data[0]).toEqual(0);
expect(res[2].datasets[2].data[1]).toEqual(29);
expect(res[2].datasets[2].data[2]).toEqual(29);
done();
});
let reportParamQuery = new ReportQuery({status: 'All'});
component.reportId = 1;
component.showReport(reportParamQuery);
jasmine.Ajax.requests.mostRecent().respondWith({
status: 200,
contentType: 'json',
responseText: analyticMock.chartTaskOverview
});
});
it('Should reset the reports when the onChanged is call', () => {
let reportId = 1;
component.reports = [ new Chart({id: 'fake', type: 'fake-type'})];
let change = new SimpleChange(null, reportId);
component.ngOnChanges({ 'reportId': change });
expect(component.reports).toBeUndefined();
});
it('Should emit onError event with a 404 response ', (done) => {
component.onError.subscribe((err) => {
expect(err).toBeDefined();
done();
});
let reportParamQuery = new ReportQuery({status: 'All'});
component.reportId = 1;
component.showReport(reportParamQuery);
jasmine.Ajax.requests.mostRecent().respondWith({
status: 404,
contentType: 'json',
responseText: []
});
});
}); });
}); });
@@ -15,11 +15,11 @@
* limitations under the License. * limitations under the License.
*/ */
import { Component, EventEmitter, OnChanges, Input, Output, SimpleChanges } from '@angular/core'; import { Component, EventEmitter, OnChanges, Input, Output, SimpleChanges, ViewChild } from '@angular/core';
import { AlfrescoTranslationService } from 'ng2-alfresco-core'; import { AlfrescoTranslationService, LogService } from 'ng2-alfresco-core';
import { AnalyticsService } from '../services/analytics.service'; import { AnalyticsService } from '../services/analytics.service';
import { ReportQuery } from '../models/report.model'; import { ReportQuery } from '../models/report.model';
import { Chart } from '../models/chart.model'; import { AnalyticsGeneratorComponent } from './analytics-generator.component';
@Component({ @Component({
moduleId: module.id, moduleId: module.id,
@@ -30,7 +30,7 @@ import { Chart } from '../models/chart.model';
export class AnalyticsComponent implements OnChanges { export class AnalyticsComponent implements OnChanges {
@Input() @Input()
appId: string; appId: number;
@Input() @Input()
reportId: number; reportId: number;
@@ -38,80 +38,37 @@ export class AnalyticsComponent implements OnChanges {
@Input() @Input()
debug: boolean = false; debug: boolean = false;
@Output()
onSuccess = new EventEmitter();
@Output() @Output()
editReport = new EventEmitter(); editReport = new EventEmitter();
@Output() @ViewChild('analyticsgenerator')
onError = new EventEmitter(); analyticsgenerator: AnalyticsGeneratorComponent;
reportParamQuery = new ReportQuery(); reportParamQuery: ReportQuery;
reports: Chart[]; constructor(private translateService: AlfrescoTranslationService,
private analyticsService: AnalyticsService,
public barChartOptions: any = { private logService: LogService) {
responsive: true, logService.info('AnalyticsComponent');
scales: { if (translateService) {
yAxes: [{ translateService.addTranslationFolder('ng2-activiti-analytics', 'node_modules/ng2-activiti-analytics/src');
ticks: {
beginAtZero: true,
stepSize: 1
}
}],
xAxes: [{
ticks: {
},
stacked: true
}]
}
};
constructor(private translate: AlfrescoTranslationService,
private analyticsService: AnalyticsService) {
console.log('AnalyticsComponent');
if (translate) {
translate.addTranslationFolder('ng2-activiti-analytics', 'node_modules/ng2-activiti-analytics/src');
} }
} }
ngOnChanges(changes: SimpleChanges) { ngOnChanges(changes: SimpleChanges) {
this.reset(); this.analyticsgenerator.reset();
} }
public showReport($event) { public showReport($event) {
this.reportParamQuery = $event; this.analyticsgenerator.generateReport(this.reportId, $event);
this.analyticsService.getReportsByParams(this.reportId, this.reportParamQuery).subscribe(
(res: Chart[]) => {
this.reports = res;
this.onSuccess.emit(res);
},
(err: any) => {
this.onError.emit(err);
console.log(err);
}
);
} }
public reset() { public reset() {
if (this.reports) { this.analyticsgenerator.reset();
this.reports = undefined;
}
}
public refresh(report): void {
/**
* (My guess), for Angular to recognize the change in the dataset
* it has to change the dataset variable directly,
* so one way around it, is to clone the data, change it and then
* assign it;
*/
let clone = JSON.parse(JSON.stringify(report));
report.datasets = clone.datasets;
} }
public onEditReport(name: string) { public onEditReport(name: string) {
this.editReport.emit(name); this.editReport.emit(name);
} }
} }
@@ -2,7 +2,7 @@
<label class="dropdown-widget__label" [attr.for]="field.id">{{field.nameKey | translate}}</label> <label class="dropdown-widget__label" [attr.for]="field.id">{{field.nameKey | translate}}</label>
<select [formControlName]="controllerName" [attr.id]="'select-' + field.id" class="dropdown-widget__select" <select [formControlName]="controllerName" [attr.id]="'select-' + field.id" class="dropdown-widget__select"
[(ngModel)]="field.value" (ngModelChange)="changeValue($event)"> [(ngModel)]="field.value" (ngModelChange)="changeValue($event)">
<option *ngIf="showDefaultOption" value="">{{defaultOptionText}}</option> <option *ngIf="showDefaultOption" value="null">{{defaultOptionText}}</option>
<option *ngFor="let opt of field.options" [value]="opt.id">{{opt.label}}</option> <option *ngFor="let opt of field.options" [value]="opt.id">{{opt.label}}</option>
</select> </select>
</div> </div>
@@ -4,7 +4,9 @@
"MESSAGES": { "MESSAGES": {
"UNKNOWN-WIDGET-TYPE": "UNKNOWN WIDGET TYPE", "UNKNOWN-WIDGET-TYPE": "UNKNOWN WIDGET TYPE",
"FILL-PARAMETER": "Fill in the parameters to generate your report", "FILL-PARAMETER": "Fill in the parameters to generate your report",
"NO-DATA-FOUND": "No data found" "NO-DATA-FOUND": "No data found",
"ZERO-DATA-FOUND": "There are only zero values",
"SETTING-TITLE": "Change report setting"
} }
}, },
"__KEY_REPORTING": { "__KEY_REPORTING": {
@@ -1,5 +1,47 @@
{ {
"ANALYTICS": { "ANALYTICS": {
"TTILE": "ANALYTICS" "TTILE": "ANALYTICS",
"MESSAGES": {
"UNKNOWN-WIDGET-TYPE": "TIPO WIDGET SCONOSCIUTO",
"FILL-PARAMETER": "Riempi tutti i campi per generare il report",
"NO-DATA-FOUND": "Nessun valore trovato",
"ZERO-DATA-FOUND": "Ci sono solo valori che valgono zero",
"SETTING-TITLE": "Modifica i parametri del report"
}
},
"__KEY_REPORTING": {
"DEFAULT-REPORTS": {
"PROCESS-DEFINITION-OVERVIEW": {
"GENERAL-TABLE-TOTAL-PROCESS-DEFINITIONS": "Numero totale di process definitions",
"GENERAL-TABLE-TOTAL-PROCESS-INSTANCES": "Numero totale di process instances",
"GENERAL-TABLE-ACTIVE-PROCESS-INSTANCES": "Numero totale di process instances attivi",
"GENERAL-TABLE-COMPLETED-PROCESS-INSTANCES": "Numero totale di process instances completi"
}
}
},
"REPORTING": {
"DEFAULT-REPORTS": {
"PROCESS-HEAT-MAP": {
"TYPE-FILTERING": "Include tutti gli steps (Deselezionandolo, rimuoverai gli step come start events, gateways, etc.)?"
},
"PROCESS-INSTANCES-OVERVIEW": {
"PROCESS-DEFINITION": "Definizione del processo",
"DATE-RANGE": "Intervallo di Date",
"SLOW-PROC-INST-NUMBER": "Quanti process instances lenti vuoi mostrare?"
},
"TASK-OVERVIEW": {
"PROCESS-DEFINITION": "Definizione del processo",
"DATE-RANGE": "Intervallo di Date",
"DATE-RANGE-INTERVAL": "Aggrega date per"
},
"TASK-SLA": {
"TASK": "Task",
"PROCESS-DEFINITION": "Definizione del processo",
"DATE-RANGE": "Intervallo di Date",
"SLA-DURATION": "Qual' é il tempo che questo task necessita per essere completato per rimanere nella SLA?"
}
},
"PROCESS-STATUS": "Stato del processo",
"TASK-STATUS": "Stato del task"
} }
} }
@@ -49,6 +49,9 @@ export class Chart {
case 'processDefinitionHeatMap': case 'processDefinitionHeatMap':
chartType = 'HeatMap'; chartType = 'HeatMap';
break; break;
case 'masterDetailTable':
chartType = 'masterDetailTable';
break;
default: default:
chartType = 'table'; chartType = 'table';
break; break;
@@ -191,6 +194,22 @@ export class TableChart extends Chart {
} }
} }
export class DetailsTableChart extends TableChart {
detailsTable: any;
showDetails: boolean = false;
constructor(obj?: any) {
super(obj);
if (obj.detailTables) {
this.detailsTable = new TableChart(obj.detailTables[0]);
}
}
hasDetailsTable() {
return this.detailsTable ? true : false;
}
}
export class HeatMapChart extends Chart { export class HeatMapChart extends Chart {
avgTimePercentages: string; avgTimePercentages: string;
avgTimeValues: string; avgTimeValues: string;
@@ -236,7 +255,20 @@ export class PieChart extends Chart {
this.data.push(data); this.data.push(data);
} }
hasData() { hasData(): boolean {
return this.data && this.data.length > 0 ? true : false; return this.data && this.data.length > 0 ? true : false;
} }
hasZeroValues(): boolean {
let isZeroValues: boolean = false;
if (this.hasData()) {
isZeroValues = true;
this.data.forEach((value) => {
if (value.toString() !== '0') {
isZeroValues = false;
}
});
}
return isZeroValues;
}
} }
@@ -16,18 +16,17 @@
*/ */
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { AlfrescoAuthenticationService, AlfrescoSettingsService, AlfrescoApiService } from 'ng2-alfresco-core';
import { Observable } from 'rxjs/Rx'; import { Observable } from 'rxjs/Rx';
import { Response } from '@angular/http'; import { Response } from '@angular/http';
import { AlfrescoApiService, LogService } from 'ng2-alfresco-core';
import { ReportParametersModel, ParameterValueModel } from '../models/report.model'; import { ReportParametersModel, ParameterValueModel } from '../models/report.model';
import { Chart, PieChart, TableChart, BarChart, HeatMapChart, MultiBarChart } from '../models/chart.model'; import { Chart, PieChart, TableChart, BarChart, HeatMapChart, MultiBarChart, DetailsTableChart } from '../models/chart.model';
@Injectable() @Injectable()
export class AnalyticsService { export class AnalyticsService {
constructor(private authService: AlfrescoAuthenticationService, constructor(private apiService: AlfrescoApiService,
public apiService: AlfrescoApiService, private logService: LogService) {
private alfrescoSettingsService: AlfrescoSettingsService) {
} }
/** /**
@@ -43,14 +42,14 @@ export class AnalyticsService {
reports.push(reportModel); reports.push(reportModel);
}); });
return reports; return reports;
}).catch(this.handleError); }).catch(err => this.handleError(err));
} }
getReportParams(reportId: string): Observable<any> { getReportParams(reportId: string): Observable<any> {
return Observable.fromPromise(this.apiService.getInstance().activiti.reportApi.getReportParams(reportId)) return Observable.fromPromise(this.apiService.getInstance().activiti.reportApi.getReportParams(reportId))
.map((res: any) => { .map((res: any) => {
return new ReportParametersModel(res); return new ReportParametersModel(res);
}).catch(this.handleError); }).catch(err => this.handleError(err));
} }
getParamValuesByType(type: string, appId: string, reportId?: string, processDefinitionId?: string) { getParamValuesByType(type: string, appId: string, reportId?: string, processDefinitionId?: string) {
@@ -123,7 +122,7 @@ export class AnalyticsService {
paramOptions.push(new ParameterValueModel(opt)); paramOptions.push(new ParameterValueModel(opt));
}); });
return paramOptions; return paramOptions;
}).catch(this.handleError); }).catch(err => this.handleError(err));
} }
getProcessDefinitionsValues(appId: string): Observable<any> { getProcessDefinitionsValues(appId: string): Observable<any> {
@@ -134,7 +133,7 @@ export class AnalyticsService {
paramOptions.push(new ParameterValueModel(opt)); paramOptions.push(new ParameterValueModel(opt));
}); });
return paramOptions; return paramOptions;
}).catch(this.handleError); }).catch(err => this.handleError(err));
} }
getTasksByProcessDefinitionId(reportId: string, processDefinitionId: string): Observable<any> { getTasksByProcessDefinitionId(reportId: string, processDefinitionId: string): Observable<any> {
@@ -145,7 +144,7 @@ export class AnalyticsService {
paramOptions.push(new ParameterValueModel({ id: opt, name: opt })); paramOptions.push(new ParameterValueModel({ id: opt, name: opt }));
}); });
return paramOptions; return paramOptions;
}).catch(this.handleError); }).catch(err => this.handleError(err));
} }
getReportsByParams(reportId: number, paramsQuery: any): Observable<any> { getReportsByParams(reportId: number, paramsQuery: any): Observable<any> {
@@ -160,7 +159,7 @@ export class AnalyticsService {
} else if (chartData.type === 'processDefinitionHeatMap') { } else if (chartData.type === 'processDefinitionHeatMap') {
elements.push(new HeatMapChart(chartData)); elements.push(new HeatMapChart(chartData));
} else if (chartData.type === 'masterDetailTable') { } else if (chartData.type === 'masterDetailTable') {
elements.push(new TableChart(chartData)); elements.push(new DetailsTableChart(chartData));
} else if (chartData.type === 'barChart') { } else if (chartData.type === 'barChart') {
elements.push(new BarChart(chartData)); elements.push(new BarChart(chartData));
} else if (chartData.type === 'multiBarChart') { } else if (chartData.type === 'multiBarChart') {
@@ -169,24 +168,24 @@ export class AnalyticsService {
}); });
return elements; return elements;
}).catch(this.handleError); }).catch(err => this.handleError(err));
} }
public createDefaultReports(): Observable<any> { createDefaultReports(): Observable<any> {
return Observable.fromPromise(this.apiService.getInstance().activiti.reportApi.createDefaultReports()) return Observable.fromPromise(this.apiService.getInstance().activiti.reportApi.createDefaultReports())
.map(this.toJson) .map(this.toJson)
.catch(this.handleError); .catch(err => this.handleError(err));
} }
public updateReport(reportId: number, name: string): Observable<any> { updateReport(reportId: number, name: string): Observable<any> {
return Observable.fromPromise(this.apiService.getInstance().activiti.reportApi.updateReport(reportId, name)) return Observable.fromPromise(this.apiService.getInstance().activiti.reportApi.updateReport(reportId, name))
.map((res: any) => { .map((res: any) => {
console.log('upload'); this.logService.info('upload');
}).catch(this.handleError); }).catch(err => this.handleError(err));
} }
private handleError(error: Response) { private handleError(error: Response) {
console.error(error); this.logService.error(error);
return Observable.throw(error.json().error || 'Server error'); return Observable.throw(error.json().error || 'Server error');
} }
@@ -62,6 +62,9 @@ Follow the 3 steps below:
<script src="node_modules/material-design-lite/material.min.js"></script> <script src="node_modules/material-design-lite/material.min.js"></script>
<link rel="stylesheet" href="node_modules/material-design-icons/iconfont/material-icons.css"> <link rel="stylesheet" href="node_modules/material-design-icons/iconfont/material-icons.css">
<!-- Load the Angular Material 2 stylesheet -->
<link href="node_modules/@angular/material/core/theming/prebuilt/deeppurple-amber.css" rel="stylesheet">
<!-- Polyfill(s) for Safari (pre-10.x) --> <!-- Polyfill(s) for Safari (pre-10.x) -->
<script src="node_modules/intl/dist/Intl.min.js"></script> <script src="node_modules/intl/dist/Intl.min.js"></script>
<script src="node_modules/intl/locale-data/jsonp/en.js"></script> <script src="node_modules/intl/locale-data/jsonp/en.js"></script>
@@ -39,17 +39,18 @@
"@angular/http": "2.2.2", "@angular/http": "2.2.2",
"@angular/platform-browser": "2.2.2", "@angular/platform-browser": "2.2.2",
"@angular/platform-browser-dynamic": "2.2.2", "@angular/platform-browser-dynamic": "2.2.2",
"@angular/material": "2.0.0-beta.1",
"@angular/router": "3.2.2", "@angular/router": "3.2.2",
"@angular/upgrade": "2.2.2", "@angular/upgrade": "2.2.2",
"alfresco-js-api": "^1.0.0", "alfresco-js-api": "~1.1.0",
"core-js": "^2.4.1", "core-js": "^2.4.1",
"dialog-polyfill": "^0.4.3", "dialog-polyfill": "^0.4.3",
"element.scrollintoviewifneeded-polyfill": "^1.0.1", "element.scrollintoviewifneeded-polyfill": "^1.0.1",
"intl": "1.2.4", "intl": "1.2.4",
"material-design-icons": "2.2.3", "material-design-icons": "2.2.3",
"material-design-lite": "1.2.1", "material-design-lite": "1.2.1",
"ng2-activiti-diagrams": "^1.0.0", "ng2-activiti-diagrams": "^1.1.0",
"ng2-alfresco-core": "1.0.0", "ng2-alfresco-core": "1.1.0",
"ng2-translate": "2.5.0", "ng2-translate": "2.5.0",
"raphael": "^2.2.6", "raphael": "^2.2.6",
"reflect-metadata": "^0.1.3", "reflect-metadata": "^0.1.3",
@@ -19,40 +19,39 @@ import { NgModule, Component } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser'; import { BrowserModule } from '@angular/platform-browser';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { CoreModule, AlfrescoSettingsService, AlfrescoAuthenticationService, StorageService } from 'ng2-alfresco-core'; import { CoreModule, AlfrescoSettingsService, AlfrescoAuthenticationService, StorageService, LogService } from 'ng2-alfresco-core';
import { DiagramsModule } from 'ng2-activiti-diagrams'; import { DiagramsModule } from 'ng2-activiti-diagrams';
@Component({ @Component({
selector: 'alfresco-app-demo', selector: 'alfresco-app-demo',
template: ` template: `
<label for="ticket"><b>Insert a valid ticket:</b></label><br> <label for="ticket"><b>Insert a valid ticket:</b></label><br>
<input id="ticket" type="text" size="48" (change)="updateTicket()" [(ngModel)]="ticket"><br> <input id="ticket" type="text" size="48" (change)="updateTicket()" [(ngModel)]="ticket"><br>
<label for="host"><b>Insert the ip of your Activiti instance:</b></label><br> <label for="host"><b>Insert the ip of your Activiti instance:</b></label><br>
<input id="host" type="text" size="48" (change)="updateHost()" [(ngModel)]="host"><br><br> <input id="host" type="text" size="48" (change)="updateHost()" [(ngModel)]="host"><br><br>
<div *ngIf="!authenticated" style="color:#FF2323"> <div *ngIf="!authenticated" style="color:#FF2323">
Authentication failed to ip {{ host }} with user: admin, admin, you can still try to add a valid ticket to perform Authentication failed to ip {{ host }} with user: admin, admin, you can still try to add a valid ticket to perform
operations. operations.
</div> </div>
<hr> <hr>
<label for="processDefinitionId"><b>Insert the ProcessDefinitionId:</b></label><br> <label for="processDefinitionId"><b>Insert the ProcessDefinitionId:</b></label><br>
<input id="processDefinitionId" size="70" type="text" [(ngModel)]="processDefinitionId"> <input id="processDefinitionId" size="70" type="text" [(ngModel)]="processDefinitionId">
<activiti-diagram [processDefinitionId]="processDefinitionId"></activiti-diagram>` <activiti-diagram [processDefinitionId]="processDefinitionId"></activiti-diagram>
`
}) })
export class DiagramDemoComponent { export class DiagramDemoComponent {
processDefinitionId: string = 'ThirdProcess:1:15053'; processDefinitionId: string = 'ThirdProcess:1:15053';
authenticated: boolean; authenticated: boolean;
host: string = 'http://localhost:9999'; host: string = 'http://localhost:9999';
ticket: string; ticket: string;
constructor(private authService: AlfrescoAuthenticationService, constructor(private authService: AlfrescoAuthenticationService,
private settingsService: AlfrescoSettingsService, private settingsService: AlfrescoSettingsService,
private storage: StorageService) { private storage: StorageService,
private logService: LogService) {
settingsService.bpmHost = this.host; settingsService.bpmHost = this.host;
settingsService.setProviders('BPM'); settingsService.setProviders('BPM');
@@ -77,12 +76,12 @@ export class DiagramDemoComponent {
login() { login() {
this.authService.login('admin', 'admin').subscribe( this.authService.login('admin', 'admin').subscribe(
ticket => { ticket => {
console.log(ticket); this.logService.info(`Logged in with ticket ${ticket}`);
this.ticket = this.authService.getTicketBpm(); this.ticket = this.authService.getTicketBpm();
this.authenticated = true; this.authenticated = true;
}, },
error => { error => {
console.log(error); this.logService.error(error);
this.authenticated = false; this.authenticated = false;
}); });
} }
@@ -1,7 +1,14 @@
// Tun on full stack traces in errors to help debugging // Tun on full stack traces in errors to help debugging
Error.stackTraceLimit = Infinity; Error.stackTraceLimit = Infinity;
jasmine.DEFAULT_TIMEOUT_INTERVAL = 1000; jasmine.DEFAULT_TIMEOUT_INTERVAL = 100000;
window.componentHandler = {
upgradeAllRegistered: function () {
},
upgradeElement: function () {
}
};
__karma__.loaded = function() {}; __karma__.loaded = function() {};
@@ -39,6 +46,8 @@ var map = {
'@angular/http': 'npm:@angular/http/bundles/http.umd.js', '@angular/http': 'npm:@angular/http/bundles/http.umd.js',
'@angular/router': 'npm:@angular/router/bundles/router.umd.js', '@angular/router': 'npm:@angular/router/bundles/router.umd.js',
'@angular/forms': 'npm:@angular/forms/bundles/forms.umd.js', '@angular/forms': 'npm:@angular/forms/bundles/forms.umd.js',
'@angular/material': 'npm:@angular/material/bundles/material.umd.js',
// testing // testing
'@angular/core/testing': 'npm:@angular/core/bundles/core-testing.umd.js', '@angular/core/testing': 'npm:@angular/core/bundles/core-testing.umd.js',
'@angular/common/testing': 'npm:@angular/common/bundles/common-testing.umd.js', '@angular/common/testing': 'npm:@angular/common/bundles/common-testing.umd.js',
@@ -1,13 +1,13 @@
{ {
"name": "ng2-activiti-diagrams", "name": "ng2-activiti-diagrams",
"description": "Activiti Angular2 Diagrams Component", "description": "Activiti Angular2 Diagrams Component",
"version": "1.0.0", "version": "1.1.0",
"author": "Alfresco Software, Ltd.", "author": "Alfresco Software, Ltd.",
"scripts": { "scripts": {
"clean": "npm install rimraf && npm run clean-build && rimraf dist node_modules typings", "clean": "npm install rimraf && npm run clean-build && rimraf dist node_modules typings",
"clean-build": "rimraf index.js index.js.map index.d.ts'src/{,**/}**.js' 'src/{,**/}**.js.map' 'src/{,**/}**.d.ts' bundles", "clean-build": "rimraf index.js index.js.map index.d.ts'src/{,**/}**.js' 'src/{,**/}**.js.map' 'src/{,**/}**.d.ts' bundles",
"build": "npm run clean-build && npm run tslint && rimraf dist && tsc && license-check && npm run build.umd", "build": "npm run clean-build && npm run tslint && rimraf dist && tsc && license-check && npm run build.umd",
"build:w": "npm run clean-build && npm run tslint && rimraf dist && tsc:w && license-check npm run build.umd", "build:w": "npm run clean-build && npm run tslint && rimraf dist && npm run tsc:w && license-check && npm run build.umd",
"tslint": "tslint -c tslint.json 'src/{,**/}**.ts' 'index.ts' -e '{,**/}**.d.ts' -e './gulpfile.ts'", "tslint": "tslint -c tslint.json 'src/{,**/}**.ts' 'index.ts' -e '{,**/}**.d.ts' -e './gulpfile.ts'",
"tsc": "tsc", "tsc": "tsc",
"tsc:w": "tsc -w", "tsc:w": "tsc -w",
@@ -16,7 +16,7 @@
"test-browser": "npm run build && concurrently \"karma start karma.conf.js --reporters kjhtml\" \"npm run watch-task\"", "test-browser": "npm run build && concurrently \"karma start karma.conf.js --reporters kjhtml\" \"npm run watch-task\"",
"posttest": "remap-istanbul -i coverage/report/coverage-final.json -o coverage/report -t html && remap-istanbul -i coverage/report/coverage-final.json -o coverage/report/coverage-final.json", "posttest": "remap-istanbul -i coverage/report/coverage-final.json -o coverage/report -t html && remap-istanbul -i coverage/report/coverage-final.json -o coverage/report/coverage-final.json",
"coverage": "npm run test && wsrv -o -p 9875 ./coverage/report", "coverage": "npm run test && wsrv -o -p 9875 ./coverage/report",
"prepublish": "npm run build", "prepublish": "npm run test",
"travis": "npm link ng2-alfresco-core", "travis": "npm link ng2-alfresco-core",
"gulp": "gulp", "gulp": "gulp",
"build.umd": "gulp build.prod --color --env-config prod --build-type prod", "build.umd": "gulp build.prod --color --env-config prod --build-type prod",
@@ -47,21 +47,21 @@
"@angular/http": "2.0.0", "@angular/http": "2.0.0",
"@angular/platform-browser": "2.0.0", "@angular/platform-browser": "2.0.0",
"@angular/platform-browser-dynamic": "2.0.0", "@angular/platform-browser-dynamic": "2.0.0",
"@angular/material": "2.0.0-beta.1",
"core-js": "^2.4.1", "core-js": "^2.4.1",
"reflect-metadata": "^0.1.3", "reflect-metadata": "^0.1.3",
"rxjs": "5.0.0-beta.12", "rxjs": "5.0.0-beta.12",
"systemjs": "0.19.27", "systemjs": "0.19.27",
"zone.js": "^0.6.23", "zone.js": "^0.6.23",
"raphael": "^2.2.6", "raphael": "^2.2.6",
"chart.js": "^2.1.4",
"ng2-charts": "1.1.0",
"ng2-translate": "2.5.0", "ng2-translate": "2.5.0",
"alfresco-js-api": "^1.0.0", "alfresco-js-api": "~1.1.0",
"ng2-alfresco-core": "1.0.0" "ng2-alfresco-core": "1.1.0"
}, },
"devDependencies": { "devDependencies": {
"@types/jasmine": "^2.2.33", "@types/jasmine": "^2.2.33",
"@types/node": "^6.0.42", "@types/node": "^6.0.42",
"autoprefixer": "^6.5.4",
"concurrently": "^2.2.0", "concurrently": "^2.2.0",
"cpx": "1.3.1", "cpx": "1.3.1",
"cssnano": "^3.8.1", "cssnano": "^3.8.1",
@@ -80,6 +80,7 @@
"gulp-template": "^4.0.0", "gulp-template": "^4.0.0",
"gulp-typescript": "^3.1.3", "gulp-typescript": "^3.1.3",
"gulp-uglify": "^2.0.0", "gulp-uglify": "^2.0.0",
"gulp-util": "^3.0.7",
"intl": "^1.2.5", "intl": "^1.2.5",
"jasmine-core": "2.4.1", "jasmine-core": "2.4.1",
"karma": "0.13.22", "karma": "0.13.22",
@@ -90,6 +91,7 @@
"karma-jasmine-html-reporter": "0.2.0", "karma-jasmine-html-reporter": "0.2.0",
"karma-mocha-reporter": "2.0.3", "karma-mocha-reporter": "2.0.3",
"license-check": "1.1.5", "license-check": "1.1.5",
"merge-stream": "^1.0.1",
"remap-istanbul": "0.6.3", "remap-istanbul": "0.6.3",
"rimraf": "2.5.2", "rimraf": "2.5.2",
"run-sequence": "^1.2.2", "run-sequence": "^1.2.2",
@@ -16,10 +16,7 @@
*/ */
import { ComponentFixture, TestBed, async } from '@angular/core/testing'; import { ComponentFixture, TestBed, async } from '@angular/core/testing';
import { import { CoreModule, AlfrescoTranslationService } from 'ng2-alfresco-core';
CoreModule,
AlfrescoTranslationService
} from 'ng2-alfresco-core';
import { DIAGRAM_DIRECTIVES, DIAGRAM_PROVIDERS } from './index'; import { DIAGRAM_DIRECTIVES, DIAGRAM_PROVIDERS } from './index';
import { RAPHAEL_DIRECTIVES, RAPHAEL_PROVIDERS } from './raphael/index'; import { RAPHAEL_DIRECTIVES, RAPHAEL_PROVIDERS } from './raphael/index';
@@ -50,7 +47,7 @@ describe('Test ng2-activiti-diagrams ', () => {
beforeEach(async(() => { beforeEach(async(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [ imports: [
CoreModule CoreModule.forRoot()
], ],
declarations: [ declarations: [
...DIAGRAM_DIRECTIVES, ...DIAGRAM_DIRECTIVES,
@@ -16,7 +16,7 @@
*/ */
import { Component, ElementRef, Input, Output, EventEmitter, SimpleChanges } from '@angular/core'; import { Component, ElementRef, Input, Output, EventEmitter, SimpleChanges } from '@angular/core';
import { AlfrescoTranslationService } from 'ng2-alfresco-core'; import { AlfrescoTranslationService, LogService } from 'ng2-alfresco-core';
import { DiagramsService } from '../services/diagrams.service'; import { DiagramsService } from '../services/diagrams.service';
import { DiagramColorService } from '../services/diagram-color.service'; import { DiagramColorService } from '../services/diagram-color.service';
import { RaphaelService } from './raphael/raphael.service'; import { RaphaelService } from './raphael/raphael.service';
@@ -57,17 +57,16 @@ export class DiagramComponent {
PADDING_HEIGHT: number = 60; PADDING_HEIGHT: number = 60;
private diagram: DiagramModel; private diagram: DiagramModel;
private elementRef: ElementRef;
constructor(elementRef: ElementRef, constructor(private elementRef: ElementRef,
private translate: AlfrescoTranslationService, private translateService: AlfrescoTranslationService,
private diagramColorService: DiagramColorService, private diagramColorService: DiagramColorService,
private raphaelService: RaphaelService, private raphaelService: RaphaelService,
private diagramsService: DiagramsService) { private diagramsService: DiagramsService,
if (translate) { private logService: LogService) {
translate.addTranslationFolder('ng2-activiti-diagrams', 'node_modules/ng2-activiti-diagrams/src'); if (translateService) {
translateService.addTranslationFolder('ng2-activiti-diagrams', 'node_modules/ng2-activiti-diagrams/src');
} }
this.elementRef = elementRef;
} }
ngOnChanges(changes: SimpleChanges) { ngOnChanges(changes: SimpleChanges) {
@@ -86,7 +85,7 @@ export class DiagramComponent {
}, },
(err: any) => { (err: any) => {
this.onError.emit(err); this.onError.emit(err);
console.log(err); this.logService.error(err);
} }
); );
} }
@@ -16,6 +16,7 @@
*/ */
import { Directive, OnInit, ElementRef, Input, Output, EventEmitter } from '@angular/core'; import { Directive, OnInit, ElementRef, Input, Output, EventEmitter } from '@angular/core';
import { LogService } from 'ng2-alfresco-core';
import { Point } from './models/point'; import { Point } from './models/point';
import { RaphaelBase } from './raphael-base'; import { RaphaelBase } from './raphael-base';
import { RaphaelService } from './raphael.service'; import { RaphaelService } from './raphael.service';
@@ -43,19 +44,20 @@ export class RaphaelMultilineTextDirective extends RaphaelBase implements OnInit
TEXT_PADDING = 3; TEXT_PADDING = 3;
constructor(public elementRef: ElementRef, constructor(public elementRef: ElementRef,
raphaelService: RaphaelService) { raphaelService: RaphaelService,
private logService: LogService) {
super(elementRef, raphaelService); super(elementRef, raphaelService);
} }
ngOnInit() { ngOnInit() {
console.log(this.elementRef); this.logService.log(this.elementRef);
if (this.text === null || this.text === undefined) { if (this.text === null || this.text === undefined) {
this.text = ''; this.text = '';
} }
this.draw(this.position, this.text); this.draw(this.position, this.text);
} }
public draw(position: Point, text: string) { draw(position: Point, text: string) {
let textPaper = this.paper.text(position.x + this.TEXT_PADDING, position.y + this.TEXT_PADDING, text).attr({ let textPaper = this.paper.text(position.x + this.TEXT_PADDING, position.y + this.TEXT_PADDING, text).attr({
'text-anchor': 'middle', 'text-anchor': 'middle',
'font-family': 'Arial', 'font-family': 'Arial',
@@ -16,27 +16,28 @@
*/ */
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { AlfrescoAuthenticationService, AlfrescoSettingsService } from 'ng2-alfresco-core';
import { Observable } from 'rxjs/Rx'; import { Observable } from 'rxjs/Rx';
import { Response, Http, Headers, RequestOptions } from '@angular/http'; import { Response, Http, Headers, RequestOptions } from '@angular/http';
import { AlfrescoAuthenticationService, AlfrescoSettingsService, LogService } from 'ng2-alfresco-core';
@Injectable() @Injectable()
export class DiagramsService { export class DiagramsService {
constructor(private authService: AlfrescoAuthenticationService, constructor(private authService: AlfrescoAuthenticationService,
private http: Http, private http: Http,
private alfrescoSettingsService: AlfrescoSettingsService) { private settingsService: AlfrescoSettingsService,
private logService: LogService) {
} }
getProcessDefinitionModel(processDefinitionId: string): Observable<any> { getProcessDefinitionModel(processDefinitionId: string): Observable<any> {
let url = `${this.alfrescoSettingsService.getBPMApiBaseUrl()}/app/rest/process-definitions/${processDefinitionId}/model-json`; let url = `${this.settingsService.getBPMApiBaseUrl()}/app/rest/process-definitions/${processDefinitionId}/model-json`;
let options = this.getRequestOptions(); let options = this.getRequestOptions();
return this.http return this.http
.get(url, options) .get(url, options)
.map((res: any) => { .map((res: any) => {
let body = res.json(); let body = res.json();
return body; return body;
}).catch(this.handleError); }).catch(err => this.handleError(err));
} }
public getHeaders(): Headers { public getHeaders(): Headers {
@@ -53,7 +54,7 @@ export class DiagramsService {
} }
private handleError(error: Response) { private handleError(error: Response) {
console.error(error); this.logService.error(error);
return Observable.throw(error.json().error || 'Server error'); return Observable.throw(error.json().error || 'Server error');
} }
} }
+92 -5
View File
@@ -31,6 +31,22 @@
</a> </a>
</p> </p>
## Library Contents
### Components
- [ActivitiForm](#activitiform-component)
- ActivitiStartForm
### Services
- [FormService](#formservice)
- ActivitiAlfrescoContentService
- EcmModelService
- FormRenderingService
- NodeService
- WidgetVisibilityService
## Prerequisites ## Prerequisites
Before you start using this development framework, make sure you have installed all required software and done all the Before you start using this development framework, make sure you have installed all required software and done all the
@@ -65,6 +81,9 @@ Follow the 3 steps below:
<script src="node_modules/material-design-lite/material.min.js"></script> <script src="node_modules/material-design-lite/material.min.js"></script>
<link rel="stylesheet" href="node_modules/material-design-icons/iconfont/material-icons.css"> <link rel="stylesheet" href="node_modules/material-design-icons/iconfont/material-icons.css">
<!-- Load the Angular Material 2 stylesheet -->
<link href="node_modules/@angular/material/core/theming/prebuilt/deeppurple-amber.css" rel="stylesheet">
<!-- Polyfill(s) for Safari (pre-10.x) --> <!-- Polyfill(s) for Safari (pre-10.x) -->
<script src="node_modules/intl/dist/Intl.min.js"></script> <script src="node_modules/intl/dist/Intl.min.js"></script>
<script src="node_modules/intl/locale-data/jsonp/en.js"></script> <script src="node_modules/intl/locale-data/jsonp/en.js"></script>
@@ -98,7 +117,9 @@ Follow the 3 steps below:
Please refer to the following example file: [systemjs.config.js](demo/systemjs Please refer to the following example file: [systemjs.config.js](demo/systemjs
.config.js) . .config.js) .
## Basic usage examples ## ActivitiForm Component
### Basic usage
The component shows a Form from Activiti The component shows a Form from Activiti
@@ -109,6 +130,7 @@ The component shows a Form from Activiti
Usage example of this component : Usage example of this component :
**main.ts** **main.ts**
```ts ```ts
import { NgModule, Component } from '@angular/core'; import { NgModule, Component } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser'; import { BrowserModule } from '@angular/platform-browser';
@@ -220,9 +242,9 @@ and store the form field as metadata. The param nameNode is optional.
</activiti-form> </activiti-form>
``` ```
## Configuration ### Configuration
### Properties #### Properties
The recommended set of properties can be found in the following table: The recommended set of properties can be found in the following table:
@@ -238,11 +260,12 @@ The recommended set of properties can be found in the following table:
| `showDebugButton` | boolean | false | Toggle debug options. | | `showDebugButton` | boolean | false | Toggle debug options. |
| `readOnly` | boolean | false | Toggle readonly state of the form. Enforces all form widgets render readonly if enabled. | | `readOnly` | boolean | false | Toggle readonly state of the form. Enforces all form widgets render readonly if enabled. |
| `showRefreshButton` | boolean | true | Toggle rendering of the `Refresh` button. | | `showRefreshButton` | boolean | true | Toggle rendering of the `Refresh` button. |
| `showValidationIcon` | boolean | true | Toggle rendering of the validation icon next form title. |
| `saveMetadata` | boolean | false | Store the value of the form as metadata. | | `saveMetadata` | boolean | false | Store the value of the form as metadata. |
| `path` | string | | Path of the folder where to store the metadata. | | `path` | string | | Path of the folder where to store the metadata. |
| `nameNode` (optional) | string | true | Name to assign to the new node where the metadata are stored. | | `nameNode` (optional) | string | true | Name to assign to the new node where the metadata are stored. |
#### Advanced properties ##### Advanced properties
The following properties are for complex customisation purposes: The following properties are for complex customisation purposes:
@@ -251,7 +274,7 @@ The recommended set of properties can be found in the following table:
| `form` | FormModel | | Underlying form model instance. | | `form` | FormModel | | Underlying form model instance. |
| `debugMode` | boolean | false | Toggle debug mode, allows displaying additional data for development and debugging purposes. | | `debugMode` | boolean | false | Toggle debug mode, allows displaying additional data for development and debugging purposes. |
### Events #### Events
| Name | Description | | Name | Description |
| --- | --- | | --- | --- |
@@ -328,6 +351,70 @@ There are two additional functions that can be of a great value when controlling
**Please note that if `event.preventDefault()` is not called then default outcome behaviour **Please note that if `event.preventDefault()` is not called then default outcome behaviour
will also be executed after your custom code.** will also be executed after your custom code.**
## FormService
```ts
import { Component } from '@angular/core';
import { FormService, FormEvent, FormFieldEvent } from 'ng2-activiti-form';
@Component(...)
class MyComponent {
constructor(private formService: FormService) {
formService.formLoaded.subscribe((e: FormEvent) => {
console.log(`Form loaded: ${e.form.id}`);
});
formService.formFieldValueChanged.subscribe((e: FormFieldEvent) => {
console.log(`Field value changed. Form: ${e.form.id}, Field: ${e.field.id}, Value: ${e.field.value}`);
});
}
}
```
### Events
| Name | Args Type | Description |
| --- | --- | --- |
| formLoaded | FormEvent | Raised when form has been loaded or reloaded |
| formFieldValueChanged | FormFieldEvent | Raised when input values change |
| taskCompleted | FormEvent | Raised when a task is completed successfully |
| taskCompletedError | FormErrorEvent | Raised when a task is completed unsuccessfully |
| taskSaved | FormEvent | Raised when a task is saved successfully |
| taskSavedError | FormErrorEvent | Raised when a task is saved unsuccessfully |
### Methods
| Name | Params | Returns | Description |
| --- | --- | --- | --- |
| createFormFromANode | (formName: string) | Observable\<any\> | Create a Form with a fields for each metadata properties |
| createForm | (formName: string) | Observable\<any\> | Create a Form |
| addFieldsToAForm | (formId: string, formModel: FormDefinitionModel) | Observable\<any\> | Add Fileds to A form |
| searchFrom | (name: string) | Observable\<any\> | Search For A Form by name |
| getForms | n/a | Observable\<any\> | Get All the forms |
| getProcessDefinitions | n/a | Observable\<any\> | Get Process Definitions |
| getTasks | n/a | Observable\<any\> | Get All the Tasks |
| getTask | (taskId: string) | Observable\<any\> | Get Task |
| saveTaskForm | (taskId: string, formValues: FormValues) | Observable\<any\> | Save Task Form |
| completeTaskForm | (taskId: string, formValues: FormValues, outcome?: string) | Observable\<any\> | Complete Task Form |
| getTaskForm | (taskId: string) | Observable\<any\> | Get Form related to a taskId |
| getFormDefinitionById | (formId: string) | Observable\<any\> | Get Form Definition |
| getFormDefinitionByName | (name: string) | Observable\<any\> | Returns form definition by a given name. |
| getStartFormInstance | (processId: string) | Observable\<any\> | Get start form instance for a given processId |
| getStartFormDefinition | (processId: string) | Observable\<any\> | Get start form definition for a given process |
| createTemporaryRawRelatedContent | (file: any) | Observable\<any\> | Save File |
| getRestFieldValues | (taskId: string, field: string) | Observable\<any\> | |
| getRestFieldValuesByProcessId | (processDefinitionId: string, field: string) | Observable\<any\> | |
| getRestFieldValuesColumnByProcessId | (processDefinitionId: string, field: string, column?: string) | Observable\<any\> | |
| getRestFieldValuesColumn | (taskId: string, field: string, column?: string) | Observable\<any\> | |
| getWorkflowGroups\* | (filter: string, groupId?: string) | Observable\<GroupModel[]\> | |
| getWorkflowUsers\* | (filter: string, groupId?: string) | Observable\<GroupUserModel[]\> | |
\* _Uses private Activiti WebApp api_
## See also ## See also
- [Form Stencils with Angular 2](docs/stencils.md) - [Form Stencils with Angular 2](docs/stencils.md)
@@ -39,6 +39,7 @@
"@angular/http": "2.2.2", "@angular/http": "2.2.2",
"@angular/platform-browser": "2.2.2", "@angular/platform-browser": "2.2.2",
"@angular/platform-browser-dynamic": "2.2.2", "@angular/platform-browser-dynamic": "2.2.2",
"@angular/material": "2.0.0-beta.1",
"@angular/router": "3.2.2", "@angular/router": "3.2.2",
"@angular/upgrade": "2.2.2", "@angular/upgrade": "2.2.2",
"core-js": "^2.4.1", "core-js": "^2.4.1",
@@ -54,9 +55,9 @@
"moment": "2.15.1", "moment": "2.15.1",
"md-date-time-picker": "^2.2.0", "md-date-time-picker": "^2.2.0",
"ng2-translate": "2.5.0", "ng2-translate": "2.5.0",
"alfresco-js-api": "^1.0.0", "alfresco-js-api": "~1.1.0",
"ng2-alfresco-core": "1.0.0", "ng2-alfresco-core": "1.1.0",
"ng2-activiti-form": "^1.0.0" "ng2-activiti-form": "^1.1.0"
}, },
"devDependencies": { "devDependencies": {
"@types/jasmine": "^2.2.33", "@types/jasmine": "^2.2.33",
@@ -18,40 +18,39 @@
import { NgModule, Component, OnInit } from '@angular/core'; import { NgModule, Component, OnInit } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser'; import { BrowserModule } from '@angular/platform-browser';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { CoreModule, AlfrescoSettingsService, AlfrescoAuthenticationService, StorageService } from 'ng2-alfresco-core'; import { CoreModule, AlfrescoSettingsService, AlfrescoAuthenticationService, StorageService, LogService } from 'ng2-alfresco-core';
import { ActivitiFormModule } from 'ng2-activiti-form'; import { ActivitiFormModule } from 'ng2-activiti-form';
@Component({ @Component({
selector: 'alfresco-app-demo', selector: 'alfresco-app-demo',
template: ` template: `
<label for="ticket"><b>Insert a valid ticket:</b></label><br> <label for="ticket"><b>Insert a valid ticket:</b></label><br>
<input id="ticket" type="text" size="48" (change)="updateTicket()" [(ngModel)]="ticket"><br> <input id="ticket" type="text" size="48" (change)="updateTicket()" [(ngModel)]="ticket"><br>
<label for="host"><b>Insert the ip of your Activiti instance:</b></label><br> <label for="host"><b>Insert the ip of your Activiti instance:</b></label><br>
<input id="host" type="text" size="48" (change)="updateHost()" [(ngModel)]="host"><br><br> <input id="host" type="text" size="48" (change)="updateHost()" [(ngModel)]="host"><br><br>
<div *ngIf="!authenticated" style="color:#FF2323"> <div *ngIf="!authenticated" style="color:#FF2323">
Authentication failed to ip {{ host }} with user: admin, admin, you can still try to add a valid ticket to perform Authentication failed to ip {{ host }} with user: admin, admin, you can still try to add a valid ticket to perform
operations. operations.
</div> </div>
<hr> <hr>
<label for="taskId"><b>Insert the taskId:</b></label><br> <label for="taskId"><b>Insert the taskId:</b></label><br>
<input id="taskId" size="10" type="text" [(ngModel)]="taskId"> <input id="taskId" size="10" type="text" [(ngModel)]="taskId">
<activiti-form [taskId]="taskId"></activiti-form>` <activiti-form [taskId]="taskId"></activiti-form>
`
}) })
export class FormDemoComponent implements OnInit { export class FormDemoComponent implements OnInit {
taskId: number; taskId: number;
authenticated: boolean; authenticated: boolean;
host: string = 'http://localhost:9999'; host: string = 'http://localhost:9999';
ticket: string; ticket: string;
constructor(private authService: AlfrescoAuthenticationService, constructor(private authService: AlfrescoAuthenticationService,
private settingsService: AlfrescoSettingsService, private settingsService: AlfrescoSettingsService,
private storage: StorageService) { private storage: StorageService,
private logService: LogService) {
settingsService.bpmHost = this.host; settingsService.bpmHost = this.host;
settingsService.setProviders('BPM'); settingsService.setProviders('BPM');
@@ -76,12 +75,12 @@ export class FormDemoComponent implements OnInit {
login() { login() {
this.authService.login('admin', 'admin').subscribe( this.authService.login('admin', 'admin').subscribe(
ticket => { ticket => {
console.log(ticket); this.logService.info(`Logged in with ticket ${ticket}`);
this.ticket = this.authService.getTicketBpm(); this.ticket = this.authService.getTicketBpm();
this.authenticated = true; this.authenticated = true;
}, },
error => { error => {
console.log(error); this.logService.error(error);
this.authenticated = false; this.authenticated = false;
}); });
} }
@@ -37,6 +37,7 @@ export * from './src/components/widgets/index';
export * from './src/services/ecm-model.service'; export * from './src/services/ecm-model.service';
export * from './src/services/node.service'; export * from './src/services/node.service';
export * from './src/services/form-rendering.service'; export * from './src/services/form-rendering.service';
export * from './src/events/index';
export const ACTIVITI_FORM_DIRECTIVES: any[] = [ export const ACTIVITI_FORM_DIRECTIVES: any[] = [
ActivitiForm, ActivitiForm,
@@ -1,7 +1,14 @@
// Tun on full stack traces in errors to help debugging // Tun on full stack traces in errors to help debugging
Error.stackTraceLimit = Infinity; Error.stackTraceLimit = Infinity;
jasmine.DEFAULT_TIMEOUT_INTERVAL = 1000; jasmine.DEFAULT_TIMEOUT_INTERVAL = 100000;
window.componentHandler = {
upgradeAllRegistered: function () {
},
upgradeElement: function () {
}
};
__karma__.loaded = function() {}; __karma__.loaded = function() {};
@@ -39,6 +46,8 @@ var map = {
'@angular/http': 'npm:@angular/http/bundles/http.umd.js', '@angular/http': 'npm:@angular/http/bundles/http.umd.js',
'@angular/router': 'npm:@angular/router/bundles/router.umd.js', '@angular/router': 'npm:@angular/router/bundles/router.umd.js',
'@angular/forms': 'npm:@angular/forms/bundles/forms.umd.js', '@angular/forms': 'npm:@angular/forms/bundles/forms.umd.js',
'@angular/material': 'npm:@angular/material/bundles/material.umd.js',
// testing // testing
'@angular/core/testing': 'npm:@angular/core/bundles/core-testing.umd.js', '@angular/core/testing': 'npm:@angular/core/bundles/core-testing.umd.js',
'@angular/common/testing': 'npm:@angular/common/bundles/common-testing.umd.js', '@angular/common/testing': 'npm:@angular/common/bundles/common-testing.umd.js',
@@ -1,13 +1,13 @@
{ {
"name": "ng2-activiti-form", "name": "ng2-activiti-form",
"description": "Alfresco Activiti Form Component for Angular 2", "description": "Alfresco Activiti Form Component for Angular 2",
"version": "1.0.0", "version": "1.1.0",
"author": "Alfresco Software, Ltd.", "author": "Alfresco Software, Ltd.",
"scripts": { "scripts": {
"clean": "npm install rimraf && npm run clean-build && rimraf dist node_modules typings", "clean": "npm install rimraf && npm run clean-build && rimraf dist node_modules typings",
"clean-build": "rimraf index.js index.js.map index.d.ts'src/{,**/}**.js' 'src/{,**/}**.js.map' 'src/{,**/}**.d.ts' bundles", "clean-build": "rimraf index.js index.js.map index.d.ts'src/{,**/}**.js' 'src/{,**/}**.js.map' 'src/{,**/}**.d.ts' bundles",
"build": "npm run clean-build && npm run tslint && rimraf dist && tsc && license-check && npm run build.umd", "build": "npm run clean-build && npm run tslint && rimraf dist && tsc && license-check && npm run build.umd",
"build:w": "npm run clean-build && npm run tslint && rimraf dist && tsc:w && license-check npm run build.umd", "build:w": "npm run clean-build && npm run tslint && rimraf dist && npm run tsc:w && license-check && npm run build.umd",
"tslint": "tslint -c tslint.json 'src/{,**/}**.ts' 'index.ts' -e '{,**/}**.d.ts' -e './gulpfile.ts'", "tslint": "tslint -c tslint.json 'src/{,**/}**.ts' 'index.ts' -e '{,**/}**.d.ts' -e './gulpfile.ts'",
"tsc": "tsc", "tsc": "tsc",
"tsc:w": "tsc -w", "tsc:w": "tsc -w",
@@ -16,7 +16,7 @@
"test-browser": "npm run build && concurrently \"karma start karma.conf.js --reporters kjhtml\" \"npm run watch-task\"", "test-browser": "npm run build && concurrently \"karma start karma.conf.js --reporters kjhtml\" \"npm run watch-task\"",
"posttest": "remap-istanbul -i coverage/report/coverage-final.json -o coverage/report -t html && remap-istanbul -i coverage/report/coverage-final.json -o coverage/report/coverage-final.json", "posttest": "remap-istanbul -i coverage/report/coverage-final.json -o coverage/report -t html && remap-istanbul -i coverage/report/coverage-final.json -o coverage/report/coverage-final.json",
"coverage": "npm run test && wsrv -o -p 9875 ./coverage/report", "coverage": "npm run test && wsrv -o -p 9875 ./coverage/report",
"prepublish": "npm run build", "prepublish": "npm run test",
"travis": "npm link ng2-alfresco-core", "travis": "npm link ng2-alfresco-core",
"gulp": "gulp", "gulp": "gulp",
"build.umd": "gulp build.prod --color --env-config prod --build-type prod", "build.umd": "gulp build.prod --color --env-config prod --build-type prod",
@@ -54,6 +54,7 @@
"@angular/http": "2.0.0", "@angular/http": "2.0.0",
"@angular/platform-browser": "2.0.0", "@angular/platform-browser": "2.0.0",
"@angular/platform-browser-dynamic": "2.0.0", "@angular/platform-browser-dynamic": "2.0.0",
"@angular/material": "2.0.0-beta.1",
"core-js": "^2.4.1", "core-js": "^2.4.1",
"reflect-metadata": "^0.1.3", "reflect-metadata": "^0.1.3",
"rxjs": "5.0.0-beta.12", "rxjs": "5.0.0-beta.12",
@@ -62,13 +63,13 @@
"moment": "2.15.1", "moment": "2.15.1",
"md-date-time-picker": "^2.2.0", "md-date-time-picker": "^2.2.0",
"ng2-translate": "2.5.0", "ng2-translate": "2.5.0",
"alfresco-js-api": "^1.0.0", "alfresco-js-api": "~1.1.0",
"ng2-alfresco-core": "1.0.0" "ng2-alfresco-core": "1.1.0"
}, },
"devDependencies": { "devDependencies": {
"@types/jasmine": "^2.2.33", "@types/jasmine": "^2.2.33",
"@types/node": "^6.0.42", "@types/node": "^6.0.42",
"autoprefixer": "^6.5.1", "autoprefixer": "^6.5.4",
"concurrently": "^2.2.0", "concurrently": "^2.2.0",
"cpx": "^1.3.1", "cpx": "^1.3.1",
"cssnano": "^3.8.1", "cssnano": "^3.8.1",
@@ -87,6 +88,7 @@
"gulp-template": "^4.0.0", "gulp-template": "^4.0.0",
"gulp-typescript": "^3.1.3", "gulp-typescript": "^3.1.3",
"gulp-uglify": "^2.0.0", "gulp-uglify": "^2.0.0",
"gulp-util": "^3.0.7",
"intl": "^1.2.5", "intl": "^1.2.5",
"jasmine-ajax": "^3.2.0", "jasmine-ajax": "^3.2.0",
"jasmine-core": "2.4.1", "jasmine-core": "2.4.1",
@@ -98,6 +100,7 @@
"karma-jasmine-html-reporter": "^0.2.0", "karma-jasmine-html-reporter": "^0.2.0",
"karma-mocha-reporter": "^2.0.3", "karma-mocha-reporter": "^2.0.3",
"license-check": "^1.0.4", "license-check": "^1.0.4",
"merge-stream": "^1.0.1",
"remap-istanbul": "^0.6.3", "remap-istanbul": "^0.6.3",
"rimraf": "2.5.2", "rimraf": "2.5.2",
"run-sequence": "^1.2.2", "run-sequence": "^1.2.2",
@@ -5,7 +5,7 @@
<div *ngIf="hasForm()"> <div *ngIf="hasForm()">
<div class="mdl-card mdl-shadow--2dp activiti-form-container"> <div class="mdl-card mdl-shadow--2dp activiti-form-container">
<div class="mdl-card__title"> <div class="mdl-card__title">
<i class="material-icons">{{ form.isValid ? 'event_available' : 'event_busy' }}</i> <i *ngIf="showValidationIcon" class="material-icons">{{ form.isValid ? 'event_available' : 'event_busy' }}</i>
<h2 *ngIf="isTitleEnabled()" class="mdl-card__title-text">{{form.taskName}}</h2> <h2 *ngIf="isTitleEnabled()" class="mdl-card__title-text">{{form.taskName}}</h2>
</div> </div>
<div class="mdl-card__media"> <div class="mdl-card__media">
@@ -24,7 +24,7 @@
alfresco-mdl-button alfresco-mdl-button
[disabled]="!isOutcomeButtonEnabled(outcome)" [disabled]="!isOutcomeButtonEnabled(outcome)"
[class.mdl-button--colored]="!outcome.isSystem" [class.mdl-button--colored]="!outcome.isSystem"
[class.activiti-form-hide-button]="!isOutcomeButtonVisible(outcome)" [class.activiti-form-hide-button]="!isOutcomeButtonVisible(outcome, form.readOnly)"
(click)="onOutcomeClicked(outcome, $event)"> (click)="onOutcomeClicked(outcome, $event)">
{{outcome.name}} {{outcome.name}}
</button> </button>
@@ -17,6 +17,7 @@
import { Observable } from 'rxjs/Rx'; import { Observable } from 'rxjs/Rx';
import { SimpleChange } from '@angular/core'; import { SimpleChange } from '@angular/core';
import { LogService } from 'ng2-alfresco-core';
import { ActivitiForm } from './activiti-form.component'; import { ActivitiForm } from './activiti-form.component';
import { FormModel, FormOutcomeModel, FormFieldModel, FormOutcomeEvent, FormFieldTypes } from './widgets/index'; import { FormModel, FormOutcomeModel, FormFieldModel, FormOutcomeEvent, FormFieldTypes } from './widgets/index';
import { FormService } from './../services/form.service'; import { FormService } from './../services/form.service';
@@ -30,6 +31,7 @@ describe('ActivitiForm', () => {
let formComponent: ActivitiForm; let formComponent: ActivitiForm;
let visibilityService: WidgetVisibilityService; let visibilityService: WidgetVisibilityService;
let nodeService: NodeService; let nodeService: NodeService;
let logService: LogService;
beforeEach(() => { beforeEach(() => {
componentHandler = jasmine.createSpyObj('componentHandler', [ componentHandler = jasmine.createSpyObj('componentHandler', [
@@ -37,11 +39,12 @@ describe('ActivitiForm', () => {
]); ]);
window['componentHandler'] = componentHandler; window['componentHandler'] = componentHandler;
visibilityService = new WidgetVisibilityService(null); logService = new LogService();
visibilityService = new WidgetVisibilityService(null, logService);
spyOn(visibilityService, 'refreshVisibility').and.stub(); spyOn(visibilityService, 'refreshVisibility').and.stub();
formService = new FormService(null, null); formService = new FormService(null, null, logService);
nodeService = new NodeService(null, null); nodeService = new NodeService(null);
formComponent = new ActivitiForm(formService, visibilityService, null, nodeService); formComponent = new ActivitiForm(formService, visibilityService, null, nodeService, logService);
}); });
it('should upgrade MDL content on view checked', () => { it('should upgrade MDL content on view checked', () => {
@@ -98,35 +101,73 @@ describe('ActivitiForm', () => {
}); });
it('should not enable outcome button when model missing', () => { it('should not enable outcome button when model missing', () => {
expect(formComponent.isOutcomeButtonVisible(null)).toBeFalsy(); expect(formComponent.isOutcomeButtonVisible(null, false)).toBeFalsy();
}); });
it('should enable custom outcome buttons', () => { it('should enable custom outcome buttons', () => {
let formModel = new FormModel(); let formModel = new FormModel();
formComponent.form = formModel;
let outcome = new FormOutcomeModel(formModel, {id: 'action1', name: 'Action 1'}); let outcome = new FormOutcomeModel(formModel, {id: 'action1', name: 'Action 1'});
expect(formComponent.isOutcomeButtonVisible(outcome)).toBeTruthy(); expect(formComponent.isOutcomeButtonVisible(outcome, formComponent.form.readOnly)).toBeTruthy();
}); });
it('should allow controlling [complete] button visibility', () => { it('should allow controlling [complete] button visibility', () => {
let formModel = new FormModel(); let formModel = new FormModel();
formComponent.form = formModel;
let outcome = new FormOutcomeModel(formModel, {id: '$save', name: FormOutcomeModel.SAVE_ACTION}); let outcome = new FormOutcomeModel(formModel, {id: '$save', name: FormOutcomeModel.SAVE_ACTION});
formComponent.showSaveButton = true; formComponent.showSaveButton = true;
expect(formComponent.isOutcomeButtonVisible(outcome)).toBeTruthy(); expect(formComponent.isOutcomeButtonVisible(outcome, formComponent.form.readOnly)).toBeTruthy();
formComponent.showSaveButton = false; formComponent.showSaveButton = false;
expect(formComponent.isOutcomeButtonVisible(outcome)).toBeFalsy(); expect(formComponent.isOutcomeButtonVisible(outcome, formComponent.form.readOnly)).toBeFalsy();
});
it('should show only [complete] button with readOnly form ', () => {
let formModel = new FormModel();
formModel.readOnly = true;
formComponent.form = formModel;
let outcome = new FormOutcomeModel(formModel, {id: '$complete', name: FormOutcomeModel.COMPLETE_ACTION});
formComponent.showCompleteButton = true;
expect(formComponent.isOutcomeButtonVisible(outcome, formComponent.form.readOnly)).toBeTruthy();
});
it('should not show [save] button with readOnly form ', () => {
let formModel = new FormModel();
formModel.readOnly = true;
formComponent.form = formModel;
let outcome = new FormOutcomeModel(formModel, {id: '$save', name: FormOutcomeModel.SAVE_ACTION});
formComponent.showSaveButton = true;
expect(formComponent.isOutcomeButtonVisible(outcome, formComponent.form.readOnly)).toBeFalsy();
});
it('should show [custom-outcome] button with readOnly form and selected custom-outcome', () => {
let formModel = new FormModel({selectedOutcome: 'custom-outcome'});
formModel.readOnly = true;
formComponent.form = formModel;
let outcome = new FormOutcomeModel(formModel, {id: '$customoutome', name: 'custom-outcome'});
formComponent.showCompleteButton = true;
formComponent.showSaveButton = true;
expect(formComponent.isOutcomeButtonVisible(outcome, formComponent.form.readOnly)).toBeTruthy();
outcome = new FormOutcomeModel(formModel, {id: '$customoutome2', name: 'custom-outcome2'});
expect(formComponent.isOutcomeButtonVisible(outcome, formComponent.form.readOnly)).toBeFalsy();
}); });
it('should allow controlling [save] button visibility', () => { it('should allow controlling [save] button visibility', () => {
let formModel = new FormModel(); let formModel = new FormModel();
formModel.readOnly = false;
formComponent.form = formModel;
let outcome = new FormOutcomeModel(formModel, {id: '$save', name: FormOutcomeModel.COMPLETE_ACTION}); let outcome = new FormOutcomeModel(formModel, {id: '$save', name: FormOutcomeModel.COMPLETE_ACTION});
formComponent.showCompleteButton = true; formComponent.showCompleteButton = true;
expect(formComponent.isOutcomeButtonVisible(outcome)).toBeTruthy(); expect(formComponent.isOutcomeButtonVisible(outcome, formComponent.form.readOnly)).toBeTruthy();
formComponent.showCompleteButton = false; formComponent.showCompleteButton = false;
expect(formComponent.isOutcomeButtonVisible(outcome)).toBeFalsy(); expect(formComponent.isOutcomeButtonVisible(outcome, formComponent.form.readOnly)).toBeFalsy();
}); });
it('should load form on refresh', () => { it('should load form on refresh', () => {
@@ -560,13 +601,6 @@ describe('ActivitiForm', () => {
expect(formService.completeTaskForm).not.toHaveBeenCalled(); expect(formService.completeTaskForm).not.toHaveBeenCalled();
}); });
it('should log error to console by default', () => {
const error = 'Error';
spyOn(console, 'log').and.stub();
formComponent.handleError(error);
expect(console.log).toHaveBeenCalledWith(error);
});
it('should complete form form and raise corresponding event', () => { it('should complete form form and raise corresponding event', () => {
spyOn(formService, 'completeTaskForm').and.callFake(() => { spyOn(formService, 'completeTaskForm').and.callFake(() => {
return Observable.create(observer => { return Observable.create(observer => {
@@ -15,18 +15,13 @@
* limitations under the License. * limitations under the License.
*/ */
import { import { Component, OnInit, AfterViewChecked, OnChanges, SimpleChanges, Input, Output, EventEmitter } from '@angular/core';
Component, import { LogService } from 'ng2-alfresco-core';
OnInit, AfterViewChecked, OnChanges,
SimpleChanges,
Input,
Output,
EventEmitter
} from '@angular/core';
import { EcmModelService } from './../services/ecm-model.service'; import { EcmModelService } from './../services/ecm-model.service';
import { FormService } from './../services/form.service'; import { FormService } from './../services/form.service';
import { NodeService } from './../services/node.service'; import { NodeService } from './../services/node.service';
import { FormModel, FormOutcomeModel, FormValues, FormFieldModel, FormOutcomeEvent } from './widgets/core/index'; import { FormModel, FormOutcomeModel, FormValues, FormFieldModel, FormOutcomeEvent } from './widgets/core/index';
import { FormEvent, FormErrorEvent } from './../events/index';
import { WidgetVisibilityService } from './../services/widget-visibility.service'; import { WidgetVisibilityService } from './../services/widget-visibility.service';
@@ -85,6 +80,7 @@ export class ActivitiForm implements OnInit, AfterViewChecked, OnChanges {
static SAVE_OUTCOME_ID: string = '$save'; static SAVE_OUTCOME_ID: string = '$save';
static COMPLETE_OUTCOME_ID: string = '$complete'; static COMPLETE_OUTCOME_ID: string = '$complete';
static START_PROCESS_OUTCOME_ID: string = '$startProcess';
static CUSTOM_OUTCOME_ID: string = '$custom'; static CUSTOM_OUTCOME_ID: string = '$custom';
@Input() @Input()
@@ -129,6 +125,9 @@ export class ActivitiForm implements OnInit, AfterViewChecked, OnChanges {
@Input() @Input()
showRefreshButton: boolean = true; showRefreshButton: boolean = true;
@Input()
showValidationIcon: boolean = true;
@Output() @Output()
formSaved: EventEmitter<FormModel> = new EventEmitter<FormModel>(); formSaved: EventEmitter<FormModel> = new EventEmitter<FormModel>();
@@ -149,9 +148,10 @@ export class ActivitiForm implements OnInit, AfterViewChecked, OnChanges {
debugMode: boolean = false; debugMode: boolean = false;
constructor(protected formService: FormService, constructor(protected formService: FormService,
public visibilityService: WidgetVisibilityService, protected visibilityService: WidgetVisibilityService,
private ecmModelService: EcmModelService, private ecmModelService: EcmModelService,
private nodeService: NodeService) { private nodeService: NodeService,
private logService: LogService) {
} }
hasForm(): boolean { hasForm(): boolean {
@@ -182,14 +182,20 @@ export class ActivitiForm implements OnInit, AfterViewChecked, OnChanges {
return false; return false;
} }
isOutcomeButtonVisible(outcome: FormOutcomeModel): boolean { isOutcomeButtonVisible(outcome: FormOutcomeModel, isFormReadOnly: boolean): boolean {
if (outcome && outcome.name) { if (outcome && outcome.name) {
if (outcome.name === FormOutcomeModel.COMPLETE_ACTION) { if (outcome.name === FormOutcomeModel.COMPLETE_ACTION) {
return this.showCompleteButton; return this.showCompleteButton;
} }
if (isFormReadOnly) {
return outcome.isSelected ;
}
if (outcome.name === FormOutcomeModel.SAVE_ACTION) { if (outcome.name === FormOutcomeModel.SAVE_ACTION) {
return this.showSaveButton; return this.showSaveButton;
} }
if (outcome.name === FormOutcomeModel.START_PROCESS_ACTION) {
return false;
}
return true; return true;
} }
return false; return false;
@@ -252,15 +258,20 @@ export class ActivitiForm implements OnInit, AfterViewChecked, OnChanges {
return true; return true;
} }
if (outcome.id === ActivitiForm.START_PROCESS_OUTCOME_ID) {
this.completeTaskForm();
return true;
}
if (outcome.id === ActivitiForm.CUSTOM_OUTCOME_ID) { if (outcome.id === ActivitiForm.CUSTOM_OUTCOME_ID) {
this.formSaved.emit(this.form); this.onTaskSaved(this.form);
this.storeFormAsMetadata(); this.storeFormAsMetadata();
return true; return true;
} }
} else { } else {
// Note: Activiti is using NAME field rather than ID for outcomes // Note: Activiti is using NAME field rather than ID for outcomes
if (outcome.name) { if (outcome.name) {
this.formSaved.emit(this.form); this.onTaskSaved(this.form);
this.completeTaskForm(outcome.name); this.completeTaskForm(outcome.name);
return true; return true;
} }
@@ -326,8 +337,8 @@ export class ActivitiForm implements OnInit, AfterViewChecked, OnChanges {
.getTaskForm(taskId) .getTaskForm(taskId)
.subscribe( .subscribe(
form => { form => {
this.form = new FormModel(form, data, this.readOnly); this.form = new FormModel(form, data, this.readOnly, this.formService);
this.formLoaded.emit(this.form); this.onFormLoaded(this.form);
}, },
(error) => { (error) => {
this.handleError(error); this.handleError(error);
@@ -342,7 +353,7 @@ export class ActivitiForm implements OnInit, AfterViewChecked, OnChanges {
form => { form => {
this.formName = form.name; this.formName = form.name;
this.form = this.parseForm(form); this.form = this.parseForm(form);
this.formLoaded.emit(this.form); this.onFormLoaded(this.form);
}, },
(error) => { (error) => {
this.handleError(error); this.handleError(error);
@@ -357,9 +368,8 @@ export class ActivitiForm implements OnInit, AfterViewChecked, OnChanges {
id => { id => {
this.formService.getFormDefinitionById(id).subscribe( this.formService.getFormDefinitionById(id).subscribe(
form => { form => {
// console.log('Get Form By Form definition Name', form);
this.form = this.parseForm(form); this.form = this.parseForm(form);
this.formLoaded.emit(this.form); this.onFormLoaded(this.form);
}, },
(error) => { (error) => {
this.handleError(error); this.handleError(error);
@@ -378,12 +388,10 @@ export class ActivitiForm implements OnInit, AfterViewChecked, OnChanges {
.saveTaskForm(this.form.taskId, this.form.values) .saveTaskForm(this.form.taskId, this.form.values)
.subscribe( .subscribe(
() => { () => {
this.formSaved.emit(this.form); this.onTaskSaved(this.form);
this.storeFormAsMetadata(); this.storeFormAsMetadata();
}, },
(error) => { error => this.onTaskSavedError(this.form, error)
this.handleError(error);
}
); );
} }
} }
@@ -394,24 +402,22 @@ export class ActivitiForm implements OnInit, AfterViewChecked, OnChanges {
.completeTaskForm(this.form.taskId, this.form.values, outcome) .completeTaskForm(this.form.taskId, this.form.values, outcome)
.subscribe( .subscribe(
() => { () => {
this.formCompleted.emit(this.form); this.onTaskCompleted(this.form);
this.storeFormAsMetadata(); this.storeFormAsMetadata();
}, },
(error) => { error => this.onTaskCompletedError(this.form, error)
this.handleError(error);
}
); );
} }
} }
handleError(err: any): any { handleError(err: any): any {
console.log(err); this.logService.error(err);
this.onError.emit(err); this.onError.emit(err);
} }
parseForm(json: any): FormModel { parseForm(json: any): FormModel {
if (json) { if (json) {
let form = new FormModel(json, this.data, this.readOnly); let form = new FormModel(json, this.data, this.readOnly, this.formService);
if (!json.fields) { if (!json.fields) {
form.outcomes = this.getFormDefinitionOutcomes(form); form.outcomes = this.getFormDefinitionOutcomes(form);
} }
@@ -427,7 +433,7 @@ export class ActivitiForm implements OnInit, AfterViewChecked, OnChanges {
*/ */
getFormDefinitionOutcomes(form: FormModel): FormOutcomeModel[] { getFormDefinitionOutcomes(form: FormModel): FormOutcomeModel[] {
return [ return [
new FormOutcomeModel(form, {id: '$custom', name: FormOutcomeModel.SAVE_ACTION, isSystem: true}) new FormOutcomeModel(form, { id: '$custom', name: FormOutcomeModel.SAVE_ACTION, isSystem: true })
]; ];
} }
@@ -478,4 +484,29 @@ export class ActivitiForm implements OnInit, AfterViewChecked, OnChanges {
); );
} }
} }
protected onFormLoaded(form: FormModel) {
this.formLoaded.emit(form);
this.formService.formLoaded.next(new FormEvent(form));
}
protected onTaskSaved(form: FormModel) {
this.formSaved.emit(form);
this.formService.taskSaved.next(new FormEvent(form));
}
protected onTaskSavedError(form: FormModel, error: any) {
this.handleError(error);
this.formService.taskSavedError.next(new FormErrorEvent(form, error));
}
protected onTaskCompleted(form: FormModel) {
this.formCompleted.emit(form);
this.formService.taskCompleted.next(new FormEvent(form));
}
protected onTaskCompletedError(form: FormModel, error: any) {
this.handleError(error);
this.formService.taskCompletedError.next(new FormErrorEvent(form, error));
}
} }
@@ -21,7 +21,7 @@
alfresco-mdl-button alfresco-mdl-button
[disabled]="!isOutcomeButtonEnabled(outcome)" [disabled]="!isOutcomeButtonEnabled(outcome)"
[class.mdl-button--colored]="!outcome.isSystem" [class.mdl-button--colored]="!outcome.isSystem"
[class.activiti-form-hide-button]="!isOutcomeButtonVisible(outcome)" [class.activiti-form-hide-button]="!isOutcomeButtonVisible(outcome, form.readOnly)"
(click)="onOutcomeClicked(outcome, $event)"> (click)="onOutcomeClicked(outcome, $event)">
{{outcome.name}} {{outcome.name}}
</button> </button>
@@ -41,7 +41,7 @@ describe('ActivitiStartForm', () => {
beforeEach(async(() => { beforeEach(async(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [CoreModule], imports: [CoreModule.forRoot()],
declarations: [ declarations: [
ActivitiStartForm, ActivitiStartForm,
FormFieldComponent, FormFieldComponent,
@@ -17,7 +17,8 @@
import { import {
Component, Component,
AfterViewChecked, OnChanges, AfterViewChecked,
OnChanges,
SimpleChanges, SimpleChanges,
Input, Input,
ViewChild, ViewChild,
@@ -25,10 +26,11 @@ import {
Output, Output,
EventEmitter EventEmitter
} from '@angular/core'; } from '@angular/core';
import { AlfrescoTranslationService } from 'ng2-alfresco-core'; import { AlfrescoTranslationService, LogService } from 'ng2-alfresco-core';
import { ActivitiForm } from './activiti-form.component'; import { ActivitiForm } from './activiti-form.component';
import { FormService } from './../services/form.service'; import { FormService } from './../services/form.service';
import { WidgetVisibilityService } from './../services/widget-visibility.service'; import { WidgetVisibilityService } from './../services/widget-visibility.service';
import { FormOutcomeModel } from './widgets/core/index';
/** /**
* Displays the start form for a named process definition, which can be used to retrieve values to start a new process. * Displays the start form for a named process definition, which can be used to retrieve values to start a new process.
@@ -67,6 +69,9 @@ export class ActivitiStartForm extends ActivitiForm implements AfterViewChecked,
@Input() @Input()
showRefreshButton: boolean = true; showRefreshButton: boolean = true;
@Input()
readOnlyForm: boolean = false;
@Output() @Output()
outcomeClick: EventEmitter<any> = new EventEmitter<any>(); outcomeClick: EventEmitter<any> = new EventEmitter<any>();
@@ -75,12 +80,15 @@ export class ActivitiStartForm extends ActivitiForm implements AfterViewChecked,
constructor(private translate: AlfrescoTranslationService, constructor(private translate: AlfrescoTranslationService,
formService: FormService, formService: FormService,
visibilityService: WidgetVisibilityService) { visibilityService: WidgetVisibilityService,
super(formService, visibilityService, null, null); logService: LogService) {
super(formService, visibilityService, null, null, logService);
if (this.translate) { if (this.translate) {
this.translate.addTranslationFolder('ng2-activiti-form', 'node_modules/ng2-activiti-form/src'); this.translate.addTranslationFolder('ng2-activiti-form', 'node_modules/ng2-activiti-form/src');
} }
this.showTitle = false;
} }
ngOnChanges(changes: SimpleChanges) { ngOnChanges(changes: SimpleChanges) {
@@ -107,12 +115,11 @@ export class ActivitiStartForm extends ActivitiForm implements AfterViewChecked,
this.formName = form.name; this.formName = form.name;
form.processDefinitionId = this.processDefinitionId; form.processDefinitionId = this.processDefinitionId;
this.form = this.parseForm(form); this.form = this.parseForm(form);
this.form.readOnly = this.readOnlyForm;
// this.form.processDefinitionId = this.processDefinitionId; // this.form.processDefinitionId = this.processDefinitionId;
this.formLoaded.emit(this.form); this.formLoaded.emit(this.form);
}, },
(error) => { error => this.handleError(error)
this.handleError(error);
}
); );
} }
@@ -123,15 +130,38 @@ export class ActivitiStartForm extends ActivitiForm implements AfterViewChecked,
form => { form => {
this.formName = form.processDefinitionName; this.formName = form.processDefinitionName;
this.form = this.parseForm(form); this.form = this.parseForm(form);
this.form.readOnly = this.readOnlyForm;
this.formLoaded.emit(this.form); this.formLoaded.emit(this.form);
}, },
(error) => { error => this.handleError(error)
this.handleError(error);
}
); );
} }
/** @override */
isOutcomeButtonVisible(outcome: FormOutcomeModel, isFormReadOnly: boolean): boolean {
if (outcome && outcome.isSystem && ( outcome.name === FormOutcomeModel.SAVE_ACTION ||
outcome.name === FormOutcomeModel.COMPLETE_ACTION )) {
return false;
} else if (outcome && outcome.name === FormOutcomeModel.START_PROCESS_ACTION) {
return true;
}
return super.isOutcomeButtonVisible(outcome, isFormReadOnly);
}
/** @override */
saveTaskForm() { saveTaskForm() {
// do nothing
}
/** @override */
onRefreshClicked() {
if (this.processDefinitionId) {
this.visibilityService.cleanProcessVariable();
this.getStartFormDefinition(this.processDefinitionId);
} else if (this.processId) {
this.visibilityService.cleanProcessVariable();
this.loadStartForm(this.processId);
}
} }
completeTaskForm(outcome?: string) { completeTaskForm(outcome?: string) {
@@ -16,6 +16,7 @@
*/ */
import { Observable } from 'rxjs/Rx'; import { Observable } from 'rxjs/Rx';
import { LogServiceMock } from 'ng2-alfresco-core';
import { AttachWidget } from './attach.widget'; import { AttachWidget } from './attach.widget';
import { ActivitiAlfrescoContentService } from '../../../services/activiti-alfresco.service'; import { ActivitiAlfrescoContentService } from '../../../services/activiti-alfresco.service';
import { FormFieldModel } from './../core/form-field.model'; import { FormFieldModel } from './../core/form-field.model';
@@ -28,10 +29,12 @@ describe('AttachWidget', () => {
let widget: AttachWidget; let widget: AttachWidget;
let contentService: ActivitiAlfrescoContentService; let contentService: ActivitiAlfrescoContentService;
let dialogPolyfill: any; let dialogPolyfill: any;
let logService: LogServiceMock;
beforeEach(() => { beforeEach(() => {
contentService = new ActivitiAlfrescoContentService(null, null); logService = new LogServiceMock();
widget = new AttachWidget(contentService); contentService = new ActivitiAlfrescoContentService(null, logService);
widget = new AttachWidget(contentService, logService);
dialogPolyfill = { dialogPolyfill = {
registerDialog(obj: any) { registerDialog(obj: any) {
@@ -268,9 +271,9 @@ describe('AttachWidget', () => {
Observable.throw(error) Observable.throw(error)
); );
spyOn(console, 'log').and.stub(); spyOn(logService, 'error').and.stub();
widget.getExternalContentNodes(); widget.getExternalContentNodes();
expect(console.log).toHaveBeenCalledWith(error); expect(logService.error).toHaveBeenCalledWith(error);
}); });
it('should register dialog via polyfill', () => { it('should register dialog via polyfill', () => {
@@ -16,6 +16,7 @@
*/ */
import { Component, OnInit, Input, Output, EventEmitter, ViewChild } from '@angular/core'; import { Component, OnInit, Input, Output, EventEmitter, ViewChild } from '@angular/core';
import { LogService } from 'ng2-alfresco-core';
import { WidgetComponent } from './../widget.component'; import { WidgetComponent } from './../widget.component';
import { ActivitiAlfrescoContentService } from '../../../services/activiti-alfresco.service'; import { ActivitiAlfrescoContentService } from '../../../services/activiti-alfresco.service';
import { ExternalContent } from '../core/external-content'; import { ExternalContent } from '../core/external-content';
@@ -49,7 +50,8 @@ export class AttachWidget extends WidgetComponent implements OnInit {
@ViewChild('dialog') @ViewChild('dialog')
dialog: any; dialog: any;
constructor(private contentService: ActivitiAlfrescoContentService) { constructor(private contentService: ActivitiAlfrescoContentService,
private logService: LogService) {
super(); super();
} }
@@ -97,7 +99,7 @@ export class AttachWidget extends WidgetComponent implements OnInit {
this.contentService.getAlfrescoNodes(this.selectedFolderAccountId, this.selectedFolderPathId) this.contentService.getAlfrescoNodes(this.selectedFolderAccountId, this.selectedFolderPathId)
.subscribe( .subscribe(
nodes => this.selectedFolderNodes = nodes, nodes => this.selectedFolderNodes = nodes,
error => this.handleError(error) error => this.logService.error(error)
); );
} }
@@ -18,3 +18,7 @@
.container-widget__header-text.collapsible { .container-widget__header-text.collapsible {
cursor: pointer; cursor: pointer;
} }
.container-widget__mdl-grid {
align-items: center;
}
@@ -11,9 +11,9 @@
<span (click)="onExpanderClicked()" id="container-header-label">{{content.name}}</span> <span (click)="onExpanderClicked()" id="container-header-label">{{content.name}}</span>
</h4> </h4>
</div> </div>
<div class="mdl-grid" [ngClass]="{'hidden':!(content?.isVisible && content?.isExpanded)}"> <div class="mdl-grid container-widget__mdl-grid" [ngClass]="{'hidden':!(content?.isVisible && content?.isExpanded)}">
<div *ngFor="let col of content.columns" class="mdl-cell mdl-cell--{{col.size}}-col"> <div *ngFor="let col of content.columns" class="mdl-cell mdl-cell--{{col.size}}-col">
<div class="mdl-grid" *ngIf="col.hasFields()"> <div class="mdl-grid container-widget__mdl-grid" *ngIf="col.hasFields()">
<div *ngFor="let field of col.fields" class="mdl-cell mdl-cell--12-col"> <div *ngFor="let field of col.fields" class="mdl-cell mdl-cell--12-col">
<form-field [field]="field"></form-field> <form-field [field]="field"></form-field>
</div> </div>
@@ -22,14 +22,17 @@ export class FormOutcomeModel extends FormWidgetModel {
static SAVE_ACTION: string = 'Save'; // Activiti 'Save' action name static SAVE_ACTION: string = 'Save'; // Activiti 'Save' action name
static COMPLETE_ACTION: string = 'Complete'; // Activiti 'Complete' action name static COMPLETE_ACTION: string = 'Complete'; // Activiti 'Complete' action name
static START_PROCESS_ACTION: string = 'Start Process'; // Activiti 'Start Process' action name
isSystem: boolean = false; isSystem: boolean = false;
isSelected: boolean = false;
constructor(form: FormModel, json?: any) { constructor(form: FormModel, json?: any) {
super(form, json); super(form, json);
if (json) { if (json) {
this.isSystem = json.isSystem ? true : false; this.isSystem = json.isSystem ? true : false;
this.isSelected = form && json.name === form.selectedOutcome ? true : false;
} }
} }
} }
@@ -270,13 +270,16 @@ describe('FormModel', () => {
}; };
let form = new FormModel(json); let form = new FormModel(json);
expect(form.outcomes.length).toBe(2); expect(form.outcomes.length).toBe(3);
expect(form.outcomes[0].id).toBe(FormModel.SAVE_OUTCOME); expect(form.outcomes[0].id).toBe(FormModel.SAVE_OUTCOME);
expect(form.outcomes[0].isSystem).toBeTruthy(); expect(form.outcomes[0].isSystem).toBeTruthy();
expect(form.outcomes[1].id).toBe(FormModel.COMPLETE_OUTCOME); expect(form.outcomes[1].id).toBe(FormModel.COMPLETE_OUTCOME);
expect(form.outcomes[1].isSystem).toBeTruthy(); expect(form.outcomes[1].isSystem).toBeTruthy();
expect(form.outcomes[2].id).toBe(FormModel.START_PROCESS_OUTCOME);
expect(form.outcomes[2].isSystem).toBeTruthy();
}); });
it('should create outcomes only when fields available', () => { it('should create outcomes only when fields available', () => {
@@ -23,12 +23,15 @@ import { FormOutcomeModel } from './form-outcome.model';
import { FormFieldModel } from './form-field.model'; import { FormFieldModel } from './form-field.model';
import { FormFieldTypes } from './form-field-types'; import { FormFieldTypes } from './form-field-types';
import { FormFieldTemplates } from './form-field-templates'; import { FormFieldTemplates } from './form-field-templates';
import { FormService } from './../../../services/form.service';
import { FormFieldEvent } from './../../../events/index';
export class FormModel { export class FormModel {
static UNSET_TASK_NAME: string = 'Nameless task'; static UNSET_TASK_NAME: string = 'Nameless task';
static SAVE_OUTCOME: string = '$save'; static SAVE_OUTCOME: string = '$save';
static COMPLETE_OUTCOME: string = '$complete'; static COMPLETE_OUTCOME: string = '$complete';
static START_PROCESS_OUTCOME: string = '$startProcess';
readonly id: string; readonly id: string;
readonly name: string; readonly name: string;
@@ -47,6 +50,7 @@ export class FormModel {
fields: FormWidgetModel[] = []; fields: FormWidgetModel[] = [];
outcomes: FormOutcomeModel[] = []; outcomes: FormOutcomeModel[] = [];
customFieldTemplates: FormFieldTemplates = {}; customFieldTemplates: FormFieldTemplates = {};
readonly selectedOutcome: string;
values: FormValues = {}; values: FormValues = {};
@@ -64,7 +68,7 @@ export class FormModel {
return this.outcomes && this.outcomes.length > 0; return this.outcomes && this.outcomes.length > 0;
} }
constructor(json?: any, data?: FormValues, readOnly: boolean = false) { constructor(json?: any, data?: FormValues, readOnly: boolean = false, protected formService?: FormService) {
this.readOnly = readOnly; this.readOnly = readOnly;
if (json) { if (json) {
@@ -76,6 +80,7 @@ export class FormModel {
this.taskName = json.taskName || json.name || FormModel.UNSET_TASK_NAME; this.taskName = json.taskName || json.name || FormModel.UNSET_TASK_NAME;
this.processDefinitionId = json.processDefinitionId; this.processDefinitionId = json.processDefinitionId;
this.customFieldTemplates = json.customFieldTemplates || {}; this.customFieldTemplates = json.customFieldTemplates || {};
this.selectedOutcome = json.selectedOutcome || {};
let tabCache: FormWidgetModelCache<TabModel> = {}; let tabCache: FormWidgetModelCache<TabModel> = {};
@@ -103,11 +108,12 @@ export class FormModel {
if (json.fields) { if (json.fields) {
let saveOutcome = new FormOutcomeModel(this, { id: FormModel.SAVE_OUTCOME, name: 'Save', isSystem: true }); let saveOutcome = new FormOutcomeModel(this, { id: FormModel.SAVE_OUTCOME, name: 'Save', isSystem: true });
let completeOutcome = new FormOutcomeModel(this, {id: FormModel.COMPLETE_OUTCOME, name: 'Complete', isSystem: true }); let completeOutcome = new FormOutcomeModel(this, {id: FormModel.COMPLETE_OUTCOME, name: 'Complete', isSystem: true });
let startProcessOutcome = new FormOutcomeModel(this, { id: FormModel.START_PROCESS_OUTCOME, name: 'Start Process', isSystem: true });
let customOutcomes = (json.outcomes || []).map(obj => new FormOutcomeModel(this, obj)); let customOutcomes = (json.outcomes || []).map(obj => new FormOutcomeModel(this, obj));
this.outcomes = [saveOutcome].concat( this.outcomes = [saveOutcome].concat(
customOutcomes.length > 0 ? customOutcomes : [completeOutcome] customOutcomes.length > 0 ? customOutcomes : [completeOutcome, startProcessOutcome]
); );
} }
} }
@@ -116,6 +122,9 @@ export class FormModel {
onFormFieldChanged(field: FormFieldModel) { onFormFieldChanged(field: FormFieldModel) {
this.validateField(field); this.validateField(field);
if (this.formService) {
this.formService.formFieldValueChanged.next(new FormFieldEvent(this, field));
}
} }
// TODO: consider evaluating and caching once the form is loaded // TODO: consider evaluating and caching once the form is loaded
@@ -1,9 +1,21 @@
.date-widget { .date-widget {
width: 100%; width: 100%;
padding-top: 10px;
padding-bottom: 10px;
} }
.date-widget--button { .date-widget--button {
margin-top: 15px; margin-top: 10px;
}
.mdl-date__input{
padding-top: 5px;
padding-bottom: 5px;
}
.mdl-grid__date-widget{
align-items:center;
padding: 0px;
} }
.date-widget__invalid .mdl-textfield__input { .date-widget__invalid .mdl-textfield__input {
@@ -21,3 +33,8 @@
.date-widget__invalid .mdl-textfield__error { .date-widget__invalid .mdl-textfield__error {
visibility: visible !important; visibility: visible !important;
} }
.date-widget-button__cell{
margin-top: 0px;
margin-bottom: 0px;
}
@@ -1,9 +1,9 @@
<div class="mdl-grid" *ngIf="field?.isVisible" id="data-widget"> <div class="mdl-grid mdl-grid__date-widget" *ngIf="field?.isVisible" id="data-widget">
<div class="mdl-cell mdl-cell--11-col"> <div class="mdl-cell mdl-cell--11-col">
<div class="mdl-textfield mdl-js-textfield date-widget" <div class="mdl-textfield mdl-js-textfield date-widget"
[class.date-widget__invalid]="!field.isValid"> [class.date-widget__invalid]="!field.isValid">
<label [attr.for]="field.id">{{field.name}}</label> <label [attr.for]="field.id">{{field.name}}</label>
<input class="mdl-textfield__input" <input class="mdl-textfield__input mdl-date__input"
type="text" type="text"
[attr.id]="field.id" [attr.id]="field.id"
[attr.required]="isRequired()" [attr.required]="isRequired()"
@@ -17,7 +17,7 @@
</div> </div>
<div class="mdl-cell mdl-cell--1-col"> <div class="mdl-cell mdl-cell--1-col">
<button <button
class="mdl-button mdl-js-button mdl-button--icon date-widget--button" class="mdl-button mdl-js-button mdl-button--icon"
(click)="datePicker.toggle()"> (click)="datePicker.toggle()">
<i class="material-icons">date_range</i> <i class="material-icons">date_range</i>
</button> </button>
@@ -16,7 +16,7 @@
*/ */
import { ComponentFixture, TestBed, async } from '@angular/core/testing'; import { ComponentFixture, TestBed, async } from '@angular/core/testing';
import { CoreModule } from 'ng2-alfresco-core'; import { CoreModule, LogServiceMock } from 'ng2-alfresco-core';
import { Observable } from 'rxjs/Rx'; import { Observable } from 'rxjs/Rx';
import { DisplayValueWidget } from './display-value.widget'; import { DisplayValueWidget } from './display-value.widget';
import { FormService } from '../../../services/form.service'; import { FormService } from '../../../services/form.service';
@@ -26,20 +26,19 @@ import { FormFieldTypes } from '../core/form-field-types';
import { FormModel } from '../core/form.model'; import { FormModel } from '../core/form.model';
import { DynamicTableColumn, DynamicTableRow } from './../dynamic-table/dynamic-table.widget.model'; import { DynamicTableColumn, DynamicTableRow } from './../dynamic-table/dynamic-table.widget.model';
import { WidgetVisibilityService } from '../../../services/widget-visibility.service'; import { WidgetVisibilityService } from '../../../services/widget-visibility.service';
import { AlfrescoSettingsService } from 'ng2-alfresco-core';
describe('DisplayValueWidget', () => { describe('DisplayValueWidget', () => {
let widget: DisplayValueWidget; let widget: DisplayValueWidget;
let formService: FormService; let formService: FormService;
let visibilityService: WidgetVisibilityService; let visibilityService: WidgetVisibilityService;
let settingsService: AlfrescoSettingsService; let logService: LogServiceMock;
beforeEach(() => { beforeEach(() => {
settingsService = new AlfrescoSettingsService(); logService = new LogServiceMock();
formService = new FormService(null, null); formService = new FormService(null, null, logService);
visibilityService = new WidgetVisibilityService(null); visibilityService = new WidgetVisibilityService(null, logService);
widget = new DisplayValueWidget(formService, visibilityService, settingsService); widget = new DisplayValueWidget(formService, visibilityService, logService);
}); });
it('should require field to setup default value', () => { it('should require field to setup default value', () => {
@@ -393,7 +392,7 @@ describe('DisplayValueWidget', () => {
Observable.throw(error) Observable.throw(error)
); );
spyOn(console, 'log').and.stub(); spyOn(logService, 'error').and.stub();
let form = new FormModel({taskId: '<id>'}); let form = new FormModel({taskId: '<id>'});
@@ -409,7 +408,7 @@ describe('DisplayValueWidget', () => {
}); });
widget.ngOnInit(); widget.ngOnInit();
expect(formService.getRestFieldValues).toHaveBeenCalled(); expect(formService.getRestFieldValues).toHaveBeenCalled();
expect(console.log).toHaveBeenCalledWith(error); expect(logService.error).toHaveBeenCalledWith(error);
expect(widget.value).toBe('100'); expect(widget.value).toBe('100');
}); });
@@ -16,14 +16,14 @@
*/ */
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core';
import * as moment from 'moment';
import { LogService } from 'ng2-alfresco-core';
import { WidgetComponent } from './../widget.component'; import { WidgetComponent } from './../widget.component';
import { FormFieldTypes } from '../core/form-field-types'; import { FormFieldTypes } from '../core/form-field-types';
import { FormService } from '../../../services/form.service'; import { FormService } from '../../../services/form.service';
import { FormFieldOption } from './../core/form-field-option'; import { FormFieldOption } from './../core/form-field-option';
import { DynamicTableColumn, DynamicTableRow } from './../dynamic-table/dynamic-table.widget.model'; import { DynamicTableColumn, DynamicTableRow } from './../dynamic-table/dynamic-table.widget.model';
import { WidgetVisibilityService } from '../../../services/widget-visibility.service'; import { WidgetVisibilityService } from '../../../services/widget-visibility.service';
import { AlfrescoSettingsService } from 'ng2-alfresco-core';
import * as moment from 'moment';
@Component({ @Component({
moduleId: module.id, moduleId: module.id,
@@ -51,7 +51,7 @@ export class DisplayValueWidget extends WidgetComponent implements OnInit {
constructor(private formService: FormService, constructor(private formService: FormService,
private visibilityService: WidgetVisibilityService, private visibilityService: WidgetVisibilityService,
private settingsService: AlfrescoSettingsService) { private logService: LogService) {
super(); super();
} }
@@ -181,9 +181,10 @@ export class DisplayValueWidget extends WidgetComponent implements OnInit {
} else { } else {
this.value = this.field.value; this.value = this.field.value;
} }
this.visibilityService.refreshVisibility(this.field.form);
}, },
error => { error => {
console.log(error); this.logService.error(error);
this.value = this.field.value; this.value = this.field.value;
} }
); );
@@ -202,9 +203,10 @@ export class DisplayValueWidget extends WidgetComponent implements OnInit {
} else { } else {
this.value = this.field.value; this.value = this.field.value;
} }
this.visibilityService.refreshVisibility(this.field.form);
}, },
error => { error => {
console.log(error); this.logService.error(error);
this.value = this.field.value; this.value = this.field.value;
} }
); );
@@ -1,9 +1,13 @@
.dropdown-widget { .dropdown-widget {
width: 100%; width: 100%;
padding-top: 20px;
padding-bottom: 20px;
} }
.dropdown-widget__select { .dropdown-widget__select {
width: 100%; width: 100%;
margin-top: 5px;
margin-bottom: 5px;
} }
.dropdown-widget__invalid .dropdown-widget__select { .dropdown-widget__invalid .dropdown-widget__select {
@@ -15,14 +15,14 @@
* limitations under the License. * limitations under the License.
*/ */
import { ComponentFixture, TestBed, async } from '@angular/core/testing';
import { Observable } from 'rxjs/Rx'; import { Observable } from 'rxjs/Rx';
import { CoreModule } from 'ng2-alfresco-core';
import { FormService } from '../../../services/form.service'; import { FormService } from '../../../services/form.service';
import { DropdownWidget } from './dropdown.widget'; import { DropdownWidget } from './dropdown.widget';
import { FormModel } from './../core/form.model'; import { FormModel } from './../core/form.model';
import { FormFieldModel } from './../core/form-field.model'; import { FormFieldModel } from './../core/form-field.model';
import { FormFieldOption } from './../core/form-field-option'; import { FormFieldOption } from './../core/form-field-option';
import { CoreModule } from 'ng2-alfresco-core';
import { ComponentFixture, TestBed, async } from '@angular/core/testing';
import { EcmModelService } from '../../../services/ecm-model.service'; import { EcmModelService } from '../../../services/ecm-model.service';
import { WidgetVisibilityService } from '../../../services/widget-visibility.service'; import { WidgetVisibilityService } from '../../../services/widget-visibility.service';
@@ -33,9 +33,9 @@ describe('DropdownWidget', () => {
let visibilityService: WidgetVisibilityService; let visibilityService: WidgetVisibilityService;
beforeEach(() => { beforeEach(() => {
formService = new FormService(null, null); formService = new FormService(null, null, null);
visibilityService = new WidgetVisibilityService(null); visibilityService = new WidgetVisibilityService(null, null);
widget = new DropdownWidget(formService, visibilityService); widget = new DropdownWidget(formService, visibilityService, null);
widget.field = new FormFieldModel(new FormModel()); widget.field = new FormFieldModel(new FormModel());
}); });
@@ -74,12 +74,6 @@ describe('DropdownWidget', () => {
expect(formService.getRestFieldValues).toHaveBeenCalledWith(taskId, fieldId); expect(formService.getRestFieldValues).toHaveBeenCalledWith(taskId, fieldId);
}); });
it('should log error to console by default', () => {
spyOn(console, 'error').and.stub();
widget.handleError('Err');
expect(console.error).toHaveBeenCalledWith('Err');
});
it('should preserve empty option when loading fields', () => { it('should preserve empty option when loading fields', () => {
let restFieldValue: FormFieldOption = <FormFieldOption> { id: '1', name: 'Option1' }; let restFieldValue: FormFieldOption = <FormFieldOption> { id: '1', name: 'Option1' };
spyOn(formService, 'getRestFieldValues').and.returnValue( spyOn(formService, 'getRestFieldValues').and.returnValue(
@@ -16,6 +16,7 @@
*/ */
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core';
import { LogService } from 'ng2-alfresco-core';
import { FormService } from '../../../services/form.service'; import { FormService } from '../../../services/form.service';
import { WidgetComponent } from './../widget.component'; import { WidgetComponent } from './../widget.component';
import { FormFieldOption } from './../core/form-field-option'; import { FormFieldOption } from './../core/form-field-option';
@@ -30,7 +31,8 @@ import { WidgetVisibilityService } from '../../../services/widget-visibility.ser
export class DropdownWidget extends WidgetComponent implements OnInit { export class DropdownWidget extends WidgetComponent implements OnInit {
constructor(private formService: FormService, constructor(private formService: FormService,
private visibilityService: WidgetVisibilityService) { private visibilityService: WidgetVisibilityService,
private logService: LogService) {
super(); super();
} }
@@ -59,7 +61,7 @@ export class DropdownWidget extends WidgetComponent implements OnInit {
this.field.options = options.concat((result || [])); this.field.options = options.concat((result || []));
this.field.updateForm(); this.field.updateForm();
}, },
this.handleError err => this.handleError(err)
); );
} }
@@ -78,7 +80,7 @@ export class DropdownWidget extends WidgetComponent implements OnInit {
this.field.options = options.concat((result || [])); this.field.options = options.concat((result || []));
this.field.updateForm(); this.field.updateForm();
}, },
this.handleError err => this.handleError(err)
); );
} }
@@ -97,7 +99,7 @@ export class DropdownWidget extends WidgetComponent implements OnInit {
} }
handleError(error: any) { handleError(error: any) {
console.error(error); this.logService.error(error);
} }
} }
@@ -15,6 +15,7 @@
* limitations under the License. * limitations under the License.
*/ */
import { LogServiceMock } from 'ng2-alfresco-core';
import { DynamicTableWidget } from './dynamic-table.widget'; import { DynamicTableWidget } from './dynamic-table.widget';
import { DynamicTableModel, DynamicTableRow, DynamicTableColumn } from './dynamic-table.widget.model'; import { DynamicTableModel, DynamicTableRow, DynamicTableColumn } from './dynamic-table.widget.model';
import { FormModel, FormFieldTypes } from './../core/index'; import { FormModel, FormFieldTypes } from './../core/index';
@@ -25,11 +26,13 @@ describe('DynamicTableWidget', () => {
let widget: DynamicTableWidget; let widget: DynamicTableWidget;
let table: DynamicTableModel; let table: DynamicTableModel;
let visibilityService: WidgetVisibilityService; let visibilityService: WidgetVisibilityService;
let logService: LogServiceMock;
beforeEach(() => { beforeEach(() => {
logService = new LogServiceMock();
table = new DynamicTableModel(null); table = new DynamicTableModel(null);
visibilityService = new WidgetVisibilityService(null); visibilityService = new WidgetVisibilityService(null, logService);
widget = new DynamicTableWidget(null, visibilityService); widget = new DynamicTableWidget(null, visibilityService, logService);
widget.content = table; widget.content = table;
}); });
@@ -192,13 +195,13 @@ describe('DynamicTableWidget', () => {
}); });
it('should require table to save changes', () => { it('should require table to save changes', () => {
spyOn(console, 'log').and.stub(); spyOn(logService, 'error').and.stub();
widget.editMode = true; widget.editMode = true;
widget.content = null; widget.content = null;
widget.onSaveChanges(); widget.onSaveChanges();
expect(widget.editMode).toBeFalsy(); expect(widget.editMode).toBeFalsy();
expect(console.log).toHaveBeenCalledWith(widget.ERROR_MODEL_NOT_FOUND); expect(logService.error).toHaveBeenCalledWith(widget.ERROR_MODEL_NOT_FOUND);
}); });
it('should cancel changes', () => { it('should cancel changes', () => {
@@ -16,6 +16,7 @@
*/ */
import { Component, ElementRef, OnInit } from '@angular/core'; import { Component, ElementRef, OnInit } from '@angular/core';
import { LogService } from 'ng2-alfresco-core';
import { WidgetComponent } from './../widget.component'; import { WidgetComponent } from './../widget.component';
import { DynamicTableModel, DynamicTableRow, DynamicTableColumn } from './dynamic-table.widget.model'; import { DynamicTableModel, DynamicTableRow, DynamicTableColumn } from './dynamic-table.widget.model';
import { WidgetVisibilityService } from '../../../services/widget-visibility.service'; import { WidgetVisibilityService } from '../../../services/widget-visibility.service';
@@ -36,7 +37,8 @@ export class DynamicTableWidget extends WidgetComponent implements OnInit {
editRow: DynamicTableRow = null; editRow: DynamicTableRow = null;
constructor(private elementRef: ElementRef, constructor(private elementRef: ElementRef,
private visibilityService: WidgetVisibilityService) { private visibilityService: WidgetVisibilityService,
private logService: LogService) {
super(); super();
} }
@@ -136,7 +138,7 @@ export class DynamicTableWidget extends WidgetComponent implements OnInit {
} }
this.content.flushValue(); this.content.flushValue();
} else { } else {
this.handleError(this.ERROR_MODEL_NOT_FOUND); this.logService.error(this.ERROR_MODEL_NOT_FOUND);
} }
this.editMode = false; this.editMode = false;
} }
@@ -39,7 +39,7 @@ describe('DropdownEditorComponent', () => {
let row: DynamicTableRow; let row: DynamicTableRow;
beforeEach(() => { beforeEach(() => {
formService = new FormService(null, null); formService = new FormService(null, null, null);
row = <DynamicTableRow> { value: { dropdown: 'one' } }; row = <DynamicTableRow> { value: { dropdown: 'one' } };
column = <DynamicTableColumn> { column = <DynamicTableColumn> {
@@ -56,7 +56,7 @@ describe('DropdownEditorComponent', () => {
table.rows.push(row); table.rows.push(row);
table.columns.push(column); table.columns.push(column);
component = new DropdownEditorComponent(formService); component = new DropdownEditorComponent(formService, null);
component.table = table; component.table = table;
component.row = row; component.row = row;
component.column = column; component.column = column;
@@ -16,6 +16,7 @@
*/ */
import { Component, Input, OnInit } from '@angular/core'; import { Component, Input, OnInit } from '@angular/core';
import { LogService } from 'ng2-alfresco-core';
import { DynamicTableModel, DynamicTableRow, DynamicTableColumn, DynamicTableColumnOption } from './../../dynamic-table.widget.model'; import { DynamicTableModel, DynamicTableRow, DynamicTableColumn, DynamicTableColumnOption } from './../../dynamic-table.widget.model';
import { FormService } from './../../../../../services/form.service'; import { FormService } from './../../../../../services/form.service';
@@ -39,7 +40,9 @@ export class DropdownEditorComponent implements OnInit {
@Input() @Input()
column: DynamicTableColumn; column: DynamicTableColumn;
constructor(private formService: FormService) {} constructor(private formService: FormService,
private logService: LogService) {
}
ngOnInit() { ngOnInit() {
let field = this.table.field; let field = this.table.field;
@@ -98,6 +101,6 @@ export class DropdownEditorComponent implements OnInit {
} }
handleError(error: any) { handleError(error: any) {
console.error(error); this.logService.error(error);
} }
} }
@@ -36,7 +36,7 @@ describe('FunctionalGroupWidget', () => {
]); ]);
window['componentHandler'] = componentHandler; window['componentHandler'] = componentHandler;
formService = new FormService(null, null); formService = new FormService(null, null, null);
elementRef = new ElementRef(null); elementRef = new ElementRef(null);
widget = new FunctionalGroupWidget(formService, elementRef); widget = new FunctionalGroupWidget(formService, elementRef);
widget.field = new FormFieldModel(new FormModel()); widget.field = new FormFieldModel(new FormModel());
@@ -36,7 +36,7 @@ describe('PeopleWidget', () => {
]); ]);
window['componentHandler'] = componentHandler; window['componentHandler'] = componentHandler;
formService = new FormService(null, null); formService = new FormService(null, null, null);
elementRef = new ElementRef(null); elementRef = new ElementRef(null);
widget = new PeopleWidget(formService, elementRef); widget = new PeopleWidget(formService, elementRef);
widget.field = new FormFieldModel(new FormModel()); widget.field = new FormFieldModel(new FormModel());
@@ -15,13 +15,14 @@
* limitations under the License. * limitations under the License.
*/ */
import { ComponentFixture, TestBed, async } from '@angular/core/testing';
import { Observable } from 'rxjs/Rx'; import { Observable } from 'rxjs/Rx';
import { CoreModule, LogServiceMock } from 'ng2-alfresco-core';
import { FormService } from '../../../services/form.service'; import { FormService } from '../../../services/form.service';
import { RadioButtonsWidget } from './radio-buttons.widget'; import { RadioButtonsWidget } from './radio-buttons.widget';
import { FormModel } from './../core/form.model'; import { FormModel } from './../core/form.model';
import { FormFieldModel } from './../core/form-field.model'; import { FormFieldModel } from './../core/form-field.model';
import { CoreModule } from 'ng2-alfresco-core';
import { ComponentFixture, TestBed, async } from '@angular/core/testing';
import { EcmModelService } from '../../../services/ecm-model.service'; import { EcmModelService } from '../../../services/ecm-model.service';
import { WidgetVisibilityService } from '../../../services/widget-visibility.service'; import { WidgetVisibilityService } from '../../../services/widget-visibility.service';
import { FormFieldTypes } from '../core/form-field-types'; import { FormFieldTypes } from '../core/form-field-types';
@@ -33,11 +34,13 @@ describe('RadioButtonsWidget', () => {
let formService: FormService; let formService: FormService;
let widget: RadioButtonsWidget; let widget: RadioButtonsWidget;
let visibilityService: WidgetVisibilityService; let visibilityService: WidgetVisibilityService;
let logService: LogServiceMock;
beforeEach(() => { beforeEach(() => {
formService = new FormService(null, null); logService = new LogServiceMock();
visibilityService = new WidgetVisibilityService(null); formService = new FormService(null, null, logService);
widget = new RadioButtonsWidget(formService, visibilityService); visibilityService = new WidgetVisibilityService(null, logService);
widget = new RadioButtonsWidget(formService, visibilityService, logService);
widget.field = new FormFieldModel(new FormModel(), { restUrl: '<url>' }); widget.field = new FormFieldModel(new FormModel(), { restUrl: '<url>' });
}); });
@@ -118,12 +121,12 @@ describe('RadioButtonsWidget', () => {
}); });
it('should log error to console by default', () => { it('should log error to console by default', () => {
spyOn(console, 'error').and.stub(); spyOn(logService, 'error').and.stub();
widget.handleError('Err'); widget.handleError('Err');
expect(console.error).toHaveBeenCalledWith('Err'); expect(logService.error).toHaveBeenCalledWith('Err');
}); });
it('should update the field value when an option is selected', () => { xit('should update the field value when an option is selected', () => {
spyOn(widget, 'checkVisibility').and.stub(); spyOn(widget, 'checkVisibility').and.stub();
widget.onOptionClick('fake-opt'); widget.onOptionClick('fake-opt');
@@ -16,6 +16,7 @@
*/ */
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core';
import { LogService } from 'ng2-alfresco-core';
import { WidgetComponent } from './../widget.component'; import { WidgetComponent } from './../widget.component';
import { FormService } from '../../../services/form.service'; import { FormService } from '../../../services/form.service';
import { FormFieldOption } from './../core/form-field-option'; import { FormFieldOption } from './../core/form-field-option';
@@ -30,7 +31,8 @@ import { WidgetVisibilityService } from '../../../services/widget-visibility.ser
export class RadioButtonsWidget extends WidgetComponent implements OnInit { export class RadioButtonsWidget extends WidgetComponent implements OnInit {
constructor(private formService: FormService, constructor(private formService: FormService,
private visibilityService: WidgetVisibilityService) { private visibilityService: WidgetVisibilityService,
private logService: LogService) {
super(); super();
} }
@@ -55,7 +57,7 @@ export class RadioButtonsWidget extends WidgetComponent implements OnInit {
this.field.options = result || []; this.field.options = result || [];
this.field.updateForm(); this.field.updateForm();
}, },
this.handleError err => this.handleError(err)
); );
} }
@@ -70,7 +72,7 @@ export class RadioButtonsWidget extends WidgetComponent implements OnInit {
this.field.options = result || []; this.field.options = result || [];
this.field.updateForm(); this.field.updateForm();
}, },
this.handleError err => this.handleError(err)
); );
} }
@@ -84,7 +86,7 @@ export class RadioButtonsWidget extends WidgetComponent implements OnInit {
} }
handleError(error: any) { handleError(error: any) {
console.error(error); this.logService.error(error);
} }
} }

Some files were not shown because too many files have changed in this diff Show More