Compare commits

...
Author SHA1 Message Date
Vito c542cbca6d [ADF-3281] - fixed error when user try to involve people (#3538)
* [ADF-3281] moved current user retrieving in the constructor

* [ADF-3281] moved current user retrieving in the constructor #2
2018-06-29 10:21:27 +01:00
davidcanonieto c8fccccb81 [ADF-3149] New Report Issue page created (#3452)
* [ADF-3149] New Report Issue page created

* [ADF-3149] Added missing comma
2018-06-29 10:19:23 +01:00
Andy Stark d7d838624e [ADF-3279] Initial refactoring of doc tools (#3541)
* [ADF-3279] Added timing option

* [ADF-3279] Updated to keep md files in memory during processing

* [ADF-3279] Changed read phase to use stored Markdown trees

* [ADF-3279] Renamed tool functions and removed obsolete ones

* [ADF-3279] Added progress bar and better error message handling
2018-06-29 10:14:58 +01:00
Vito e5ea283332 [ADF-2348] added actions on start and cancel start process buttons (#3543) 2018-06-29 10:14:30 +01:00
bbcodrin eeb3b9abe3 header separator fix (#3535) 2018-06-28 10:40:16 +01:00
bbcodrin 68735cb88a [ADF-2494] Task Standalone - Provide a way to attach a form (#3459)
* button added for task standalone

* attach form component added

* slide-toggle position fixed

* tests added

* rebase fixed

* review fix

* test added & fixes

* tests fixes

* refresh task details

* tests fixed

* attachFormToATask test added

* formPreview test fixed

* adf-form test fixed
2018-06-28 10:39:15 +01:00
Vito a2afb730fa [ADF-2671] added permission check on permissions (#3521)
* [ADF-2671] start adding permission check for changing permissions

* [ADF-2671] added permission check for inherit button and permission dialog

* [ADF-2671] added permission check for inherit button and permission dialog

* [ADF-2671] start fixing and adding test for new permission check

* ]
[ADF-2671] improved check for node-permission directive

* [ADF-2671] fixed and added more test for permission on permissions

* [ADF-2671] reverting change on node-permission directive

* [ADF-2671] fixing test for permission check
2018-06-27 15:15:57 +01:00
Alex Bolboșenco cb8bd6f814 [ADF-3275] Select CardView item (#3530)
* Select card view item

* package json fix

* package json fix

* console.log removed

* Usage of generic
2018-06-27 14:14:13 +01:00
davidcanonieto 8300e9461b [ADF-2598] Sidenav button only visible when sidenav is enebled (#3526) 2018-06-27 10:08:14 +01:00
Vito 1bf346c5de [ADF-2876] retrieving user from APS api and not from js-api (#3426)
* [ADF-2876] retrieving user from APS api and not from js-api

* [ADF-2876] added support by checking externalId instead of email

* [ADF-2876] fixed auth service after rebase
2018-06-27 10:01:17 +01:00
Maurizio Vitale ffef7688d7 Expose uriLogout in the host setting form page (#3517) 2018-06-27 09:47:10 +01:00
Vito f6a300850c [ADF-3268] added datetime widget for dynamic table (#3529) 2018-06-27 09:45:14 +01:00
Vito aad162399b [ADF-3266] fixed style for amount widget (#3531) 2018-06-27 09:43:30 +01:00
bbcodrin 546928fe86 [ADF-3238] content size min error added (#3532)
* content size min error added

* clean code
2018-06-27 09:38:57 +01:00
Vito 8a8dfffa90 [ADF-3267] changed true/false values showed with a readonly checkbox (#3524) 2018-06-26 10:49:09 +01:00
Vito 570a75478d [ADF-2769] Added example page for content node selector (#3498)
* content node selector test page

* [ADF-2769] start adding more examples to node selector page

* [ADF-2769] added more example for node selector page

* [ADF-2769] added examples on node selector page
2018-06-25 17:11:19 +01:00
bbcodrin 4697a8fc58 [ADF-2962] Login Hide/Show icon is not reachable (#3489)
* hide/show focused & triggered

* keyup.enter method used
2018-06-25 17:07:00 +01:00
Alex Bolboșenco a680b47cf5 [ADF-3193] CardView Variables Item (#3485)
* variables item

* unit tests

* translations fix

* docs added

* change requests

* change requests
2018-06-25 17:04:46 +01:00
davidcanonieto 6610a326a7 [ADF-3199] Datatable layout fix (#3481) 2018-06-25 16:56:56 +01:00
Eugenio Romano de216d86f0 fix test 2018-06-25 14:12:33 +01:00
Eugenio Romano 5710749e9d update lock 2018-06-25 11:49:27 +01:00
Eugenio Romano 9275f22553 bump 2.4.0 version 2018-06-24 14:02:59 +01:00
Eugenio Romano 8f7383d9d0 Merge branch 'master' into development
* master:
  [ADF-2825] - Form visibility fix on Master (#3237)
2018-06-22 21:56:01 +01:00
Maurizio Vitale c3db1c2ff8 [ADF-2825] - Form visibility fix on Master (#3237)
* Fix form visibility related to the process variables (#3232)

* bump 2.3.1 version
2018-04-25 18:28:06 +01:00
Eugenio Romano afa298060a Merge pull request #3206 from Alfresco/development
2.3.0
2018-04-17 17:43:50 +01:00
99 changed files with 8062 additions and 6102 deletions
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "Alfresco-ADF-Angular-Demo",
"description": "Demo shell for Alfresco Angular components",
"version": "2.4.0-beta14",
"version": "2.4.0",
"author": "Alfresco Software, Ltd.",
"repository": {
"type": "git",
+1
View File
@@ -52,6 +52,7 @@
"APP_LAYOUT": {
"APP_NAME": "ADF Demo Application",
"HOME": "Home",
"NODE-SELECTOR": "Node Selector",
"CONTENT_SERVICES": "Content Services",
"BREADCRUMB": "Breadcrumb",
"PROCESS_SERVICES": "Process Services",
+9 -2
View File
@@ -52,7 +52,9 @@ import { SharedLinkViewComponent } from './components/shared-link-view/shared-li
import { DemoPermissionComponent } from './components/permissions/demo-permissions.component';
import { PreviewService } from './services/preview.service';
import { BreadcrumbDemoComponent } from './components/breadcrumb-demo/breadcrumb-demo.component';
import { ContentNodeSelectorComponent } from './components/content-node-selector/content-node-selector.component';
import { NotificationsComponent } from './components/notifications/notifications.component';
import { ReportIssueComponent } from './components/report-issue/report-issue.component';
@NgModule({
imports: [
@@ -106,7 +108,9 @@ import { NotificationsComponent } from './components/notifications/notifications
FormLoadingComponent,
BlobPreviewComponent,
BreadcrumbDemoComponent,
NotificationsComponent
NotificationsComponent,
ContentNodeSelectorComponent,
ReportIssueComponent
],
providers: [
{ provide: AppConfigService, useClass: DebugAppConfigService }, // not use this service in production
@@ -128,7 +132,10 @@ import { NotificationsComponent } from './components/notifications/notifications
},
PreviewService
],
entryComponents: [VersionManagerDialogAdapterComponent, MetadataDialogAdapterComponent],
entryComponents: [
VersionManagerDialogAdapterComponent,
MetadataDialogAdapterComponent
],
bootstrap: [AppComponent]
})
export class AppModule {}
+10
View File
@@ -50,6 +50,8 @@ import { DemoPermissionComponent } from './components/permissions/demo-permissio
import { BlobPreviewComponent } from './components/blob-preview/blob-preview.component';
import { BreadcrumbDemoComponent } from './components/breadcrumb-demo/breadcrumb-demo.component';
import { NotificationsComponent } from './components/notifications/notifications.component';
import { ContentNodeSelectorComponent } from './components/content-node-selector/content-node-selector.component';
import { ReportIssueComponent } from './components/report-issue/report-issue.component';
export const appRoutes: Routes = [
{ path: 'login', component: LoginComponent },
@@ -86,6 +88,10 @@ export const appRoutes: Routes = [
path: 'home',
component: HomeComponent
},
{
path: 'node-selector',
component: ContentNodeSelectorComponent
},
{
path: 'settings-layout',
component: SettingsComponent
@@ -216,6 +222,10 @@ export const appRoutes: Routes = [
component: OverlayViewerComponent,
canActivate: [AuthGuardEcm]
},
{
path: 'report-issue',
component: ReportIssueComponent
},
{
path: 'datatable-lazy',
loadChildren: 'app/components/lazy-loading/lazy-loading.module#LazyLoadingModule'
@@ -1,9 +1,9 @@
<adf-sidenav-layout [sidenavMin]="70" [sidenavMax]="220" [stepOver]="780" [hideSidenav]="false"[expandedSidenav]= "expandedSidenav" (expanded)="setState($event)">
<adf-sidenav-layout [sidenavMin]="70" [sidenavMax]="220" [stepOver]="780" [hideSidenav]="hideSidenav" [expandedSidenav]= "expandedSidenav" (expanded)="setState($event)">
<adf-sidenav-layout-header>
<ng-template let-toggleMenu="toggleMenu">
<mat-toolbar color="primary" class="adf-app-layout-toolbar mat-elevation-z6">
<button mat-icon-button (click)="toggleMenu()">
<button mat-icon-button (click)="toggleMenu()" *ngIf="!hideSidenav">
<mat-icon>menu</mat-icon>
</button>
@@ -34,6 +34,7 @@ export class AppLayoutComponent implements OnInit {
{ href: '/files', icon: 'folder_open', title: 'APP_LAYOUT.CONTENT_SERVICES' },
{ href: '/breadcrumb', icon: 'label', title: 'APP_LAYOUT.BREADCRUMB' },
{ href: '/notifications', icon: 'alarm', title: 'APP_LAYOUT.NOTIFICATIONS'},
{ href: '/node-selector', icon: 'attachment', title: 'APP_LAYOUT.NODE-SELECTOR' },
{ href: '/activiti', icon: 'device_hub', title: 'APP_LAYOUT.PROCESS_SERVICES' },
{ href: '/login', icon: 'vpn_key', title: 'APP_LAYOUT.LOGIN' },
{ href: '/trashcan', icon: 'delete', title: 'APP_LAYOUT.TRASHCAN' },
@@ -54,6 +55,8 @@ export class AppLayoutComponent implements OnInit {
expandedSidenav = false;
hideSidenav = false;
enabelRedirect = true;
ngOnInit() {
@@ -19,7 +19,7 @@ import { Component } from '@angular/core';
@Component({
templateUrl: './breadcrumb-demo.component.html',
styleUrls: [`./breadcrumb-demo.component.scss`],
styleUrls: [`./breadcrumb-demo.component.scss`]
})
export class BreadcrumbDemoComponent {
@@ -0,0 +1,198 @@
<main>
<h1>Object picker</h1>
<mat-accordion>
<mat-expansion-panel>
<mat-expansion-panel-header>
<mat-panel-title>
<h2>Plain picker</h2>
</mat-panel-title>
<mat-panel-description>
<label class="content-node-selector-demo-basic-label">
dropdownHideMyFiles: {{dropdownHideMyFiles}}
</label>
</mat-panel-description>
</mat-expansion-panel-header>
<div class="content-node-selector-demo-basic-table">
<adf-content-node-selector-panel
[currentFolderId]="'-root-'">
</adf-content-node-selector-panel>
</div>
</mat-expansion-panel>
<mat-expansion-panel>
<mat-expansion-panel-header>
<mat-panel-title>
<h2>Hide My files</h2>
</mat-panel-title>
<mat-panel-description>
<label class="content-node-selector-demo-basic-label">
My Files site will be hided from dropdown
</label>
</mat-panel-description>
</mat-expansion-panel-header>
<div class="content-node-selector-demo-basic-table">
<adf-content-node-selector-panel
[currentFolderId]="'-root-'"
[dropdownHideMyFiles]="true">
</adf-content-node-selector-panel>
</div>
</mat-expansion-panel>
<mat-expansion-panel>
<mat-expansion-panel-header>
<mat-panel-title>
<h2>Custom Site List</h2>
</mat-panel-title>
<mat-panel-description>
<label class="content-node-selector-demo-basic-label">
Adding a custom site list
</label>
</mat-panel-description>
</mat-expansion-panel-header>
<div>
<mat-list>
<h3 mat-subheader>Add Site</h3>
<form class="content-node-selector-demo-example-form">
<mat-form-field>
<label>Site Guid</label>
<input matInput [(ngModel)]="customSideGuid" [ngModelOptions]="{standalone: true}">
</mat-form-field>
<mat-form-field>
<label>Site Title</label>
<input matInput [(ngModel)]="customSideTitle" [ngModelOptions]="{standalone: true}">
</mat-form-field>
</form>
<button mat-button color="primary" (click)="onClickAddSite()">Add</button>
<button mat-button color="primary" (click)="onClickResetSite()">Reset</button>
<mat-divider></mat-divider>
<h3 mat-subheader>Custom Sites Added</h3>
<mat-list-item *ngFor="let customSite of customSites.list.entries">
<mat-icon mat-list-icon>shopping_basket</mat-icon>
<h4 mat-line>{{customSite.entry.title}}</h4>
<p mat-line> {{customSite.entry.guid}} </p>
</mat-list-item>
</mat-list>
</div>
<div class="content-node-selector-demo-basic-table">
<adf-content-node-selector-panel
[currentFolderId]="'-root-'"
[dropdownSiteList]="customSites">
</adf-content-node-selector-panel>
</div>
</mat-expansion-panel>
<mat-expansion-panel>
<mat-expansion-panel-header>
<mat-panel-title>
<h2>Row Filtering</h2>
</mat-panel-title>
<mat-panel-description>
<label class="content-node-selector-demo-basic-label">
Will automatically filter the row on the list
</label>
</mat-panel-description>
</mat-expansion-panel-header>
<mat-slide-toggle color="primary" [(ngModel)]="showFiles" (click)="recreateRowFilterFunction()">
Show Files
</mat-slide-toggle>
<mat-slide-toggle color="primary" [(ngModel)]="showFolders" (click)="recreateRowFilterFunction()">
Slide Folders
</mat-slide-toggle>
<div class="content-node-selector-demo-basic-table">
<adf-content-node-selector-panel
[rowFilter]="rowFilterFunction"
[currentFolderId]="'-root-'">
</adf-content-node-selector-panel>
</div>
</mat-expansion-panel>
<mat-expansion-panel>
<mat-expansion-panel-header>
<mat-panel-title>
<h2>Custom Image Resolving</h2>
</mat-panel-title>
<mat-panel-description>
<label class="content-node-selector-demo-basic-label">
A function to manage the way folder/file icons and thumbnails are resolved
</label>
</mat-panel-description>
</mat-expansion-panel-header>
<mat-slide-toggle color="primary" (click)="recreateImageResolverFunction()">
Add Custom Images Resolver
</mat-slide-toggle>
<div class="content-node-selector-demo-basic-table">
<adf-content-node-selector-panel
[imageResolver]="customImageResolver"
[currentFolderId]="'-root-'">
</adf-content-node-selector-panel>
</div>
</mat-expansion-panel>
<mat-expansion-panel>
<mat-expansion-panel-header>
<mat-panel-title>
<h2>Set Page Size</h2>
</mat-panel-title>
<mat-panel-description>
<label class="content-node-selector-demo-basic-label">
Number of items shown per page in the list. actual page {{actualPageSize}}
</label>
</mat-panel-description>
</mat-expansion-panel-header>
<div class="content-node-selector-demo-basic-table">
<adf-content-node-selector-panel
[currentFolderId]="'-root-'"
[pageSize]="actualPageSize"
>
</adf-content-node-selector-panel>
</div>
</mat-expansion-panel>
<mat-expansion-panel>
<mat-expansion-panel-header>
<mat-panel-title>
<h2>Is Valid Selection Function </h2>
</mat-panel-title>
<mat-panel-description>
<label class="content-node-selector-demo-basic-label">
Function used to decide if the selected node has permission to be selected
</label>
</mat-panel-description>
</mat-expansion-panel-header>
<label>Only folder starting with the letter 'a' or 'A' are valid selections</label>
<label>Actual Selection is : {{validSelection}}</label>
<div class="content-node-selector-demo-basic-table">
<adf-content-node-selector-panel
(select)="onNodeSelect($event)"
[isSelectionValid]="customIsValidFunction"
[currentFolderId]="'-root-'">
</adf-content-node-selector-panel>
</div>
</mat-expansion-panel>
<mat-expansion-panel>
<mat-expansion-panel-header>
<mat-panel-title>
<h2>BreadCrumb Transform Function </h2>
</mat-panel-title>
<mat-panel-description>
<label class="content-node-selector-demo-basic-label">
Transformation to be performed on the chosen/folder node before building the breadcrumb UI
</label>
</mat-panel-description>
</mat-expansion-panel-header>
<label>Folder starting with letter 'd' or 'D' won't be displayed in the breadcrumb dropdown</label>
<div class="content-node-selector-demo-basic-table">
<adf-content-node-selector-panel
[breadcrumbTransform]="customBreadcrumbFunction"
[currentFolderId]="'-root-'">
</adf-content-node-selector-panel>
</div>
</mat-expansion-panel>
</mat-accordion>
</main>
@@ -0,0 +1,27 @@
.content-node-selector-demo {
&-main {
display: flex;
flex-direction: column;
justify-content: space-around;
align-content: center;
align-self: center;
align-items: center;
}
&-basic-table {
height:400px;
width:700px
}
&-basic-label {
display: flex;
align-self: center;
}
&-example-form {
display: flex;
flex-direction: column;
align-items: flex-start;
}
}
@@ -0,0 +1,115 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Component, ViewEncapsulation } from '@angular/core';
import { SitePaging, SiteEntry, MinimalNodeEntryEntity } from 'alfresco-js-api';
import { ShareDataRow } from '@alfresco/adf-content-services';
import { DataRow, DataColumn, ThumbnailService } from '@alfresco/adf-core';
@Component({
templateUrl: './content-node-selector.component.html',
styleUrls: [`./content-node-selector.component.scss`],
encapsulation: ViewEncapsulation.None
})
export class ContentNodeSelectorComponent {
constructor(private thumbnailService: ThumbnailService) {
}
dropdownHideMyFiles = false;
showFiles = false;
showFolders = false;
enableImageResolver = false;
validSelection = false;
customSideGuid = '';
customSideTitle = '';
actualPageSize = 2;
rowFilterFunction: any = null;
customImageResolver: any = null;
defaultSites: SiteEntry[] = [
{ entry: { title: 'MINE', guid: '-my-' } },
{ entry: { title: 'ROOTY', guid: '-root-' } }];
customSites: SitePaging = {
list: {
entries: [
{ entry: { title: 'MINE', guid: '-my-' } },
{ entry: { title: 'ROOTY', guid: '-root-' } }],
pagination: {}
}
};
onClickAddSite() {
const newSiteEntry: SiteEntry = { entry: { title: this.customSideTitle, guid: this.customSideGuid } };
this.customSites.list.entries.push(newSiteEntry);
this.customSideGuid = '';
this.customSideTitle = '';
}
onClickResetSite() {
this.customSites.list.entries = this.defaultSites;
this.customSideGuid = '';
this.customSideTitle = '';
}
recreateRowFilterFunction() {
this.rowFilterFunction = this.rowFilteringExample.bind(this);
}
recreateImageResolverFunction() {
this.enableImageResolver = !this.enableImageResolver;
if (this.enableImageResolver) {
this.customImageResolver = this.customImageResolverExample.bind(this);
} else {
this.customImageResolver = null;
}
}
rowFilteringExample(row: ShareDataRow) {
let showNode = true;
const node: MinimalNodeEntryEntity = row.node.entry;
if (this.showFiles) {
showNode = node.isFile;
}
if (this.showFolders) {
showNode = node.isFolder;
}
return showNode;
}
customImageResolverExample(row: DataRow, col: DataColumn) {
return this.thumbnailService.getMimeTypeIcon('video/quicktime');
}
onNodeSelect(node: MinimalNodeEntryEntity) {
this.validSelection = !!node;
}
customIsValidFunction(entry: MinimalNodeEntryEntity): boolean {
return entry.name.startsWith('a') || entry.name.startsWith('A');
}
customBreadcrumbFunction(node: MinimalNodeEntryEntity) {
if (node && node.path && node.path.elements) {
node.path.elements = node.path.elements.filter((element) => !element.name.toLocaleLowerCase().startsWith('d') ? element : null );
}
return node;
}
}
@@ -428,7 +428,9 @@
<div *ngIf="processId">
<adf-start-process
[values]="formValues"
[appId]="processId">
[appId]="processId"
(start)="closeStartProcess()"
(cancel)="closeStartProcess()">
</adf-start-process>
</div>
@@ -474,6 +474,10 @@ export class FilesComponent implements OnInit, OnChanges, OnDestroy {
}
closeStartProcess() {
this.processId = null;
}
onChangePageSize(event: Pagination): void {
this.preference.paginationSize = event.maxItems;
this.changedPageSize.emit(event);
@@ -0,0 +1,9 @@
<div class="adf-report-issue">
<h1>Report Issue</h1>
<form class="adf-report-issue-form">
<mat-form-field>
<textarea matInput placeholder="Leave a comment" class="adf-report-issue-form-textarea"></textarea>
</mat-form-field>
</form>
<button mat-raised-button>Submit</button>
</div>
@@ -0,0 +1,24 @@
.adf-report-issue {
margin: 50px 0;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
&-form {
width: 50%;
min-width: 250px;
margin-bottom: 50px;
& mat-form-field {
width: 100%;
}
&-textarea {
background-color: white;
width: 100%;
height: 250px;
}
}
}
@@ -0,0 +1,26 @@
/*!
* @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 } from '@angular/core';
@Component({
templateUrl: './report-issue.component.html',
styleUrls: [`./report-issue.component.scss`],
})
export class ReportIssueComponent {
}
+47
View File
@@ -74,6 +74,17 @@ Defining properties from Typescript:
key: 'mental-stability',
default: 0.0
}),
new CardViewKeyValuePairsItemModel({
label: 'Variables',
value: [],
key: 'key-value-pairs'
}),
new CardViewSelectItemModel({
label: 'Select box',
value: 'one',
options$: of([{ key: 'one', label: 'One' }, { key: 'two', label: 'Two' }]),
key: 'select'
}),
...
]
```
@@ -99,6 +110,8 @@ You define the property list, the [`CardViewComponent`](../core/card-view.compon
- [**CardViewBoolItemModel**](#card-bool-item) - _for bool items (checkbox)_
- [**CardViewIntItemModel**](#card-int-item) - _for integer items_
- [**CardViewFloatItemModel**](#card-float-item) - _for float items_
- [**CardViewKeyValuePairsItemModel**](#card-key-values-pairs-item) - _for key-value-pairs items_
- [**CardViewSelectItemModel**](#card-select-item) - _for select items_
Each of these types implements the [Card View Item interface](card-view-item.interface.md):
@@ -287,6 +300,40 @@ const floatItemProperty = new CardViewFloatItemModel(options);
| displayValue\* | float | | The value to display |
| editable | boolean | false | Toggles whether the item is editable |
#### Card Key Value Pairs Item
[`CardViewKeyValuePairsItemModel`](../../lib/core/card-view/models/card-view-keyvaluepairs.model.ts) is a property type for key-value properties.
```ts
const keyValuePairsItemProperty = new CardViewKeyValuePairsItemModel(options);
```
| Name | Type | Default | Description |
| ---- | ---- | ------- | ----------- |
| label\* | string | | Item label |
| key\* | string | | Identifying key (important when editing the item) |
| editable | boolean | false | Toggles whether the item is editable |
| value\* | `[{ name: '', value: '' }, ...]` | | The original data value for the item |
#### Card Select Item
[`CardViewSelectItemModel`](../../lib/core/card-view/models/card-view-selectitem.model.ts) is a property type for select properties.
```ts
const selectItemProperty = new CardViewSelectItemModel(options);
```
| Name | Type | Default | Description |
| ---- | ---- | ------- | ----------- |
| label\* | string | | Item label |
| key\* | string | | Identifying key (important when editing the item) |
| editable | boolean | false | Toggles whether the item is editable |
| value | string | | The original data value for the item |
| options$\* | Observable<CardViewSelectItemOption[]> | | The original data value for the item |
## See also
- [Card View Update service](card-view-update.service.md)
@@ -0,0 +1,31 @@
---
Added: v2.0.0
Status: Active
---
# Attach Form component
This component can be used when there is no form attached to a task and we want to add one.
## Basic Usage
```html
<adf-attach-form
[taskId]="taskid">
</adf-attach-form>
```
## Class members
### Properties
| Name | Type | Default value | Description |
| -- | -- | -- | -- |
| taskId | `string` | | Id of the task. |
### Events
| Name | Type | Description |
| -- | -- | -- |
| cancelAttachForm | [`EventEmitter`](https://angular.io/api/core/EventEmitter)`<void>` | Emitted when the "Cancel" button is clicked. |
| completeAttachForm | [`EventEmitter`](https://angular.io/api/core/EventEmitter)`<void>` | Emitted when the form associated with the task is completed. |
@@ -25,6 +25,7 @@ This component can be used when there is no form attached to a task.
| hideCancelButton | `boolean` | true | Toggles rendering of the `Cancel` button. |
| isCompleted | `boolean` | false | If true then Task completed message is shown and `Complete` and `Cancel` buttons are hidden. |
| taskName | `string` | | Name of the task. |
| taskId | `number` | | Id of the task. |
### Events
@@ -153,7 +153,7 @@
}
& .adf-name-location-cell-name {
padding: 18px 0 2px 0;
padding: 5px 0 2px 0;
}
&.adf-content-selector-modified-cell {
@@ -416,7 +416,7 @@ export class DocumentListComponent implements OnInit, OnChanges, OnDestroy, Afte
if (changes.node && changes.node.currentValue) {
this.data.loadPage(changes.node.currentValue);
this.onDataReady(changes.node.currentValue);
} else if (changes.rowFilter) {
} else if (changes.rowFilter && changes.rowFilter.currentValue !== changes.rowFilter.previousValue) {
this.data.setFilter(changes.rowFilter.currentValue);
if (this.currentFolderId) {
this.loadFolderNodesByFolderNodeId(this.currentFolderId, this.pagination.getValue()).catch(err => this.error.emit(err));
+2 -1
View File
@@ -283,7 +283,8 @@
"EVERYONE" : "EVERYONE"
},
"ERROR": {
"DUPLICATE-PERMISSION": "One or more of the permissions you have set is already present : {{list}}"
"DUPLICATE-PERMISSION": "One or more of the permissions you have set is already present : {{list}}",
"NOT-ALLOWED": "You are not allowed to change permissions"
}
}
}
+3 -3
View File
@@ -1,7 +1,7 @@
{
"name": "@alfresco/adf-content-services",
"description": "Alfresco ADF content services",
"version": "2.4.0-beta14",
"version": "2.4.0",
"author": "Alfresco Software, Ltd.",
"main": "bundles/adf-content-services.js",
"repository": {
@@ -26,8 +26,8 @@
"@angular/platform-browser-dynamic": "5.1.1",
"@angular/router": "5.1.1",
"@ngx-translate/core": "9.1.1",
"alfresco-js-api": "2.4.0-beta14",
"@alfresco/adf-core": "2.4.0-beta14",
"alfresco-js-api": "2.4.0",
"@alfresco/adf-core": "2.4.0",
"chart.js": "2.5.0",
"core-js": "2.4.1",
"hammerjs": "2.0.8",
@@ -5,7 +5,7 @@
<button mat-button
id="adf-add-permission-action-button"
class="adf-permission-action"
[disabled]="selectedItems?.length === 0"
[disabled]="!isAddEnabled()"
(click)="applySelection()">
{{'PERMISSION_MANAGER.ADD-PERMISSION.ADD-ACTION' | translate}}
</button>
@@ -19,7 +19,7 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { AddPermissionComponent } from './add-permission.component';
import { AddPermissionPanelComponent } from './add-permission-panel.component';
import { By } from '@angular/platform-browser';
import { setupTestBed } from '@alfresco/adf-core';
import { setupTestBed, NodesApiService } from '@alfresco/adf-core';
import { Observable } from 'rxjs/Observable';
import { fakeAuthorityResults } from '../../../mock/add-permission.component.mock';
import { ContentTestingModule } from '../../../testing/content.testing.module';
@@ -30,6 +30,7 @@ describe('AddPermissionComponent', () => {
let fixture: ComponentFixture<AddPermissionComponent>;
let element: HTMLElement;
let nodePermissionService: NodePermissionService;
let nodeApiService: NodesApiService;
setupTestBed({
imports: [
@@ -38,6 +39,8 @@ describe('AddPermissionComponent', () => {
});
beforeEach(() => {
nodeApiService = TestBed.get(NodesApiService);
spyOn(nodeApiService, 'getNode').and.returnValue(Observable.of({ id: 'fake-node', allowableOperations: ['updatePermissions']}));
fixture = TestBed.createComponent(AddPermissionComponent);
element = fixture.nativeElement;
nodePermissionService = TestBed.get(NodePermissionService);
@@ -67,12 +70,24 @@ describe('AddPermissionComponent', () => {
});
}));
it('should emit a success event when the node is updated', async(() => {
it('should NOT enable the ADD button when a selection is sent but the user does not have the permissions', async(() => {
const addPermissionPanelComponent: AddPermissionPanelComponent = fixture.debugElement.query(By.directive(AddPermissionPanelComponent)).componentInstance;
addPermissionPanelComponent.select.emit(fakeAuthorityResults);
fixture.componentInstance.currentNode = {id: 'fake-node-id'};
fixture.whenStable().then(() => {
fixture.detectChanges();
const addButton: HTMLButtonElement = <HTMLButtonElement> element.querySelector('#adf-add-permission-action-button');
expect(addButton.disabled).toBeTruthy();
});
}));
it('should emit a success event when the node is updated', (done) => {
fixture.componentInstance.selectedItems = fakeAuthorityResults;
spyOn(nodePermissionService, 'updateNodePermissions').and.returnValue(Observable.of({ id: 'fake-node-id'}));
fixture.componentInstance.success.subscribe((node) => {
expect(node.id).toBe('fake-node-id');
done();
});
fixture.detectChanges();
@@ -81,14 +96,25 @@ describe('AddPermissionComponent', () => {
const addButton: HTMLButtonElement = <HTMLButtonElement> element.querySelector('#adf-add-permission-action-button');
addButton.click();
});
}));
});
it('should emit an error event when the node update fail', async(() => {
it('should NOT emit a success event when the user does not have permission to update the node', () => {
fixture.componentInstance.selectedItems = fakeAuthorityResults;
fixture.componentInstance.currentNode = { id: 'fake-node-id' };
spyOn(nodePermissionService, 'updateNodePermissions').and.returnValue(Observable.of({ id: 'fake-node-id' }));
let spySuccess = spyOn(fixture.componentInstance, 'success');
fixture.componentInstance.applySelection();
expect(spySuccess).not.toHaveBeenCalled();
});
it('should emit an error event when the node update fail', (done) => {
fixture.componentInstance.selectedItems = fakeAuthorityResults;
spyOn(nodePermissionService, 'updateNodePermissions').and.returnValue(Observable.throw({ error: 'errored'}));
fixture.componentInstance.error.subscribe((error) => {
expect(error.error).toBe('errored');
done();
});
fixture.detectChanges();
@@ -97,6 +123,6 @@ describe('AddPermissionComponent', () => {
const addButton: HTMLButtonElement = <HTMLButtonElement> element.querySelector('#adf-add-permission-action-button');
addButton.click();
});
}));
});
});
@@ -18,6 +18,7 @@
import { Component, ViewEncapsulation, EventEmitter, Input, Output } from '@angular/core';
import { MinimalNodeEntity, MinimalNodeEntryEntity } from 'alfresco-js-api';
import { NodePermissionService } from '../../services/node-permission.service';
import { NodesApiService, ContentService, PermissionsEnum } from '@alfresco/adf-core';
@Component({
selector: 'adf-add-permission',
@@ -43,22 +44,32 @@ export class AddPermissionComponent {
currentNode: MinimalNodeEntryEntity;
currentNodeRoles: string[];
constructor(private nodePermissionService: NodePermissionService) {
constructor(private nodePermissionService: NodePermissionService,
private nodeApiService: NodesApiService,
private contentService: ContentService) {
this.nodeApiService.getNode(this.nodeId).subscribe((node) => this.currentNode = node);
}
onSelect(selection: MinimalNodeEntity[]) {
this.selectedItems = selection;
}
isAddEnabled(): boolean {
return this.contentService.hasPermission(this.currentNode, PermissionsEnum.UPDATEPERMISSIONS) &&
this.selectedItems.length !== 0;
}
applySelection() {
this.nodePermissionService.updateNodePermissions(this.nodeId, this.selectedItems)
.subscribe(
(node) => {
this.success.emit(node);
},
(error) => {
this.error.emit(error);
});
if (this.contentService.hasPermission(this.currentNode, PermissionsEnum.UPDATEPERMISSIONS)) {
this.nodePermissionService.updateNodePermissions(this.nodeId, this.selectedItems)
.subscribe(
(node) => {
this.success.emit(node);
},
(error) => {
this.error.emit(error);
});
}
}
}
@@ -21,8 +21,9 @@ import { InheritPermissionDirective } from './inherited-button.directive';
import { NodesApiService, setupTestBed, CoreModule } from '@alfresco/adf-core';
import { Observable } from 'rxjs/Observable';
const fakeNodeWithInherit: any = { id: 'fake-id', permissions : {isInheritanceEnabled : true}};
const fakeNodeNoInherit: any = { id: 'fake-id', permissions : {isInheritanceEnabled : false}};
const fakeNodeWithInherit: any = { id: 'fake-id', permissions : {isInheritanceEnabled : true}, allowableOperations: ['updatePermissions']};
const fakeNodeNoInherit: any = { id: 'fake-id', permissions : {isInheritanceEnabled : false}, allowableOperations: ['updatePermissions']};
const fakeNodeWithInheritNoPermission: any = { id: 'fake-id', permissions : {isInheritanceEnabled : true}};
describe('InheritPermissionDirective', () => {
@@ -95,4 +96,19 @@ describe('InheritPermissionDirective', () => {
});
}));
it('should not update the node when node has no permission', async(() => {
spyOn(nodeService, 'getNode').and.returnValue(Observable.of(fakeNodeWithInheritNoPermission));
let spyUpdateNode = spyOn(nodeService, 'updateNode');
component.updatedNode = true;
fixture.detectChanges();
const buttonPermission: HTMLButtonElement = <HTMLButtonElement> element.querySelector('#sample-button-permission');
expect(buttonPermission).not.toBeNull();
expect(element.querySelector('#update-notification')).not.toBeNull();
buttonPermission.click();
fixture.detectChanges();
fixture.whenStable().then(() => {
expect(spyUpdateNode).not.toHaveBeenCalled();
});
}));
});
@@ -17,7 +17,7 @@
/* tslint:disable:no-input-rename */
import { Directive, Input, Output, EventEmitter } from '@angular/core';
import { NodesApiService } from '@alfresco/adf-core';
import { NodesApiService, ContentService, PermissionsEnum } from '@alfresco/adf-core';
import { MinimalNodeEntryEntity } from 'alfresco-js-api';
@Directive({
@@ -38,15 +38,20 @@ export class InheritPermissionDirective {
@Output()
error: EventEmitter<any> = new EventEmitter<any>();
constructor(private nodeService: NodesApiService) {
constructor(private nodeService: NodesApiService,
private contentService: ContentService) {
}
onInheritPermissionClicked() {
this.nodeService.getNode(this.nodeId).subscribe((node: MinimalNodeEntryEntity) => {
const nodeBody = { permissions: { isInheritanceEnabled: !node.permissions.isInheritanceEnabled } };
this.nodeService.updateNode(this.nodeId, nodeBody, { include: ['permissions'] }).subscribe((nodeUpdated: MinimalNodeEntryEntity) => {
this.updated.emit(nodeUpdated);
}, (error) => this.error.emit(error));
if (this.contentService.hasPermission(node, PermissionsEnum.UPDATEPERMISSIONS)) {
const nodeBody = { permissions: { isInheritanceEnabled: !node.permissions.isInheritanceEnabled } };
this.nodeService.updateNode(this.nodeId, nodeBody, { include: ['permissions'] }).subscribe((nodeUpdated: MinimalNodeEntryEntity) => {
this.updated.emit(nodeUpdated);
}, (error) => this.error.emit(error));
} else {
this.error.emit('PERMISSION_MANAGER.ERROR.NOT-ALLOWED');
}
});
}
@@ -16,13 +16,14 @@
*/
import { TestBed, async } from '@angular/core/testing';
import { AppConfigService, setupTestBed } from '@alfresco/adf-core';
import { AppConfigService, setupTestBed, ContentService } from '@alfresco/adf-core';
import { NodePermissionDialogService } from './node-permission-dialog.service';
import { MatDialog } from '@angular/material';
import { Observable } from 'rxjs/Observable';
import { Subject } from 'rxjs/Subject';
import { ContentTestingModule } from '../../testing/content.testing.module';
import { NodePermissionService } from './node-permission.service';
import { Node } from 'alfresco-js-api';
describe('NodePermissionDialogService', () => {
@@ -31,6 +32,7 @@ describe('NodePermissionDialogService', () => {
let spyOnDialogOpen: jasmine.Spy;
let afterOpenObservable: Subject<any>;
let nodePermissionService: NodePermissionService;
let contentService: ContentService;
setupTestBed({
imports: [ContentTestingModule],
@@ -44,6 +46,7 @@ describe('NodePermissionDialogService', () => {
materialDialog = TestBed.get(MatDialog);
afterOpenObservable = new Subject<any>();
nodePermissionService = TestBed.get(NodePermissionService);
contentService = TestBed.get(ContentService);
spyOnDialogOpen = spyOn(materialDialog, 'open').and.returnValue({
afterOpen: () => afterOpenObservable,
afterClosed: () => Observable.of({}),
@@ -57,27 +60,65 @@ describe('NodePermissionDialogService', () => {
expect(service).not.toBeNull();
});
it('should be able to open the dialog showing node permissions', () => {
service.openAddPermissionDialog('fake-node-id', 'fake-title');
expect(spyOnDialogOpen).toHaveBeenCalled();
describe('when node has permission to update permissions', () => {
let fakePermissionNode = {};
beforeEach(() => {
fakePermissionNode = <Node> { id: 'fake-permission-node', allowableOperations: ['updatePermissions']};
});
it('should be able to open the dialog showing node permissions', () => {
service.openAddPermissionDialog(fakePermissionNode, 'fake-title');
expect(spyOnDialogOpen).toHaveBeenCalled();
});
it('should return the updated node', (done) => {
spyOn(nodePermissionService, 'updateNodePermissions').and.returnValue(Observable.of({id : 'fake-node-updated'}));
spyOn(service, 'openAddPermissionDialog').and.returnValue(Observable.of({}));
spyOn(contentService, 'getNode').and.returnValue(Observable.of(fakePermissionNode));
service.updateNodePermissionByDialog('fake-node-id', 'fake-title').subscribe((node) => {
expect(node.id).toBe('fake-node-updated');
done();
});
});
it('should throw an error if the update of the node fails', (done) => {
spyOn(nodePermissionService, 'updateNodePermissions').and.returnValue(Observable.throw({error : 'error'}));
spyOn(service, 'openAddPermissionDialog').and.returnValue(Observable.of({}));
spyOn(contentService, 'getNode').and.returnValue(Observable.of(fakePermissionNode));
service.updateNodePermissionByDialog('fake-node-id', 'fake-title').subscribe(() => {
Observable.throw('This call should fail');
}, (error) => {
expect(error.error).toBe('error');
done();
});
});
});
it('should throw an error if the update of the node fails', async(() => {
spyOn(nodePermissionService, 'updateNodePermissions').and.returnValue(Observable.throw({error : 'error'}));
spyOn(service, 'openAddPermissionDialog').and.returnValue(Observable.of({}));
service.updateNodePermissionByDialog('fake-node-id', 'fake-title').subscribe(() => {
Observable.throw('This call should fail');
}, (error) => {
expect(error.error).toBe('error');
});
}));
describe('when node does not have permission to update permissions', () => {
it('should return the updated node', async(() => {
spyOn(nodePermissionService, 'updateNodePermissions').and.returnValue(Observable.of({id : 'fake-node'}));
spyOn(service, 'openAddPermissionDialog').and.returnValue(Observable.of({}));
service.updateNodePermissionByDialog('fake-node-id', 'fake-title').subscribe((node) => {
expect(node.id).toBe('fake-node');
});
}));
let fakeForbiddenNode = {};
beforeEach(() => {
fakeForbiddenNode = <Node> { id: 'fake-permission-node', allowableOperations: ['update']};
});
it('should not be able to open the dialog showing node permissions', () => {
service.openAddPermissionDialog(fakeForbiddenNode, 'fake-title');
expect(spyOnDialogOpen).not.toHaveBeenCalled();
});
it('should return the updated node', (done) => {
spyOn(contentService, 'getNode').and.returnValue(Observable.of(fakeForbiddenNode));
service.updateNodePermissionByDialog('fake-node-id', 'fake-title').subscribe((node) => {
Observable.throw('This call should fail');
},
(error) => {
expect(error.message).toBe('PERMISSION_MANAGER.ERROR.NOT-ALLOWED');
done();
});
});
});
});
@@ -21,14 +21,16 @@ import { Subject } from 'rxjs/Subject';
import { Observable } from 'rxjs/Observable';
import { AddPermissionDialogComponent } from '../components/add-permission/add-permission-dialog.component';
import { AddPermissionDialogData } from '../components/add-permission/add-permission-dialog-data.interface';
import { MinimalNodeEntity, MinimalNodeEntryEntity } from 'alfresco-js-api';
import { MinimalNodeEntity, MinimalNodeEntryEntity, Node } from 'alfresco-js-api';
import { NodePermissionService } from './node-permission.service';
import { ContentService, PermissionsEnum } from '@alfresco/adf-core';
@Injectable()
export class NodePermissionDialogService {
constructor(private dialog: MatDialog,
private nodePermissionService: NodePermissionService) {
private nodePermissionService: NodePermissionService,
private contentService: ContentService) {
}
/**
@@ -37,21 +39,27 @@ export class NodePermissionDialogService {
* @param title Dialog title
* @returns Node with updated permissions
*/
openAddPermissionDialog(nodeId: string, title?: string): Observable<MinimalNodeEntity[]> {
const confirm = new Subject<MinimalNodeEntity[]>();
openAddPermissionDialog(node: Node, title?: string): Observable<MinimalNodeEntity[]> {
if (this.contentService.hasPermission(node, PermissionsEnum.UPDATEPERMISSIONS)) {
const confirm = new Subject<MinimalNodeEntity[]>();
confirm.subscribe({
complete: this.close.bind(this)
});
confirm.subscribe({
complete: this.close.bind(this)
});
const data: AddPermissionDialogData = {
nodeId: nodeId,
title: title,
confirm : confirm
};
const data: AddPermissionDialogData = {
nodeId: node.id,
title: title,
confirm: confirm
};
this.openDialog(data, 'adf-add-permission-dialog', '630px');
return confirm;
this.openDialog(data, 'adf-add-permission-dialog', '630px');
return confirm;
} else {
let errors = new Error(JSON.stringify({ error: { statusCode: 403 } }));
errors.message = 'PERMISSION_MANAGER.ERROR.NOT-ALLOWED';
return Observable.throw(errors);
}
}
private openDialog(data: any, currentPanelClass: string, chosenWidth: string) {
@@ -72,8 +80,10 @@ export class NodePermissionDialogService {
* @returns Node with updated permissions
*/
updateNodePermissionByDialog(nodeId?: string, title?: string): Observable<MinimalNodeEntryEntity> {
return this.openAddPermissionDialog(nodeId, title).switchMap((selection) => {
return this.nodePermissionService.updateNodePermissions(nodeId, selection);
return this.contentService.getNode(nodeId, { include: ['allowableOperations'] }).switchMap((node) => {
return this.openAddPermissionDialog(node.entry, title).switchMap((selection) => {
return this.nodePermissionService.updateNodePermissions(nodeId, selection);
});
});
}
}
@@ -5,7 +5,7 @@
matInput [formControl]="from" [errorStateMatcher]="matcher"
placeholder="{{ 'SEARCH.FILTER.RANGE.FROM' | translate }}"
autocomplete="off">
<mat-error *ngIf="from.hasError('pattern')">
<mat-error *ngIf="from.hasError('pattern') || from.hasError('min')">
{{ 'SEARCH.FILTER.VALIDATION.INVALID-FORMAT' | translate }}
</mat-error>
<mat-error *ngIf="from.hasError('required')">
@@ -18,7 +18,7 @@
matInput [formControl]="to" [errorStateMatcher]="matcher"
placeholder="{{ 'SEARCH.FILTER.RANGE.TO' | translate }}"
autocomplete="off">
<mat-error *ngIf="to.hasError('pattern')">
<mat-error *ngIf="to.hasError('pattern') || to.hasError('min')">
{{ 'SEARCH.FILTER.VALIDATION.INVALID-FORMAT' | translate }}
</mat-error>
<mat-error *ngIf="to.hasError('required')">
@@ -160,4 +160,10 @@ describe('SearchNumberRangeComponent', () => {
component.from = new FormControl(123, component.validators);
expect(component.from.hasError('required')).toBe(false);
});
it('should throw error if "from" value is a negative value', () => {
component.ngOnInit();
component.from = new FormControl(-100, component.validators);
expect(component.from.hasError('min')).toBe(true);
});
});
@@ -55,7 +55,8 @@ export class SearchNumberRangeComponent implements SearchWidget, OnInit {
this.validators = Validators.compose([
Validators.required,
Validators.pattern(/^-?(0|[1-9]\d*)?$/)
Validators.pattern(/^-?(0|[1-9]\d*)?$/),
Validators.min(0)
]);
this.from = new FormControl('', this.validators);
@@ -78,9 +79,8 @@ export class SearchNumberRangeComponent implements SearchWidget, OnInit {
map.set('TO', model.to);
const value = this.formatString(this.format, map);
const query = `${this.field}:${value}`;
this.context.queryFragments[this.id] = query;
this.context.queryFragments[this.id] = `${this.field}:${value}`;
this.context.update();
}
}
+15 -3
View File
@@ -21,10 +21,12 @@ import { FormsModule } from '@angular/forms';
import {
MatButtonModule,
MatDatepickerModule,
MatTableModule,
MatIconModule,
MatInputModule,
MatCheckboxModule,
MatNativeDateModule
MatNativeDateModule,
MatSelectModule
} from '@angular/material';
import { MatDatetimepickerModule, MatNativeDatetimeModule } from '@mat-datetimepicker/core';
import { FlexLayoutModule } from '@angular/flex-layout';
@@ -37,6 +39,8 @@ import { CardViewDateItemComponent } from './components/card-view-dateitem/card-
import { CardViewItemDispatcherComponent } from './components/card-view-item-dispatcher/card-view-item-dispatcher.component';
import { CardViewMapItemComponent } from './components/card-view-mapitem/card-view-mapitem.component';
import { CardViewTextItemComponent } from './components/card-view-textitem/card-view-textitem.component';
import { CardViewKeyValuePairsItemComponent } from './components/card-view-keyvaluepairsitem/card-view-keyvaluepairsitem.component';
import { CardViewSelectItemComponent } from './components/card-view-selectitem/card-view-selectitem.component';
@NgModule({
imports: [
@@ -48,7 +52,9 @@ import { CardViewTextItemComponent } from './components/card-view-textitem/card-
MatNativeDateModule,
MatCheckboxModule,
MatInputModule,
MatTableModule,
MatIconModule,
MatSelectModule,
MatButtonModule,
MatDatetimepickerModule,
MatNativeDatetimeModule
@@ -59,6 +65,8 @@ import { CardViewTextItemComponent } from './components/card-view-textitem/card-
CardViewDateItemComponent,
CardViewMapItemComponent,
CardViewTextItemComponent,
CardViewKeyValuePairsItemComponent,
CardViewSelectItemComponent,
CardViewItemDispatcherComponent,
CardViewContentProxyDirective
],
@@ -66,14 +74,18 @@ import { CardViewTextItemComponent } from './components/card-view-textitem/card-
CardViewBoolItemComponent,
CardViewDateItemComponent,
CardViewMapItemComponent,
CardViewTextItemComponent
CardViewTextItemComponent,
CardViewSelectItemComponent,
CardViewKeyValuePairsItemComponent
],
exports: [
CardViewComponent,
CardViewBoolItemComponent,
CardViewDateItemComponent,
CardViewMapItemComponent,
CardViewTextItemComponent
CardViewTextItemComponent,
CardViewSelectItemComponent,
CardViewKeyValuePairsItemComponent
]
})
export class CardViewModule {}
@@ -0,0 +1,58 @@
<div class="adf-property-label">{{ property.label | translate }}</div>
<div class="adf-property-value">
<div *ngIf="isEditable()">
{{ 'CORE.CARDVIEW.KEYVALUEPAIRS.ADD' | translate }}
<button (click)="add()" mat-icon-button class="card-view__key-value-pairs__add-btn">
<mat-icon>add</mat-icon>
</button>
</div>
<div *ngIf="!isEditable()" class="card-view__key-value-pairs__read-only">
<mat-table #table [dataSource]="matTableValues" class="mat-elevation-z8">
<ng-container matColumnDef="name">
<mat-header-cell *matHeaderCellDef>{{ 'CORE.CARDVIEW.KEYVALUEPAIRS.NAME' | translate }}</mat-header-cell>
<mat-cell *matCellDef="let item">{{item.name}}</mat-cell>
</ng-container>
<ng-container matColumnDef="value">
<mat-header-cell *matHeaderCellDef>{{ 'CORE.CARDVIEW.KEYVALUEPAIRS.VALUE' | translate }}</mat-header-cell>
<mat-cell *matCellDef="let item">{{item.value}}</mat-cell>
</ng-container>
<mat-header-row *matHeaderRowDef="['name', 'value']"></mat-header-row>
<mat-row *matRowDef="let row; columns: ['name', 'value'];"></mat-row>
</mat-table>
</div>
<div class="card-view__key-value-pairs" *ngIf="isEditable() && values && values.length">
<div class="card-view__key-value-pairs__row">
<div class="card-view__key-value-pairs__col">{{ 'CORE.CARDVIEW.KEYVALUEPAIRS.NAME' | translate }}</div>
<div class="card-view__key-value-pairs__col">{{ 'CORE.CARDVIEW.KEYVALUEPAIRS.VALUE' | translate }}</div>
</div>
<div class="card-view__key-value-pairs__row" *ngFor="let item of values; let i = index">
<div class="card-view__key-value-pairs__col">
<mat-form-field class="example-full-width">
<input matInput
placeholder="{{ 'CORE.CARDVIEW.KEYVALUEPAIRS.NAME' | translate }}"
(blur)="onBlur(item.value)"
[attr.data-automation-id]="'card-'+ property.key +'-name-input-' + i"
[(ngModel)]="values[i].name">
</mat-form-field>
</div>
<div class="card-view__key-value-pairs__col">
<mat-form-field class="example-full-width">
<input matInput
placeholder="{{ 'CORE.CARDVIEW.KEYVALUEPAIRS.VALUE' | translate }}"
(blur)="onBlur(item.value)"
[attr.data-automation-id]="'card-'+ property.key +'-value-input-' + i"
[(ngModel)]="values[i].value">
</mat-form-field>
</div>
<button mat-icon-button (click)="remove(i)" class="card-view__key-value-pairs__remove-btn">
<mat-icon>close</mat-icon>
</button>
</div>
</div>
</div>
@@ -0,0 +1,22 @@
.card-view {
&__key-value-pairs {
&__col {
display: inline-block;
width: 39%;
.mat-form-field {
width: 100%;
}
}
&__read-only {
.mat-table {
box-shadow: none;
}
.mat-header-row, .mat-row {
padding: 0;
}
}
}
}
@@ -0,0 +1,176 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { ComponentFixture, TestBed, async } from '@angular/core/testing';
import { By } from '@angular/platform-browser';
import { CardViewKeyValuePairsItemModel } from '../../models/card-view-keyvaluepairs.model';
import { CardViewKeyValuePairsItemComponent } from './card-view-keyvaluepairsitem.component';
import { setupTestBed } from '../../../testing/setupTestBed';
import { CoreTestingModule } from '../../../testing/core.testing.module';
import { CardViewUpdateService } from '../../services/card-view-update.service';
describe('CardViewKeyValuePairsItemComponent', () => {
let fixture: ComponentFixture<CardViewKeyValuePairsItemComponent>;
let component: CardViewKeyValuePairsItemComponent;
let cardViewUpdateService;
const mockEmptyData = [{ name: '', value: '' }];
const mockData = [{ name: 'test-name', value: 'test-value' }];
setupTestBed({
imports: [CoreTestingModule]
});
beforeEach(() => {
fixture = TestBed.createComponent(CardViewKeyValuePairsItemComponent);
cardViewUpdateService = TestBed.get(CardViewUpdateService);
component = fixture.componentInstance;
component.property = new CardViewKeyValuePairsItemModel({
label: 'Key Value Pairs',
value: [],
key: 'key-value-pairs',
editable: true
});
component.editable = true;
});
afterEach(() => {
fixture.destroy();
});
describe('Component', () => {
it('should render the label', () => {
fixture.detectChanges();
const labelValue = fixture.debugElement.query(By.css('.adf-property-label'));
expect(labelValue).not.toBeNull();
expect(labelValue.nativeElement.innerText).toBe('Key Value Pairs');
});
it('should render readOnly table is editable property is FALSE', () => {
component.property = new CardViewKeyValuePairsItemModel({
label: 'Key Value Pairs',
value: mockData,
key: 'key-value-pairs'
});
component.ngOnChanges();
fixture.detectChanges();
const table = fixture.debugElement.query(By.css('.card-view__key-value-pairs__read-only'));
const form = fixture.debugElement.query(By.css('.card-view__key-value-pairs'));
expect(table).not.toBeNull();
expect(form).toBeNull();
});
it('should add new item on ADD button click', () => {
component.ngOnChanges();
fixture.detectChanges();
const addButton = fixture.debugElement.query(By.css('.card-view__key-value-pairs__add-btn'));
addButton.triggerEventHandler('click', null);
fixture.detectChanges();
expect(JSON.stringify(component.values)).toBe(JSON.stringify(mockEmptyData));
const content = fixture.debugElement.query(By.css('.card-view__key-value-pairs'));
expect(content).not.toBeNull();
let nameInput = fixture.debugElement.query(By.css(`[data-automation-id="card-${component.property.key}-name-input-0"]`));
let valueInput = fixture.debugElement.query(By.css(`[data-automation-id="card-${component.property.key}-value-input-0"]`));
expect(nameInput).not.toBeNull();
expect(valueInput).not.toBeNull();
});
it('should remove an item from list on REMOVE button click', () => {
spyOn(cardViewUpdateService, 'update');
component.ngOnChanges();
fixture.detectChanges();
const addButton = fixture.debugElement.query(By.css('.card-view__key-value-pairs__add-btn'));
addButton.triggerEventHandler('click', null);
fixture.detectChanges();
const removeButton = fixture.debugElement.query(By.css('.card-view__key-value-pairs__remove-btn'));
removeButton.triggerEventHandler('click', null);
fixture.detectChanges();
expect(component.values.length).toBe(0);
const content = fixture.debugElement.query(By.css('.card-view__key-value-pairs'));
expect(content).toBeNull();
expect(cardViewUpdateService.update).toHaveBeenCalled();
expect(component.property.value.length).toBe(0);
});
it('should update property on input blur', async(() => {
spyOn(cardViewUpdateService, 'update');
component.ngOnChanges();
fixture.detectChanges();
const addButton = fixture.debugElement.query(By.css('.card-view__key-value-pairs__add-btn'));
addButton.triggerEventHandler('click', null);
fixture.detectChanges();
let nameInput = fixture.debugElement.query(By.css(`[data-automation-id="card-${component.property.key}-name-input-0"]`));
let valueInput = fixture.debugElement.query(By.css(`[data-automation-id="card-${component.property.key}-value-input-0"]`));
nameInput.nativeElement.value = mockData[0].name;
nameInput.nativeElement.dispatchEvent(new Event('input'));
valueInput.nativeElement.value = mockData[0].value;
valueInput.nativeElement.dispatchEvent(new Event('input'));
fixture.whenStable().then(() => {
fixture.detectChanges();
valueInput.triggerEventHandler('blur', null);
fixture.detectChanges();
expect(cardViewUpdateService.update).toHaveBeenCalled();
expect(JSON.stringify(component.property.value)).toBe(JSON.stringify(mockData));
});
}));
it('should not update property if at least one input is empty on blur', async(() => {
spyOn(cardViewUpdateService, 'update');
component.ngOnChanges();
fixture.detectChanges();
const addButton = fixture.debugElement.query(By.css('.card-view__key-value-pairs__add-btn'));
addButton.triggerEventHandler('click', null);
fixture.detectChanges();
let valueInput = fixture.debugElement.query(By.css(`[data-automation-id="card-${component.property.key}-value-input-0"]`));
valueInput.nativeElement.value = mockData[0].value;
valueInput.nativeElement.dispatchEvent(new Event('input'));
fixture.whenStable().then(() => {
fixture.detectChanges();
valueInput.triggerEventHandler('blur', null);
fixture.detectChanges();
expect(cardViewUpdateService.update).not.toHaveBeenCalled();
});
}));
});
});
@@ -0,0 +1,75 @@
/*!
* @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, Input, OnChanges } from '@angular/core';
import { CardViewUpdateService } from '../../services/card-view-update.service';
import { CardViewKeyValuePairsItemModel } from '../../models/card-view.models';
import { CardViewKeyValuePairsItemType } from '../../interfaces/card-view.interfaces';
import { MatTableDataSource } from '@angular/material';
@Component({
selector: 'adf-card-view-boolitem',
templateUrl: './card-view-keyvaluepairsitem.component.html',
styleUrls: ['./card-view-keyvaluepairsitem.component.scss']
})
export class CardViewKeyValuePairsItemComponent implements OnChanges {
@Input()
property: CardViewKeyValuePairsItemModel;
@Input()
editable: boolean = false;
values: CardViewKeyValuePairsItemType[];
matTableValues: MatTableDataSource<CardViewKeyValuePairsItemType>;
constructor(private cardViewUpdateService: CardViewUpdateService) {}
ngOnChanges() {
this.values = this.property.value || [];
this.matTableValues = new MatTableDataSource(this.values);
}
isEditable(): boolean {
return this.editable && this.property.editable;
}
add(): void {
this.values.push({ name: '', value: '' });
}
remove(index: number): void {
this.values.splice(index, 1);
this.save(true);
}
onBlur(value): void {
if (value.length) {
this.save();
}
}
save(remove?: boolean): void {
const validValues = this.values.filter(i => i.name.length && i.value.length);
if (remove || validValues.length) {
this.cardViewUpdateService.update(this.property, validValues);
this.property.value = validValues;
}
}
}
@@ -0,0 +1,13 @@
<div class="adf-property-label">{{ property.label | translate }}</div>
<div class="adf-property-value">
<div *ngIf="!isEditable()" data-automation-class="read-only-value">{{ property.displayValue | async }}</div>
<div *ngIf="isEditable()">
<mat-form-field>
<mat-select placeholder="{{ property.label | translate }}" [(value)]="value" (change)="onChange($event)" data-automation-class="select-box">
<mat-option *ngFor="let option of getOptions() | async" [value]="option.key">
{{ option.label | translate }}
</mat-option>
</mat-select>
</mat-form-field>
</div>
</div>
@@ -0,0 +1,113 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { By } from '@angular/platform-browser';
import { CardViewSelectItemModel } from '../../models/card-view-selectitem.model';
import { CardViewUpdateService } from '../../services/card-view-update.service';
import { CardViewSelectItemComponent } from './card-view-selectitem.component';
import { setupTestBed } from '../../../testing/setupTestBed';
import { CoreTestingModule } from '../../../testing/core.testing.module';
import { of } from 'rxjs/observable/of';
describe('CardViewSelectItemComponent', () => {
let fixture: ComponentFixture<CardViewSelectItemComponent>;
let component: CardViewSelectItemComponent;
let cardViewUpdateService;
const mockData = [{ key: 'one', label: 'One' }, { key: 'two', label: 'Two' }, { key: 'three', label: 'Three' }];
const mockDefaultProps = {
label: 'Select box label',
value: 'two',
options$: of(mockData),
key: 'key',
editable: true
};
setupTestBed({
imports: [CoreTestingModule]
});
beforeEach(() => {
fixture = TestBed.createComponent(CardViewSelectItemComponent);
cardViewUpdateService = TestBed.get(CardViewUpdateService);
component = fixture.componentInstance;
component.property = new CardViewSelectItemModel(mockDefaultProps);
});
afterEach(() => {
fixture.destroy();
});
describe('Rendering', () => {
it('should render the label', () => {
fixture.detectChanges();
const labelValue = fixture.debugElement.query(By.css('.adf-property-label'));
expect(labelValue).not.toBeNull();
expect(labelValue.nativeElement.innerText).toBe('Select box label');
});
it('should render readOnly value is editable property is FALSE', () => {
component.property = new CardViewSelectItemModel({
...mockDefaultProps,
editable: false
});
component.ngOnChanges();
fixture.detectChanges();
const readOnly = fixture.debugElement.query(By.css('[data-automation-class="read-only-value"]'));
const selectBox = fixture.debugElement.query(By.css('[data-automation-class="select-box"]'));
expect(readOnly).not.toBeNull();
expect(selectBox).toBeNull();
});
it('should render select box if editable property is TRUE', () => {
component.ngOnChanges();
component.editable = true;
fixture.detectChanges();
const selectBox = fixture.debugElement.query(By.css('[data-automation-class="select-box"]'));
expect(selectBox).not.toBeNull();
});
it('should update property on input blur', async(() => {
spyOn(cardViewUpdateService, 'update');
component.ngOnChanges();
component.editable = true;
fixture.detectChanges();
const selectBox = fixture.debugElement.query(By.css('[data-automation-class="select-box"]'));
selectBox.nativeElement.click();
fixture.detectChanges();
const option = fixture.debugElement.query(By.css(`mat-option[ng-reflect-value="${mockData[2].key}"]`));
option.nativeElement.click();
fixture.detectChanges();
fixture.whenStable().then(() => {
fixture.detectChanges();
expect(cardViewUpdateService.update).toHaveBeenCalled();
expect(component.property.value).toBe(mockData[2].key);
});
}));
});
});
@@ -0,0 +1,56 @@
/*!
* @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, Input, OnChanges } from '@angular/core';
import { CardViewSelectItemModel } from '../../models/card-view-selectitem.model';
import { CardViewUpdateService } from '../../services/card-view-update.service';
import { Observable } from 'rxjs/Observable';
import { CardViewSelectItemOption } from '../../interfaces/card-view.interfaces';
import { MatSelectChange } from '@angular/material';
@Component({
selector: 'adf-card-view-selectitem',
templateUrl: './card-view-selectitem.component.html'
})
export class CardViewSelectItemComponent implements OnChanges {
@Input() property: CardViewSelectItemModel<string>;
@Input() editable: boolean = false;
@Input() options$: Observable<CardViewSelectItemOption<string>[]>;
value: string;
constructor(private cardViewUpdateService: CardViewUpdateService) {}
ngOnChanges(): void {
this.value = this.property.value;
}
isEditable(): boolean {
return this.editable && this.property.editable;
}
getOptions(): Observable<CardViewSelectItemOption<string>[]> {
return this.options$ || this.property.options$;
}
onChange(event: MatSelectChange): void {
this.cardViewUpdateService.update(this.property, event.value);
this.property.value = event.value;
}
}
@@ -21,3 +21,5 @@ export * from './card-view-dateitem/card-view-dateitem.component';
export * from './card-view-item-dispatcher/card-view-item-dispatcher.component';
export * from './card-view-mapitem/card-view-mapitem.component';
export * from './card-view-textitem/card-view-textitem.component';
export * from './card-view-selectitem/card-view-selectitem.component';
export * from './card-view-keyvaluepairsitem/card-view-keyvaluepairsitem.component';
@@ -0,0 +1,27 @@
/*!
* @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 { CardViewItemProperties } from './card-view-item-properties.interface';
export interface CardViewKeyValuePairsItemType {
name: string;
value: string;
}
export interface CardViewKeyValuePairsItemProperties extends CardViewItemProperties {
value: CardViewKeyValuePairsItemType[];
}
@@ -0,0 +1,29 @@
/*!
* @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 { CardViewItemProperties } from './card-view-item-properties.interface';
import { Observable } from 'rxjs/Observable';
export interface CardViewSelectItemOption<T> {
label: string;
key: T;
}
export interface CardViewSelectItemProperties<T> extends CardViewItemProperties {
value: string;
options$: Observable<CardViewSelectItemOption<T>[]>;
}
@@ -22,3 +22,5 @@ export * from './card-view-textitem-properties.interface';
export * from './card-view-dateitem-properties.interface';
export * from './card-view-boolitem-properties.interface';
export * from './card-view-textitem-pipe-property.interface';
export * from './card-view-keyvaluepairsitem-properties.interface';
export * from './card-view-selectitem-properties.interface';
@@ -0,0 +1,33 @@
/*!
* @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 { CardViewItem } from '../interfaces/card-view-item.interface';
import { DynamicComponentModel } from '../../services/dynamic-component-mapper.service';
import { CardViewBaseItemModel } from './card-view-baseitem.model';
import { CardViewKeyValuePairsItemProperties } from '../interfaces/card-view.interfaces';
export class CardViewKeyValuePairsItemModel extends CardViewBaseItemModel implements CardViewItem, DynamicComponentModel {
type: string = 'keyvaluepairs';
constructor(obj: CardViewKeyValuePairsItemProperties) {
super(obj);
}
get displayValue() {
return this.value;
}
}
@@ -0,0 +1,46 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { async } from '@angular/core/testing';
import { CardViewSelectItemModel } from './card-view-selectitem.model';
import { CardViewSelectItemProperties } from '../interfaces/card-view.interfaces';
import { of } from 'rxjs/observable/of';
describe('CardViewSelectItemModel', () => {
let properties: CardViewSelectItemProperties;
const mockData = [{ key: 'one', label: 'One' }, { key: 'two', label: 'Two' }, { key: 'three', label: 'Three' }];
beforeEach(() => {
properties = {
label: 'Select box label',
value: 'two',
options$: of(mockData),
key: 'key',
editable: true
};
});
describe('displayValue', () => {
it('should return the value if it is present', async(() => {
const itemModel = new CardViewSelectItemModel(properties);
itemModel.displayValue.subscribe(value => {
expect(value).toBe(mockData[1].label);
});
}));
});
});
@@ -0,0 +1,44 @@
/*!
* @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 { CardViewItem } from '../interfaces/card-view-item.interface';
import { DynamicComponentModel } from '../../services/dynamic-component-mapper.service';
import { CardViewBaseItemModel } from './card-view-baseitem.model';
import { CardViewSelectItemProperties, CardViewSelectItemOption } from '../interfaces/card-view.interfaces';
import { Observable } from 'rxjs/Observable';
import { switchMap } from 'rxjs/operators';
import { of } from 'rxjs/observable/of';
export class CardViewSelectItemModel<T> extends CardViewBaseItemModel implements CardViewItem, DynamicComponentModel {
type: string = 'select';
options$: Observable<CardViewSelectItemOption<T>[]>;
constructor(obj: CardViewSelectItemProperties<T>) {
super(obj);
this.options$ = obj.options$;
}
get displayValue() {
return this.options$.pipe(
switchMap(options => {
const option = options.find(o => o.key === this.value);
return of(option ? option.label : '');
})
);
}
}
@@ -23,3 +23,5 @@ export * from './card-view-floatitem.model';
export * from './card-view-intitem.model';
export * from './card-view-mapitem.model';
export * from './card-view-textitem.model';
export * from './card-view-keyvaluepairs.model';
export * from './card-view-selectitem.model';
+3 -1
View File
@@ -20,7 +20,9 @@ export {
CardViewBoolItemComponent,
CardViewDateItemComponent,
CardViewMapItemComponent,
CardViewTextItemComponent
CardViewTextItemComponent,
CardViewSelectItemComponent,
CardViewKeyValuePairsItemComponent
} from './components/card-view.components';
export * from './interfaces/card-view.interfaces';
@@ -19,7 +19,9 @@ import { Injectable, Type } from '@angular/core';
import { CardViewDateItemComponent } from '../components/card-view-dateitem/card-view-dateitem.component';
import { CardViewMapItemComponent } from '../components/card-view-mapitem/card-view-mapitem.component';
import { CardViewTextItemComponent } from '../components/card-view-textitem/card-view-textitem.component';
import { CardViewSelectItemComponent } from '../components/card-view-selectitem/card-view-selectitem.component';
import { CardViewBoolItemComponent } from '../components/card-view-boolitem/card-view-boolitem.component';
import { CardViewKeyValuePairsItemComponent } from '../components/card-view-keyvaluepairsitem/card-view-keyvaluepairsitem.component';
import { DynamicComponentMapper, DynamicComponentResolveFunction, DynamicComponentResolver } from '../../services/dynamic-component-mapper.service';
@Injectable()
@@ -29,11 +31,13 @@ export class CardItemTypeService extends DynamicComponentMapper {
protected types: { [key: string]: DynamicComponentResolveFunction } = {
'text': DynamicComponentResolver.fromType(CardViewTextItemComponent),
'select': DynamicComponentResolver.fromType(CardViewSelectItemComponent),
'int': DynamicComponentResolver.fromType(CardViewTextItemComponent),
'float': DynamicComponentResolver.fromType(CardViewTextItemComponent),
'date': DynamicComponentResolver.fromType(CardViewDateItemComponent),
'datetime': DynamicComponentResolver.fromType(CardViewDateItemComponent),
'bool': DynamicComponentResolver.fromType(CardViewBoolItemComponent),
'map': DynamicComponentResolver.fromType(CardViewMapItemComponent)
'map': DynamicComponentResolver.fromType(CardViewMapItemComponent),
'keyvaluepairs': DynamicComponentResolver.fromType(CardViewKeyValuePairsItemComponent)
};
}
@@ -390,8 +390,6 @@
.ellipsis-cell {
.cell-container {
height: 100%;
display: inline-grid;
width: 100%;
}
.cell-container > * {
@@ -407,7 +405,7 @@
/* visible content */
.cell-value {
display: block;
position: relative;
position: absolute;
max-width: calc(100% - 2em);
white-space: nowrap;
overflow: hidden;
@@ -1,5 +1,5 @@
<div class="adf-amount-widget__container adf-amount-widget {{field.className}}" [class.adf-invalid]="!field.isValid" [class.adf-readonly]="field.readOnly">
<mat-form-field floatPlaceholder="never" class="adf-amount-widget__input">
<mat-form-field class="adf-amount-widget__input">
<label class="adf-label" [attr.for]="field.id">{{field.name}}<span *ngIf="isRequired()">*</span></label>
<span matPrefix class="adf-amount-widget__prefix-spacing"> {{currency }}</span>
<input matInput
@@ -7,13 +7,12 @@
type="text"
[id]="field.id"
[required]="isRequired()"
[placeholder]="field.placeholder"
[value]="field.value"
[(ngModel)]="field.value"
(ngModelChange)="onFieldChanged(field)"
[disabled]="field.readOnly"
placeholder="{{field.placeholder}}">
[disabled]="field.readOnly">
</mat-form-field>
<error-widget [error]="field.validationSummary" ></error-widget>
<error-widget *ngIf="isInvalidFieldRequired()" required="{{ 'FORM.FIELD.REQUIRED' | translate }}"></error-widget>
</div>
@@ -5,20 +5,27 @@
width: 100%;
vertical-align: baseline !important;
.mat-input-element {
margin-left: 11px;
}
.mat-input-prefix {
position: absolute;
margin-top: 58px;
}
.mat-input-placeholder {
margin-top: 5px;
display: none;
}
.mat-form-field-flex{
position: relative;
padding-top: 18.5px;
}
.mat-form-field-infix{
position: static;
padding-top: 19px;
}
.adf-label{
position: absolute;
top: 18.5px;
left:0;
}
}
&-amount-widget__container {
@@ -8,32 +8,29 @@
display: none;
}
.adf {
&-field-list {
padding: 0;
list-style-type: none;
width: 100%;
height: 100%;
.container-widget__header-text {
border-bottom: 1px solid rgba(0, 0, 0, 0.87);
padding-bottom: 10px;
cursor: default;
user-select: none;
-webkit-user-select: none;
/* Chrome/Safari/Opera */
-moz-user-select: none;
/* Firefox */
-ms-user-select: none;
/* IE/Edge */
-webkit-touch-callout: none;
/* iOS Safari */
&.collapsible {
cursor: pointer;
}
}
.container-widget__header-text {
border-bottom: 1px solid rgba(0, 0, 0, 0.87);
margin-left: 10px;
margin-right: 10px;
cursor: default;
user-select: none;
-webkit-user-select: none;
/* Chrome/Safari/Opera */
-moz-user-select: none;
/* Firefox */
-ms-user-select: none;
/* IE/Edge */
-webkit-touch-callout: none;
/* iOS Safari */
&.collapsible {
cursor: pointer;
}
}
.adf-field-list {
padding: 0;
list-style-type: none;
width: 100%;
height: 100%;
}
container-widget {
@@ -50,7 +47,7 @@
padding-left: 1%;
padding-right: 1%;
}
@media screen and ($mat-small) {
.grid-list-item {
flex: 1 0 100%;
@@ -17,7 +17,13 @@
[class.adf-dynamic-table-widget__row-selected]="row.selected" (keyup)="onKeyPressed($event, row)">
<td *ngFor="let column of content.visibleColumns"
(click)="onRowClicked(row)">
{{ getCellValue(row, column) }}
<span *ngIf="column.type !== 'Boolean' else checkbox">
{{ getCellValue(row, column) }}
</span>
<ng-template #checkbox>
<mat-checkbox disabled [checked]="getCellValue(row, column)">
</mat-checkbox>
</ng-template>
</td>
</tr>
</tbody>
@@ -0,0 +1,20 @@
<div>
<mat-form-field class="adf-date-editor">
<label [attr.for]="column.id">{{column.name}} {{DATE_FORMAT}}</label>
<input matInput
[matDatetimepicker]="datetimePicker"
[(ngModel)]="value"
[id]="column.id"
[required]="column.required"
[disabled]="!column.editable"
(focusout)="onDateChanged($event.srcElement.value)"
(dateChange)="onDateChanged($event)">
<mat-datetimepicker-toggle matSuffix [for]="datetimePicker"
*ngIf="column.editable"
class="adf-date-editor-button"></mat-datetimepicker-toggle>
</mat-form-field>
<mat-datetimepicker #datetimePicker
type="datetime"
openOnFocus="true"
timeInterval="5"></mat-datetimepicker>
</div>
@@ -0,0 +1,10 @@
.adf {
&-date-editor {
width: 100%;
}
&-date-editor-button {
position: relative;
top: 25px;
}
}
@@ -0,0 +1,89 @@
/*!
* @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 } from '@angular/core/testing';
import moment from 'moment-es6';
import { FormFieldModel, FormModel } from '../../../index';
import { DynamicTableColumn } from './../../dynamic-table-column.model';
import { DynamicTableRow } from './../../dynamic-table-row.model';
import { DynamicTableModel } from './../../dynamic-table.widget.model';
import { DateTimeEditorComponent } from './datetime.editor';
import { setupTestBed } from '../../../../../../testing/setupTestBed';
import { CoreModule } from '../../../../../../core.module';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
describe('DateTimeEditorComponent', () => {
let component: DateTimeEditorComponent;
let fixture: ComponentFixture<DateTimeEditorComponent>;
let row: DynamicTableRow;
let column: DynamicTableColumn;
let table: DynamicTableModel;
setupTestBed({
imports: [
NoopAnimationsModule,
CoreModule.forRoot()
]
});
beforeEach(() => {
fixture = TestBed.createComponent(DateTimeEditorComponent);
component = fixture.componentInstance;
row = <DynamicTableRow> { value: { date: '1879-03-14T00:00:00.000Z' } };
column = <DynamicTableColumn> { id: 'datetime', type: 'Datetime' };
const field = new FormFieldModel(new FormModel());
table = new DynamicTableModel(field, null);
table.rows.push(row);
table.columns.push(column);
component.table = table;
component.row = row;
component.column = column;
});
it('should create instance of DateTimeEditorComponent', () => {
expect(fixture.componentInstance instanceof DateTimeEditorComponent).toBe(true, 'should create DateTimeEditorComponent');
});
it('should update fow value on change', () => {
component.ngOnInit();
let newDate = moment('22-6-2018 04:20 AM', 'D-M-YYYY hh:mm A');
component.onDateChanged(newDate);
expect(moment(row.value[column.id]).isSame(newDate)).toBeTruthy();
});
it('should update row value upon user input', () => {
const input = '22-6-2018 04:20 AM';
component.ngOnInit();
component.onDateChanged(input);
let actual = row.value[column.id];
expect(actual).toBe('22-6-2018 04:20 AM');
});
it('should flush value on user input', () => {
spyOn(table, 'flushValue').and.callThrough();
const input = '22-6-2018 04:20 AM';
component.ngOnInit();
component.onDateChanged(input);
expect(table.flushValue).toHaveBeenCalled();
});
});
@@ -0,0 +1,92 @@
/*!
* @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.
*/
/* tslint:disable:component-selector */
import { UserPreferencesService } from '../../../../../../services/user-preferences.service';
import { MomentDateAdapter } from '../../../../../../utils/momentDateAdapter';
import { MOMENT_DATE_FORMATS } from '../../../../../../utils/moment-date-formats.model';
import { Component, Input, OnInit } from '@angular/core';
import { DateAdapter, MAT_DATE_FORMATS } from '@angular/material';
import moment from 'moment-es6';
import { Moment } from 'moment';
import { DynamicTableColumn } from './../../dynamic-table-column.model';
import { DynamicTableRow } from './../../dynamic-table-row.model';
import { DynamicTableModel } from './../../dynamic-table.widget.model';
import { DatetimeAdapter, MAT_DATETIME_FORMATS } from '@mat-datetimepicker/core';
import { MomentDatetimeAdapter, MAT_MOMENT_DATETIME_FORMATS } from '@mat-datetimepicker/moment';
@Component({
selector: 'adf-datetime-editor',
templateUrl: './datetime.editor.html',
providers: [
{ provide: DateAdapter, useClass: MomentDateAdapter },
{ provide: MAT_DATE_FORMATS, useValue: MOMENT_DATE_FORMATS },
{ provide: DatetimeAdapter, useClass: MomentDatetimeAdapter },
{ provide: MAT_DATETIME_FORMATS, useValue: MAT_MOMENT_DATETIME_FORMATS }
],
styleUrls: ['./datetime.editor.scss']
})
export class DateTimeEditorComponent implements OnInit {
DATE_FORMAT: string = 'D-M-YYYY hh:mm A';
value: any;
@Input()
table: DynamicTableModel;
@Input()
row: DynamicTableRow;
@Input()
column: DynamicTableColumn;
minDate: Moment;
maxDate: Moment;
constructor(private dateAdapter: DateAdapter<Moment>,
private preferences: UserPreferencesService) {
}
ngOnInit() {
this.preferences.locale$.subscribe((locale) => {
this.dateAdapter.setLocale(locale);
});
let momentDateAdapter = <MomentDateAdapter> this.dateAdapter;
momentDateAdapter.overrideDisplyaFormat = this.DATE_FORMAT;
this.value = moment(this.table.getCellValue(this.row, this.column), this.DATE_FORMAT);
}
onDateChanged(newDateValue) {
if (newDateValue && newDateValue.value) {
const newValue = moment(newDateValue.value, this.DATE_FORMAT);
this.row.value[this.column.id] = newDateValue.value.format(this.DATE_FORMAT);
this.value = newValue;
this.table.flushValue();
} else if (newDateValue) {
const newValue = moment(newDateValue, this.DATE_FORMAT);
this.value = newValue;
this.row.value[this.column.id] = newDateValue;
this.table.flushValue();
} else {
this.row.value[this.column.id] = '';
}
}
}
@@ -16,7 +16,13 @@
[column]="column">
</adf-date-editor>
</div>
<div *ngSwitchCase="'Datetime'">
<adf-datetime-editor
[table]="table"
[row]="row"
[column]="column">
</adf-datetime-editor>
</div>
<div *ngSwitchCase="'Boolean'">
<adf-boolean-editor
[table]="table"
+4 -1
View File
@@ -28,6 +28,7 @@ import { DropdownWidgetComponent } from './dropdown/dropdown.widget';
import { DynamicTableWidgetComponent } from './dynamic-table/dynamic-table.widget';
import { BooleanEditorComponent } from './dynamic-table/editors/boolean/boolean.editor';
import { DateEditorComponent } from './dynamic-table/editors/date/date.editor';
import { DateTimeEditorComponent } from './dynamic-table/editors/datetime/datetime.editor';
import { DropdownEditorComponent } from './dynamic-table/editors/dropdown/dropdown.editor';
import { RowEditorComponent } from './dynamic-table/editors/row.editor';
import { TextEditorComponent } from './dynamic-table/editors/text/text.editor';
@@ -80,6 +81,7 @@ export * from './dynamic-table/editors/date/date.editor';
export * from './dynamic-table/editors/dropdown/dropdown.editor';
export * from './dynamic-table/editors/boolean/boolean.editor';
export * from './dynamic-table/editors/text/text.editor';
export * from './dynamic-table/editors/datetime/datetime.editor';
export * from './text/text-mask.component';
export const WIDGET_DIRECTIVES: any[] = [
@@ -108,7 +110,8 @@ export const WIDGET_DIRECTIVES: any[] = [
RowEditorComponent,
ErrorWidgetComponent,
DocumentWidgetComponent,
DateTimeWidgetComponent
DateTimeWidgetComponent,
DateTimeEditorComponent
];
export const MASK_DIRECTIVE: any[] = [
+7 -1
View File
@@ -133,11 +133,17 @@
"APPLY": "APPLY",
"NOT_VALID": "http(s)://host|ip:port(/path) not recognized, try a different URL.",
"REDIRECT": "Redirect Uri",
"REDIRECT_LOGOUT": "Redirect Uri Logout",
"SILENT": "Silent Login",
"SCOPE": "Scope",
"CLIENT": "ClientId"
},
"CARDVIEW": {
"KEYVALUEPAIRS": {
"ADD": "Add new",
"NAME": "Name",
"VALUE": "Value"
},
"VALIDATORS": {
"FLOAT_VALIDATION_ERROR": "Use a number format",
"INT_VALIDATION_ERROR": "Use an integer format"
@@ -301,7 +307,7 @@
"DESCRIPTION": "You're not allowed access to this resource on the server.",
"SECONDARY_BUTTON": {
"TEXT": "Report issue",
"URL": ""
"URL": "report-issue"
},
"RETURN_BUTTON": {
"TEXT": "Back to home",
@@ -61,11 +61,11 @@
data-automation-id="password"
tabindex="2">
<mat-icon *ngIf="isPasswordShow" matSuffix class="adf-login-password-icon"
data-automation-id="hide_password" (click)="toggleShowPassword()">
data-automation-id="hide_password" (click)="toggleShowPassword()" (keyup.enter)="toggleShowPassword()" tabindex="3">
visibility
</mat-icon>
<mat-icon *ngIf="!isPasswordShow" matSuffix class="adf-login-password-icon"
data-automation-id="show_password" (click)="toggleShowPassword()">
data-automation-id="show_password" (click)="toggleShowPassword()" (keyup.enter)="toggleShowPassword()" tabindex="3">
visibility_off
</mat-icon>
</mat-form-field>
@@ -79,7 +79,7 @@
<ng-content></ng-content>
<br>
<button type="submit" id="login-button" tabindex="3"
<button type="submit" id="login-button" tabindex="4"
class="adf-login-button"
mat-raised-button color="primary"
[class.isChecking]="actualLoginStep === LoginSteps.Checking"
+2
View File
@@ -27,6 +27,7 @@ export class UserProcessModel implements LightUserRepresentation {
firstName?: string;
lastName?: string;
pictureId?: number = null;
externalId?: string;
constructor(obj?: any) {
if (obj) {
@@ -35,6 +36,7 @@ export class UserProcessModel implements LightUserRepresentation {
this.firstName = obj.firstName || null;
this.lastName = obj.lastName || null;
this.pictureId = obj.pictureId || null;
this.externalId = obj.externalId || null;
}
}
+2 -2
View File
@@ -1,7 +1,7 @@
{
"name": "@alfresco/adf-core",
"description": "Alfresco ADF core",
"version": "2.4.0-beta14",
"version": "2.4.0",
"author": "Alfresco Software, Ltd.",
"main": "bundles/adf-core.js",
"repository": {
@@ -26,7 +26,7 @@
"@angular/platform-browser-dynamic": "5.1.1",
"@angular/router": "5.1.1",
"@ngx-translate/core": "9.1.1",
"alfresco-js-api": "2.4.0-beta14",
"alfresco-js-api": "2.4.0",
"chart.js": "2.5.0",
"core-js": "2.4.1",
"hammerjs": "2.0.8",
@@ -22,6 +22,7 @@ import { CookieService } from './cookie.service';
import { AppConfigService } from '../app-config/app-config.service';
import { setupTestBed } from '../testing/setupTestBed';
import { CoreTestingModule } from '../testing/core.testing.module';
import { UserRepresentation } from 'alfresco-js-api';
declare let jasmine: any;
@@ -311,7 +312,7 @@ describe('AuthenticationService', () => {
},
(err: any) => {
expect(err).toBeDefined();
expect(authService.getTicketBpm()).toBe(null);
expect(authService.getTicketBpm()).toBe(undefined);
done();
});
@@ -349,6 +350,18 @@ describe('AuthenticationService', () => {
it('[BPM] should return isALLProvider false', () => {
expect(authService.isALLProvider()).toBe(false);
});
it('[BPM] should be able to retrieve current logged in user', (done) => {
spyOn(apiService.getInstance().activiti.profileApi, 'getProfile').and.returnValue(
Promise.resolve((<UserRepresentation> {
email: 'fake-email'
})));
authService.getBpmLoggedUser().subscribe((fakeUser) => {
expect(fakeUser.email).toBe('fake-email');
done();
});
});
});
describe('when the setting is both ECM and BPM ', () => {
@@ -26,6 +26,7 @@ import { AppConfigService, AppConfigValues } from '../app-config/app-config.serv
import 'rxjs/add/observable/fromPromise';
import 'rxjs/add/operator/catch';
import 'rxjs/add/observable/throw';
import { UserRepresentation } from 'alfresco-js-api';
const REMEMBER_ME_COOKIE_KEY = 'ALFRESCO_REMEMBER_ME';
const REMEMBER_ME_UNTIL = 1000 * 60 * 60 * 24 * 30 ;
@@ -228,6 +229,10 @@ export class AuthenticationService {
return this.hasValidRedirection(provider) ? this.redirectUrl.url : null;
}
getBpmLoggedUser(): Observable<UserRepresentation> {
return Observable.fromPromise(this.alfrescoApi.getInstance().activiti.profileApi.getProfile());
}
private hasValidRedirection(provider: string): boolean {
return this.redirectUrl && (this.redirectUrl.provider === provider || this.hasSelectedProviderAll(provider));
}
@@ -106,6 +106,12 @@
{{ 'CORE.HOST_SETTINGS.REQUIRED'| translate }}
</mat-error>
</mat-form-field>
<mat-form-field class="full-width" floatLabel="Redirect Uri Logout">
<mat-label>{{ 'CORE.HOST_SETTINGS.REDIRECT_LOGOUT'| translate }}</mat-label>
<input matInput placeholder="{{ 'CORE.HOST_SETTINGS.REDIRECT_LOGOUT'| translate }}"
name="redirectUriLogout" formControlName="redirectUriLogout">
</mat-form-field>
</div>
</ng-container>
<mat-card-actions class="adf-actions">
@@ -140,6 +140,7 @@ export class HostSettingsComponent implements OnInit {
host: [oauth.host, [Validators.required, Validators.pattern(this.HOST_REGEX)]],
clientId: [oauth.clientId, Validators.required],
redirectUri: [oauth.redirectUri, Validators.required],
redirectUriLogout: [oauth.redirectUriLogout],
scope: [oauth.scope, Validators.required],
secret: oauth.secret,
silentLogin: oauth.silentLogin,
@@ -250,6 +251,10 @@ export class HostSettingsComponent implements OnInit {
return this.oauthConfig.get('redirectUri');
}
get redirectUriLogout(): AbstractControl {
return this.oauthConfig.get('redirectUriLogout');
}
get oauthConfig(): AbstractControl {
return this.form.get('oauthConfig');
}
+3 -3
View File
@@ -1,7 +1,7 @@
{
"name": "@alfresco/adf-insights",
"description": "Alfresco ADF insights",
"version": "2.4.0-beta14",
"version": "2.4.0",
"author": "Alfresco Software, Ltd.",
"main": "bundles/adf-insights.js",
"repository": {
@@ -26,8 +26,8 @@
"@angular/platform-browser-dynamic": "5.1.1",
"@angular/router": "5.1.1",
"@ngx-translate/core": "9.1.1",
"alfresco-js-api": "2.4.0-beta14",
"@alfresco/adf-core": "2.4.0-beta14",
"alfresco-js-api": "2.4.0",
"@alfresco/adf-core": "2.4.0",
"chart.js": "2.5.0",
"core-js": "2.4.1",
"hammerjs": "2.0.8",
+3
View File
@@ -152,6 +152,9 @@
"NO_FORM_MESSAGE": "No forms attached",
"COMPLETE_TASK_MESSAGE": "Task {{taskName}} completed",
"COMPLETE_TASK_SUB_MESSAGE": "No forms to be added"
},
"ATTACH_FORM":{
"SELECT_FORM": "Select Form To Attach"
}
},
"ADF_PROCESS_LIST": {
+4 -4
View File
@@ -1,7 +1,7 @@
{
"name": "@alfresco/adf-process-services",
"description": "Alfresco ADF process services",
"version": "2.4.0-beta14",
"version": "2.4.0",
"author": "Alfresco Software, Ltd.",
"main": "bundles/adf-process-services.js",
"repository": {
@@ -12,8 +12,8 @@
"url": "https://github.com/Alfresco/alfresco-ng2-components/issues"
},
"dependencies": {
"@alfresco/adf-core": "2.4.0-beta14",
"@alfresco/adf-content-services": "2.4.0-beta14",
"@alfresco/adf-core": "2.4.0",
"@alfresco/adf-content-services": "2.4.0",
"@angular/animations": "5.1.1",
"@angular/cdk": "5.0.1",
"@angular/common": "5.1.1",
@@ -28,7 +28,7 @@
"@angular/platform-browser-dynamic": "5.1.1",
"@angular/router": "5.1.1",
"@ngx-translate/core": "9.1.1",
"alfresco-js-api": "2.4.0-beta14",
"alfresco-js-api": "2.4.0",
"chart.js": "2.5.0",
"core-js": "2.4.1",
"hammerjs": "2.0.8",
@@ -0,0 +1,30 @@
<div class="adf-attach-form">
<mat-card>
<mat-card-content>
<div class="adf-no-form-message-container">
<mat-card-title class="mat-card-title">
<h4 class="adf-form-title">{{ 'ADF_TASK_LIST.ATTACH_FORM.SELECT_FORM' | translate }}</h4>
</mat-card-title>
<div class="adf-attach-form-row">
<mat-form-field class="adf-grid-full-width">
<mat-select id="form_id" [(ngModel)]="formKey">
<mat-option *ngFor="let form of forms" [value]="form.id">{{ form.name }}</mat-option>
</mat-select>
</mat-form-field>
</div>
<adf-form
[formId]="formKey"
[readOnly]="true"
[disableCompleteButton]="true"
[showRefreshButton]="false">
</adf-form>
</div>
</mat-card-content>
<mat-card-actions class="adf-no-form-mat-card-actions">
<button mat-button id="adf-no-form-cancel-button" (click)="onCancelButtonClick()">{{ 'ADF_TASK_LIST.START_TASK.FORM.ACTION.CANCEL' | translate }}</button>
<button mat-button id="adf-no-form-attach-form-button" color="primary" (click)="onAttachFormButtonClick()">{{ 'ADF_TASK_LIST.START_TASK.FORM.LABEL.ATTACHFORM' | translate }}</button>
</mat-card-actions>
</mat-card>
</div>
@@ -0,0 +1,17 @@
.adf-attach-form {
.mat-form-field {
width: 100%;
}
&-row {
display: flex;
justify-content: space-between;
margin: 20px 0;
}
.adf-no-form-mat-card-actions {
justify-content: flex-end;
margin-top: 30px;
text-align: right;
}
}
@@ -0,0 +1,78 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { AttachFormComponent } from './attach-form.component';
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { setupTestBed } from '@alfresco/adf-core';
import { ProcessTestingModule } from '../../testing/process.testing.module';
import { TaskListService } from './../services/tasklist.service';
import { Observable } from 'rxjs/Observable';
describe('AttachFormComponent', () => {
let component: AttachFormComponent;
let fixture: ComponentFixture<AttachFormComponent>;
let element: HTMLElement;
let taskService: TaskListService;
setupTestBed({
imports: [
ProcessTestingModule
]
});
beforeEach(async(() => {
fixture = TestBed.createComponent(AttachFormComponent);
component = fixture.componentInstance;
element = fixture.nativeElement;
taskService = TestBed.get(TaskListService);
fixture.detectChanges();
}));
it('should emit cancel event if clicked on Cancel Button ', async(() => {
fixture.detectChanges();
fixture.whenStable().then(() => {
const emitSpy = spyOn(component.cancelAttachForm, 'emit');
const el = fixture.nativeElement.querySelector('#adf-no-form-cancel-button');
el.click();
expect(emitSpy).toHaveBeenCalled();
});
}));
it('should call attachFormToATask if clicked on Complete Button', async(() => {
component.taskId = 1;
component.formKey = 2;
spyOn(taskService, 'attachFormToATask').and.returnValue(Observable.of(true));
fixture.detectChanges();
fixture.whenStable().then(() => {
expect(element.querySelector('#adf-no-form-attach-form-button')).toBeDefined();
const el = fixture.nativeElement.querySelector('#adf-no-form-attach-form-button');
el.click();
expect(taskService.attachFormToATask).toHaveBeenCalledWith(1, 2);
});
}));
it('should show the adf-form of the selected form', async(() => {
component.taskId = 1;
component.formKey = 12;
fixture.detectChanges();
const formContainer = fixture.debugElement.nativeElement.querySelector('adf-form');
fixture.whenStable().then(() => {
expect(formContainer).toBeDefined();
expect(formContainer).not.toBeNull();
});
}));
});
@@ -0,0 +1,87 @@
/*!
* @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 { LogService } from '@alfresco/adf-core';
import { Component, EventEmitter, Input, OnInit, OnChanges, Output } from '@angular/core';
import { Form } from '../models/form.model';
import { TaskListService } from './../services/tasklist.service';
@Component({
selector: 'adf-attach-form',
templateUrl: './attach-form.component.html',
styleUrls: ['./attach-form.component.scss']
})
export class AttachFormComponent implements OnInit, OnChanges {
constructor(private taskService: TaskListService,
private logService: LogService) { }
/** The id of the task whose details we are asking for. */
@Input()
taskId;
/** Emitted when the "Cancel" button is clicked. */
@Output()
cancelAttachForm: EventEmitter<void> = new EventEmitter<void>();
/** Emitted when the form associated with the form task is attached. */
@Output()
completeAttachForm: EventEmitter<void> = new EventEmitter<void>();
/** Emitted when an error occurs. */
@Output()
error: EventEmitter<any> = new EventEmitter<any>();
forms: Form[];
formKey: number;
ngOnInit() {
this.loadFormsTask();
}
ngOnChanges() {
this.loadFormsTask();
}
onCancelButtonClick(): void {
this.cancelAttachForm.emit();
}
onAttachFormButtonClick(): void {
this.attachForm(this.taskId, this.formKey);
}
private loadFormsTask(): void {
this.taskService.getFormList().subscribe((res: Form[]) => {
this.forms = res;
},
(err) => {
this.error.emit(err);
this.logService.error('An error occurred while trying to get the forms');
});
}
private attachForm(taskId: string, formKey: number) {
if (taskId && formKey) {
this.taskService.attachFormToATask(taskId, formKey)
.subscribe((res) => {
this.completeAttachForm.emit();
});
}
}
}
@@ -17,15 +17,19 @@
import { NoTaskDetailsTemplateDirective } from './no-task-detail-template.directive';
import { TaskDetailsComponent } from './task-details.component';
import { FormRenderingService } from '@alfresco/adf-core';
import { FormRenderingService, AuthenticationService } from '@alfresco/adf-core';
import { Observable } from 'rxjs/Observable';
describe('NoTaskDetailsTemplateDirective', () => {
let component: NoTaskDetailsTemplateDirective;
let detailsComponent: TaskDetailsComponent;
let authService: AuthenticationService;
beforeEach(() => {
detailsComponent = new TaskDetailsComponent(null, null, null, new FormRenderingService(), null, null, null);
authService = new AuthenticationService(null, null, null, null);
spyOn(authService, 'getBpmLoggedUser').and.returnValue(Observable.of({ email: 'fake-email'}));
detailsComponent = new TaskDetailsComponent(null, authService, null, new FormRenderingService(), null, null, null);
component = new NoTaskDetailsTemplateDirective(detailsComponent);
});
@@ -39,12 +39,14 @@
(error)='onFormError($event)'
(executeOutcome)='onFormExecuteOutcome($event)'>
</adf-form>
<adf-task-standalone *ngIf="!hasFormKey()"
[taskName]= "taskDetails.name"
[isCompleted]="isCompletedTask()"
[hasCompletePermission]="isCompleteButtonEnabled()"
[hideCancelButton]="true"
(complete)="onComplete()">
<adf-task-standalone *ngIf="!hasFormKey()"
[taskName]= "taskDetails.name"
[taskId]= "taskDetails.id"
[isCompleted]="isCompletedTask()"
[hasCompletePermission]="isCompleteButtonEnabled()"
[hideCancelButton]="true"
(complete)="onComplete()"
(formAttached)="onFormAttached()">
</adf-task-standalone>
</div>
<div *ngIf="!isAssigned()" id="claim-message-id">
@@ -74,10 +74,12 @@ adf-task-header.assign-edit-view ::ng-deep adf-card-view ::ng-deep .adf-property
flex-grow: 1;
& ::ng-deep .adf-form-debug-container {
padding: 20px 0 0 0;
display: flex;
flex-direction: column;
padding: 20px 0;
.mat-slide-toggle {
float: right;
margin-left: auto;
& + div {
background-color: black;
@@ -21,7 +21,7 @@ import { By } from '@angular/platform-browser';
import { Observable } from 'rxjs/Observable';
import { FormModel, FormOutcomeEvent, FormOutcomeModel, FormService, setupTestBed, BpmUserService } from '@alfresco/adf-core';
import { CommentProcessService, LogService } from '@alfresco/adf-core';
import { CommentProcessService, LogService, AuthenticationService } from '@alfresco/adf-core';
import { UserProcessModel } from '@alfresco/adf-core';
import { TaskDetailsModel } from '../models/task-details.model';
@@ -51,6 +51,7 @@ describe('TaskDetailsComponent', () => {
let logService: LogService;
let commentProcessService: CommentProcessService;
let peopleProcessService: PeopleProcessService;
let authService: AuthenticationService;
setupTestBed({
imports: [
@@ -80,6 +81,9 @@ describe('TaskDetailsComponent', () => {
completeTaskSpy = spyOn(service, 'completeTask').and.returnValue(Observable.of({}));
commentProcessService = TestBed.get(CommentProcessService);
authService = TestBed.get(AuthenticationService);
spyOn(authService, 'getBpmLoggedUser').and.returnValue(Observable.of({ email: 'fake-email'}));
spyOn(commentProcessService, 'getTaskComments').and.returnValue(Observable.of([
{message: 'Test1', created: Date.now(), createdBy: {firstName: 'Admin', lastName: 'User'}},
{message: 'Test2', created: Date.now(), createdBy: {firstName: 'Admin', lastName: 'User'}},
@@ -156,9 +160,12 @@ describe('TaskDetailsComponent', () => {
it('should not display task standalone component when the task have an associated form', async(() => {
component.taskId = '123';
component.taskDetails = new TaskDetailsModel(taskDetailsMock);
component.taskDetails.formKey = '10';
fixture.detectChanges();
fixture.whenStable().then(() => {
fixture.detectChanges();
expect(fixture.debugElement.query(By.css('adf-task-standalone'))).toBeDefined();
expect(fixture.debugElement.query(By.css('adf-task-standalone'))).not.toBeNull();
});
}));
@@ -21,6 +21,7 @@ import {
CardViewUpdateService,
ClickNotification,
LogService,
FormService,
UpdateNotification,
FormRenderingService,
CommentsComponent
@@ -44,6 +45,7 @@ import { TaskQueryRequestRepresentationModel } from '../models/filter.model';
import { TaskDetailsModel } from '../models/task-details.model';
import { TaskListService } from './../services/tasklist.service';
import { AttachFileWidgetComponent, AttachFolderWidgetComponent } from '../../content-widget';
import { UserRepresentation } from 'alfresco-js-api';
@Component({
selector: 'adf-task-details',
@@ -179,10 +181,13 @@ export class TaskDetailsComponent implements OnInit, OnChanges {
peopleSearch: Observable<UserProcessModel[]>;
currentLoggedUser: UserRepresentation;
constructor(private taskListService: TaskListService,
private authService: AuthenticationService,
private peopleProcessService: PeopleProcessService,
private formRenderingService: FormRenderingService,
private formService: FormService,
private logService: LogService,
private cardViewUpdateService: CardViewUpdateService,
private dialog: MatDialog) {
@@ -190,6 +195,9 @@ export class TaskDetailsComponent implements OnInit, OnChanges {
this.formRenderingService.setComponentTypeResolver('select-folder', () => AttachFolderWidgetComponent, true);
this.formRenderingService.setComponentTypeResolver('upload', () => AttachFileWidgetComponent, true);
this.peopleSearch = new Observable<UserProcessModel[]>(observer => this.peopleSearchObserver = observer).share();
this.authService.getBpmLoggedUser().subscribe((user: UserRepresentation) => {
this.currentLoggedUser = user;
});
}
ngOnInit() {
@@ -285,8 +293,22 @@ export class TaskDetailsComponent implements OnInit, OnChanges {
return this.taskDetails.assignee ? true : false;
}
private hasEmailAddress(): boolean {
return this.taskDetails.assignee.email ? true : false;
}
isAssignedToMe(): boolean {
return this.isAssigned() ? this.taskDetails.assignee.email === this.authService.getBpmUsername() : false;
return this.isAssigned() && this.hasEmailAddress() ?
this.isEmailEqual(this.taskDetails.assignee.email, this.currentLoggedUser.email) :
this.isExternalIdEqual(this.taskDetails.assignee.externalId, this.currentLoggedUser.externalId);
}
private isEmailEqual(assigneeMail, currentLoggedEmail): boolean {
return assigneeMail.toLocaleLowerCase() === currentLoggedEmail.toLocaleLowerCase();
}
private isExternalIdEqual(assigneeExternalId, currentUserExternalId): boolean {
return assigneeExternalId.toLocaleLowerCase() === currentUserExternalId.toLocaleLowerCase();
}
isCompleteButtonEnabled(): boolean {
@@ -342,6 +364,13 @@ export class TaskDetailsComponent implements OnInit, OnChanges {
);
}
onFormAttached() {
this.formService.getTaskForm(this.taskId)
.subscribe((res) => {
this.loadDetails(this.taskId);
}, error => this.logService.error('Could not load forms'));
}
onFormContentClick(content: ContentLinkModel): void {
this.formContentClicked.emit(content);
}
@@ -1,4 +1,4 @@
<mat-card class="adf-message-card">
<mat-card class="adf-message-card" *ngIf="!showAttachForm">
<mat-card-content>
<div class="adf-no-form-message-container">
<div class="adf-no-form-message-list">
@@ -18,7 +18,16 @@
</mat-card-content>
<mat-card-actions class="adf-no-form-mat-card-actions">
<button mat-button *ngIf="hasCancelButton()" id="adf-no-form-cancel-button" (click)="onCancelButtonClick()">{{ 'ADF_TASK_LIST.START_TASK.FORM.ACTION.CANCEL' | translate }}</button>
<button mat-button *ngIf="hasCompleteButton()" id="adf-no-form-complete-button" color="primary" (click)="onCompleteButtonClick()">{{ 'ADF_TASK_LIST.DETAILS.BUTTON.COMPLETE' | translate }}</button>
<button mat-button *ngIf="hasAttachFormButton()" id="adf-no-form-attach-form-button" (click)="onShowAttachForm()">{{ 'ADF_TASK_LIST.START_TASK.FORM.LABEL.ATTACHFORM' | translate }}</button>
<div>
<button mat-button *ngIf="hasCancelButton()" id="adf-no-form-cancel-button" (click)="onCancelButtonClick()">{{ 'ADF_TASK_LIST.START_TASK.FORM.ACTION.CANCEL' | translate }}</button>
<button mat-button *ngIf="hasCompleteButton()" id="adf-no-form-complete-button" color="primary" (click)="onCompleteButtonClick()">{{ 'ADF_TASK_LIST.DETAILS.BUTTON.COMPLETE' | translate }}</button>
</div>
</mat-card-actions>
</mat-card>
</mat-card>
<adf-attach-form *ngIf="showAttachForm"
[taskId]="taskId"
(cancelAttachForm)="onCancelAttachForm()"
(completeAttachForm)="onCompleteAttachForm()">
</adf-attach-form>
@@ -2,7 +2,7 @@
$config: mat-typography-config();
$background: map-get($theme, background);
.adf {
&-message-card {
width: 60%;
@@ -13,7 +13,7 @@
}
}
&-no-form-message-container {
height:256px;
height: 256px;
width: 100%;
display: table;
}
@@ -37,8 +37,9 @@
margin: auto;
width: fit-content !important;
}
&-no-form-mat-card-actions {
text-align: right;
&-no-form-mat-card-actions.mat-card-actions {
display: flex;
justify-content: space-between;
& .mat-button {
text-transform: uppercase;
border-radius: 5px;
@@ -50,4 +51,4 @@
}
}
}
}
}
@@ -28,7 +28,11 @@ export class TaskStandaloneComponent {
/** Name of the task. */
@Input()
taskName: string;
taskName;
/** Id of the task. */
@Input()
taskId;
/** If true then Task completed message is shown and `Complete` and `Cancel` buttons are hidden. */
@Input()
@@ -50,6 +54,11 @@ export class TaskStandaloneComponent {
@Output()
complete: EventEmitter<void> = new EventEmitter<void>();
@Output()
formAttached: EventEmitter<void> = new EventEmitter<void>();
showAttachForm: boolean = false;
constructor() { }
onCancelButtonClick(): void {
@@ -67,4 +76,21 @@ export class TaskStandaloneComponent {
hasCancelButton(): boolean {
return !this.hideCancelButton && !this.isCompleted;
}
hasAttachFormButton(): boolean {
return !this.isCompleted;
}
onShowAttachForm() {
this.showAttachForm = true;
}
onCancelAttachForm() {
this.showAttachForm = false;
}
onCompleteAttachForm() {
this.showAttachForm = false;
this.formAttached.emit();
}
}
@@ -24,6 +24,7 @@ export * from './components/task-details.component';
export * from './components/task-audit.directive';
export * from './components/start-task.component';
export * from './components/task-standalone.component';
export * from './components/attach-form.component';
export * from './services/tasklist.service';
export * from './services/process-upload.service';
@@ -40,6 +40,7 @@ import { TaskFiltersComponent } from './components/task-filters.component';
import { TaskHeaderComponent } from './components/task-header.component';
import { TaskListComponent } from './components/task-list.component';
import { TaskStandaloneComponent } from './components/task-standalone.component';
import { AttachFormComponent } from './components/attach-form.component';
@NgModule({
imports: [
@@ -63,7 +64,8 @@ import { TaskStandaloneComponent } from './components/task-standalone.component'
ChecklistComponent,
TaskHeaderComponent,
StartTaskComponent,
TaskStandaloneComponent
TaskStandaloneComponent,
AttachFormComponent
],
providers: [
TaskListService,
@@ -80,7 +82,8 @@ import { TaskStandaloneComponent } from './components/task-standalone.component'
ChecklistComponent,
TaskHeaderComponent,
StartTaskComponent,
TaskStandaloneComponent
TaskStandaloneComponent,
AttachFormComponent
]
})
export class TaskListModule {
+5783 -5797
View File
File diff suppressed because it is too large Load Diff
+6 -6
View File
@@ -1,7 +1,7 @@
{
"name": "alfresco-components",
"description": "Alfresco Angular components",
"version": "2.4.0-beta14",
"version": "2.4.0",
"author": "Alfresco Software, Ltd.",
"main": "./index.js",
"scripts": {
@@ -55,10 +55,10 @@
"process services"
],
"dependencies": {
"@alfresco/adf-content-services": "2.4.0-beta14",
"@alfresco/adf-core": "2.4.0-beta14",
"@alfresco/adf-insights": "2.4.0-beta14",
"@alfresco/adf-process-services": "2.4.0-beta14",
"@alfresco/adf-content-services": "2.4.0",
"@alfresco/adf-core": "2.4.0",
"@alfresco/adf-insights": "2.4.0",
"@alfresco/adf-process-services": "2.4.0",
"@angular/animations": "5.1.1",
"@angular/cdk": "5.0.1",
"@angular/common": "5.1.1",
@@ -76,7 +76,7 @@
"@mat-datetimepicker/moment": "1.0.4",
"@ngx-translate/core": "9.1.1",
"@nrwl/schematics": "^1.0.3",
"alfresco-js-api": "2.4.0-beta14",
"alfresco-js-api": "2.4.0",
"chart.js": "2.5.0",
"classlist.js": "1.1.20150312",
"core-js": "2.4.1",
+54 -56
View File
@@ -18,6 +18,7 @@ var configFileName = "doctool.config.json";
var defaultFolder = path.resolve("docs");
/*
function initPhase(aggData) {
toolList.forEach(toolName => {
toolModules[toolName].initPhase(aggData);
@@ -25,19 +26,10 @@ function initPhase(aggData) {
}
function readPhase(filenames, aggData) {
for (var i = 0; i < filenames.length; i++) {
var pathname = filenames[i];//path.resolve(srcFolder, filenames[i]);
var src = fs.readFileSync(pathname);
var tree = remark().use(frontMatter, ["yaml"]).parse(src);
toolList.forEach(toolName => {
toolModules[toolName].readPhase(tree, pathname, aggData);
});
}
//console.log(JSON.stringify(aggData.mdFileData));
function readPhase(mdCache, aggData) {
toolList.forEach(toolName => {
toolModules[toolName].readPhase(mdCache, aggData);
});
}
@@ -46,36 +38,25 @@ function aggPhase(aggData) {
toolModules[toolName].aggPhase(aggData);
});
}
*/
function updatePhase(filenames, aggData) {
function updatePhase(mdCache, aggData) {
var errorMessages;
for (var i = 0; i < filenames.length; i++) {
toolList.forEach(toolName => {
errorMessages = [];
console.log(`Tool: ${toolName}`);
toolModules[toolName].processDocs(mdCache, aggData, errorMessages);
});
var filenames = Object.keys(mdCache);
for (var i = 0; i < filenames.length; i++) {
var pathname = filenames[i];
if (program.verbose) {
console.log("Reading " + pathname);
}
var src = fs.readFileSync(pathname);
var tree = remark().use(frontMatter, ["yaml"]).parse(src);
var tree = mdCache[pathname].mdOutTree;
var original = mdCache[pathname].mdInTree;
var original = minimiseTree(tree);
var modified = false;
toolList.forEach(toolName => {
modified |= toolModules[toolName].updatePhase(tree, pathname, aggData, errorMessages);
});
if (errorMessages.length > 0) {
showErrors(pathname, errorMessages);
}
tree = minimiseTree(tree);
if (program.json) {
let filename = path.basename(pathname);
@@ -84,10 +65,8 @@ function updatePhase(filenames, aggData) {
console.log(`\nFile "${filename}" after processing:`);
console.log(JSON.stringify(tree));
}
modified = !lodash.isEqual(tree, original);
if (modified) {
if (!lodash.isEqual(tree, original)) {
if (program.verbose) {
console.log(`Modified: ${pathname}`);
}
@@ -113,17 +92,6 @@ function minimiseTree(tree) {
}
function showErrors(filename, errorMessages) {
console.log(filename);
errorMessages.forEach(message => {
console.log(" " + message);
});
console.log("");
}
function loadToolModules() {
var mods = {};
var toolsFolderPath = path.resolve(__dirname, toolsFolderName);
@@ -164,13 +132,37 @@ function getAllDocFilePaths(docFolder, files) {
}
function initMdCache(filenames) {
var mdCache = {};
for (var i = 0; i < filenames.length; i++) {
var pathname = filenames[i];
mdCache[pathname] = {};
var src = fs.readFileSync(pathname);
var tree = remark().use(frontMatter, ["yaml"]).parse(src);
mdCache[pathname].mdInTree = minimiseTree(tree);
mdCache[pathname].mdOutTree = minimiseTree(tree);
}
return mdCache;
}
program
.usage("[options] <source>")
.option("-p, --profile [profileName]", "Select named config profile", "default")
.option("-j, --json", "Output JSON data for Markdown syntax tree")
.option("-v, --verbose", "Log doc files as they are processed")
.option("-t, --timing", "Output time taken for run")
.parse(process.argv);
var startTime;
if (program.timing) {
startTime = process.hrtime();
}
var sourcePath;
if (program.args.length === 0) {
@@ -206,23 +198,29 @@ if (sourceInfo.isDirectory()) {
}
files = files.filter(filename =>
(filename !== undefined) &&
(path.extname(filename) === ".md") &&
(filename !== "README.md")
);
//files.forEach(element => console.log(element));
var mdCache = initMdCache(files);
/*
console.log("Initialising...");
initPhase(aggData);
console.log("Analysing Markdown files...");
readPhase(files, aggData);
readPhase(mdCache, aggData);
console.log("Computing aggregate data...");
aggPhase(aggData);
*/
console.log("Updating Markdown files...");
updatePhase(files, aggData);
updatePhase(mdCache, aggData);
if (program.timing) {
var endTime = process.hrtime(startTime);
console.log(`Run complete in ${endTime[0]} sec`);
}
+2 -1
View File
@@ -19,7 +19,8 @@
"toc"
],
"dev": [
"toc"
"tsInfo",
"typeLinker"
]
},
"statusIcons": {
+21 -11
View File
@@ -12,10 +12,7 @@ var searchLibraryRecursive = require("../libsearch");
var mdNav = require("../mdNav");
module.exports = {
"initPhase": initPhase,
"readPhase": readPhase,
"aggPhase": aggPhase,
"updatePhase": updatePhase
"processDocs": processDocs
}
var angFilenameRegex = /([a-zA-Z0-9\-]+)\.((component)|(directive)|(model)|(pipe)|(service)|(widget))\.ts/;
@@ -34,6 +31,14 @@ var adfLibNames = ["core", "content-services", "insights", "process-services"];
var statusIcons;
function processDocs(mdCache, aggData, _errorMessages) {
initPhase(aggData);
readPhase(mdCache, aggData);
aggPhase(aggData);
}
function initPhase(aggData) {
statusIcons = aggData.config["statusIcons"] || {};
aggData.stoplist = makeStoplist(aggData.config);
@@ -45,7 +50,16 @@ function initPhase(aggData) {
}
function readPhase(tree, pathname, aggData) {
function readPhase(mdCache, aggData) {
var pathnames = Object.keys(mdCache);
pathnames.forEach(pathname => {
getFileData(mdCache[pathname].mdInTree, pathname, aggData);
});
}
function getFileData(tree, pathname, aggData) {
var itemName = path.basename(pathname, ".md");
// Look for the first paragraph in the file by skipping other items.
@@ -149,11 +163,6 @@ function aggPhase(aggData) {
}
function updatePhase(tree, pathname, aggData) {
return false;
}
// Create a stoplist of regular expressions.
function makeStoplist(config) {
var listExpressions = config.undocStoplist;
@@ -281,7 +290,7 @@ function makeMDDocumentedTableRow(docItem, forSubFolder) {
}
var srcFileLink = unist.makeLink(unist.makeText("Source"), srcPath);
var desc = docItem.briefDesc;
var desc = JSON.parse(JSON.stringify(docItem.briefDesc));
removeBriefDescLinks(desc);
@@ -401,5 +410,6 @@ function removeBriefDescLinks(desc) {
links.forEach(link => {
link.item.type = "text";
link.item.value = link.item.children[0].value;
link.item.children = null;
});
}
+15 -8
View File
@@ -17,15 +17,18 @@ const maxTocHeadingDepth = 3;
var templateFolder = path.resolve("tools", "doc", "templates");
module.exports = {
"initPhase": initPhase,
"readPhase": readPhase,
"aggPhase": aggPhase,
"updatePhase": updatePhase
"processDocs": processDocs
}
function processDocs(mdCache, aggData, errorMessages) {
var pathnames = Object.keys(mdCache);
pathnames.forEach(pathname => {
updateFile(mdCache[pathname].mdOutTree, pathname, aggData, errorMessages);
});
}
function initPhase(aggData) {}
function readPhase(tree, pathname, aggData) {}
function aggPhase(aggData) {}
// Find an existing Contents section or add a new empty one if needed.
@@ -80,7 +83,11 @@ function establishContentsSection(mdTree) {
return numTocHeadings;
}
function updatePhase(tree, pathname, aggData) {
function updateFile(tree, pathname, _aggData, _errorMessages) {
if (path.basename(pathname, ".md").match(/README|versionIndex/)) {
return false;
}
+21 -9
View File
@@ -18,6 +18,26 @@ var nameExceptions;
var undocMethodNames = {
"ngOnChanges": 1
};
function processDocs(mdCache, aggData, _errorMessages) {
initPhase(aggData);
var pathnames = Object.keys(mdCache);
var internalErrors;
pathnames.forEach(function (pathname) {
internalErrors = [];
updateFile(mdCache[pathname].mdOutTree, pathname, aggData, internalErrors);
if (internalErrors.length > 0) {
showErrors(pathname, internalErrors);
}
});
}
exports.processDocs = processDocs;
function showErrors(filename, errorMessages) {
console.log(filename);
errorMessages.forEach(function (message) {
console.log(" " + message);
});
console.log("");
}
var PropInfo = /** @class */ (function () {
function PropInfo(rawProp) {
var _this = this;
@@ -189,14 +209,7 @@ function initPhase(aggData) {
}));
aggData.projData = app.convert(sources);
}
exports.initPhase = initPhase;
function readPhase(tree, pathname, aggData) {
}
exports.readPhase = readPhase;
function aggPhase(aggData) {
}
exports.aggPhase = aggPhase;
function updatePhase(tree, pathname, aggData, errorMessages) {
function updateFile(tree, pathname, aggData, errorMessages) {
var compName = angNameToClassName(path.basename(pathname, ".md"));
var classRef = aggData.projData.findReflectionByName(compName);
if (!classRef) {
@@ -232,7 +245,6 @@ function updatePhase(tree, pathname, aggData, errorMessages) {
}
return true;
}
exports.updatePhase = updatePhase;
function initialCap(str) {
return str[0].toUpperCase() + str.substr(1);
}
+29 -8
View File
@@ -40,6 +40,33 @@ let undocMethodNames = {
};
export function processDocs(mdCache, aggData, _errorMessages) {
initPhase(aggData);
let pathnames = Object.keys(mdCache);
let internalErrors;
pathnames.forEach(pathname => {
internalErrors = [];
updateFile(mdCache[pathname].mdOutTree, pathname, aggData, internalErrors);
if (internalErrors.length > 0) {
showErrors(pathname, internalErrors);
}
});
}
function showErrors(filename, errorMessages) {
console.log(filename);
errorMessages.forEach(message => {
console.log(" " + message);
});
console.log("");
}
class PropInfo {
name: string;
type: string;
@@ -258,7 +285,7 @@ class ComponentInfo {
}
export function initPhase(aggData) {
function initPhase(aggData) {
nameExceptions = aggData.config.typeNameExceptions;
let app = new Application({
@@ -276,15 +303,9 @@ export function initPhase(aggData) {
}
export function readPhase(tree, pathname, aggData) {
}
export function aggPhase(aggData) {
}
export function updatePhase(tree, pathname, aggData, errorMessages) {
function updateFile(tree, pathname, aggData, errorMessages) {
let compName = angNameToClassName(path.basename(pathname, ".md"));
let classRef = aggData.projData.findReflectionByName(compName);
+4 -9
View File
@@ -11,10 +11,10 @@ var unist = require("../unistHelpers");
var tutFolder = path.resolve("docs", "tutorials");
var templateFolder = path.resolve("tools", "doc", "templates");
var userGuideFolder = path.resolve("docs", "user-guide");
function initPhase(aggData) { }
exports.initPhase = initPhase;
function readPhase(tree, pathname, aggData) { }
exports.readPhase = readPhase;
function processDocs(tree, pathname, aggData, errorMessages) {
aggPhase(aggData);
}
exports.processDocs = processDocs;
function aggPhase(aggData) {
var indexDocData = getIndexDocData();
var templateName = path.resolve(templateFolder, "tutIndex.ejs");
@@ -33,11 +33,6 @@ function aggPhase(aggData) {
});
fs.writeFileSync(tutIndexFile, remark().use(frontMatter, { type: 'yaml', fence: '---' }).data("settings", { paddedTable: false, gfm: false }).stringify(tutIndexMD));
}
exports.aggPhase = aggPhase;
function updatePhase(tree, pathname, aggData) {
return false;
}
exports.updatePhase = updatePhase;
function getIndexDocData() {
var indexFile = path.resolve(userGuideFolder, "summary.json");
var summaryArray = JSON.parse(fs.readFileSync(indexFile, "utf8"));
+4 -8
View File
@@ -15,12 +15,12 @@ const templateFolder = path.resolve("tools", "doc", "templates");
const userGuideFolder = path.resolve("docs", "user-guide");
export function initPhase(aggData) {}
export function readPhase(tree, pathname, aggData) {}
export function processDocs(tree, pathname, aggData, errorMessages) {
aggPhase(aggData);
}
export function aggPhase(aggData) {
function aggPhase(aggData) {
let indexDocData = getIndexDocData();
let templateName = path.resolve(templateFolder, "tutIndex.ejs");
@@ -46,10 +46,6 @@ export function aggPhase(aggData) {
}
export function updatePhase(tree, pathname, aggData) {
return false;
}
function getIndexDocData() {
let indexFile = path.resolve(userGuideFolder, "summary.json");
+17 -8
View File
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
var path = require("path");
var fs = require("fs");
var typedoc_1 = require("typedoc");
var ProgressBar = require("progress");
var unist = require("../unistHelpers");
var ngHelpers = require("../ngHelpers");
var includedNodeTypes = [
@@ -13,6 +14,21 @@ var includedNodeTypes = [
var docFolder = path.resolve("docs");
var adfLibNames = ["core", "content-services", "insights", "process-services"];
var externalNameLinks;
function processDocs(mdCache, aggData, errorMessages) {
initPhase(aggData);
var pathnames = Object.keys(mdCache);
var progress = new ProgressBar("Processing: [:bar] (:current/:total)", {
total: pathnames.length,
width: 50,
clear: true
});
pathnames.forEach(function (pathname) {
updateFile(mdCache[pathname].mdOutTree, pathname, aggData, errorMessages);
progress.tick();
progress.render();
});
}
exports.processDocs = processDocs;
function initPhase(aggData) {
externalNameLinks = aggData.config.externalNameLinks;
aggData.docFiles = {};
@@ -36,13 +52,7 @@ function initPhase(aggData) {
});
//console.log(JSON.stringify(aggData.nameLookup));
}
exports.initPhase = initPhase;
function readPhase(tree, pathname, aggData) { }
exports.readPhase = readPhase;
function aggPhase(aggData) {
}
exports.aggPhase = aggPhase;
function updatePhase(tree, pathname, aggData) {
function updateFile(tree, pathname, aggData, errorMessages) {
traverseMDTree(tree);
return true;
function traverseMDTree(node) {
@@ -86,7 +96,6 @@ function updatePhase(tree, pathname, aggData) {
*/
}
}
exports.updatePhase = updatePhase;
var SplitNameNode = /** @class */ (function () {
function SplitNameNode(key, value) {
if (key === void 0) { key = ""; }
+23 -7
View File
@@ -18,6 +18,8 @@ import {
} from "typedoc";
import { CommentTag } from "typedoc/dist/lib/models";
import * as ProgressBar from "progress";
import * as unist from "../unistHelpers";
import * as ngHelpers from "../ngHelpers";
import { match } from "minimatch";
@@ -34,7 +36,26 @@ const adfLibNames = ["core", "content-services", "insights", "process-services"]
let externalNameLinks;
export function initPhase(aggData) {
export function processDocs(mdCache, aggData, errorMessages) {
initPhase(aggData);
var pathnames = Object.keys(mdCache);
let progress = new ProgressBar("Processing: [:bar] (:current/:total)", {
total: pathnames.length,
width: 50,
clear: true
});
pathnames.forEach(pathname => {
updateFile(mdCache[pathname].mdOutTree, pathname, aggData, errorMessages);
progress.tick();
progress.render();
});
}
function initPhase(aggData) {
externalNameLinks = aggData.config.externalNameLinks;
aggData.docFiles = {};
aggData.nameLookup = new SplitNameLookup();
@@ -64,15 +85,10 @@ export function initPhase(aggData) {
//console.log(JSON.stringify(aggData.nameLookup));
}
export function readPhase(tree, pathname, aggData) {}
export function aggPhase(aggData) {
}
export function updatePhase(tree, pathname, aggData) {
function updateFile(tree, pathname, aggData, errorMessages) {
traverseMDTree(tree);
return true;
+19 -10
View File
@@ -14,10 +14,7 @@ var ngHelpers = require("../ngHelpers");
module.exports = {
"initPhase": initPhase,
"readPhase": readPhase,
"aggPhase": aggPhase,
"updatePhase": updatePhase
"processDocs": processDocs
}
var angFilenameRegex = /([a-zA-Z0-9\-]+)\.((component)|(directive)|(model)|(pipe)|(service)|(widget))\.ts/;
@@ -30,12 +27,29 @@ var initialVersion = "v2.0.0";
var templateFolder = path.resolve("tools", "doc", "templates");
function processDocs(mdCache, aggData, errorMessages) {
initPhase(aggData);
readPhase(mdCache, aggData);
aggPhase(aggData);
}
function initPhase(aggData) {
aggData.versions = { "v2.0.0":[] };
}
function readPhase(tree, pathname, aggData) {
function readPhase(mdCache, aggData) {
var pathnames = Object.keys(mdCache);
pathnames.forEach(pathname => {
getFileData(mdCache[pathname].mdInTree, pathname, aggData);
});
}
function getFileData(tree, pathname, aggData) {
var compName = pathname;
var angNameRegex = /([a-zA-Z0-9\-]+)\.((component)|(directive)|(model)|(pipe)|(service)|(widget))/;
@@ -121,8 +135,3 @@ function aggPhase(aggData) {
fs.writeFileSync(histFilePath, remark().use(frontMatter, {type: 'yaml', fence: '---'}).data("settings", {paddedTable: false, gfm: false}).stringify(histFileTree));
}
function updatePhase(tree, pathname, aggData) {
return false;
}