diff --git a/ng2-components/ng2-alfresco-datatable/src/components/datatable.component.ts b/ng2-components/ng2-alfresco-datatable/src/components/datatable.component.ts index 2ef163b267..7e9c38a56d 100644 --- a/ng2-components/ng2-alfresco-datatable/src/components/datatable.component.ts +++ b/ng2-components/ng2-alfresco-datatable/src/components/datatable.component.ts @@ -29,7 +29,8 @@ import { DataTableAdapter, DataRow, DataColumn, - DataSorting + DataSorting, + DataRowEvent } from './../data/datatable-adapter'; import { ObjectDataTableAdapter } from '../data/object-datatable-adapter'; @@ -54,10 +55,10 @@ export class DataTableComponent implements OnInit, AfterViewChecked { actions: boolean = false; @Output() - rowClick: EventEmitter = new EventEmitter(); + rowClick: EventEmitter = new EventEmitter(); @Output() - rowDblClick: EventEmitter = new EventEmitter(); + rowDblClick: EventEmitter = new EventEmitter(); isSelectAllChecked: boolean = false; @@ -84,7 +85,8 @@ export class DataTableComponent implements OnInit, AfterViewChecked { } this.rowClick.emit({ - value: row + value: row, + event: e }); } @@ -94,7 +96,8 @@ export class DataTableComponent implements OnInit, AfterViewChecked { } this.rowDblClick.emit({ - value: row + value: row, + event: e }); } @@ -134,14 +137,16 @@ export class DataTableComponent implements OnInit, AfterViewChecked { isIconValue(row: DataRow, col: DataColumn) { if (row && col) { - return row.getValue(col.key).startsWith('material-icons://'); + let value = row.getValue(col.key); + return value && value.startsWith('material-icons://'); } return false; } asIconValue(row: DataRow, col: DataColumn) { if (this.isIconValue(row, col)) { - return row.getValue(col.key).replace('material-icons://', ''); + let value = row.getValue(col.key) || ''; + return value.replace('material-icons://', ''); } return null; } diff --git a/ng2-components/ng2-alfresco-datatable/src/data/datatable-adapter.ts b/ng2-components/ng2-alfresco-datatable/src/data/datatable-adapter.ts index 9ba0a0c716..e6b2cb07b2 100644 --- a/ng2-components/ng2-alfresco-datatable/src/data/datatable-adapter.ts +++ b/ng2-components/ng2-alfresco-datatable/src/data/datatable-adapter.ts @@ -16,7 +16,6 @@ */ export interface DataTableAdapter { - getRows(): Array; setRows(rows: Array): void; getColumns(): Array; @@ -25,19 +24,15 @@ export interface DataTableAdapter { getSorting(): DataSorting; setSorting(sorting: DataSorting): void; sort(key?: string, direction?: string): void; - } export interface DataRow { - isSelected: boolean; hasValue(key: string): boolean; getValue(key: string): any; - } export interface DataColumn { - key: string; type: string; // text|image|date format?: string; @@ -45,14 +40,16 @@ export interface DataColumn { title?: string; srTitle?: string; cssClass?: string; - } export class DataSorting { - constructor( public key?: string, public direction?: string) { } - +} + +export interface DataRowEvent { + value?: DataRow; + event: Event; } diff --git a/ng2-components/ng2-alfresco-documentlist/demo/src/main.ts b/ng2-components/ng2-alfresco-documentlist/demo/src/main.ts index 0902e6a751..8b84975ca5 100644 --- a/ng2-components/ng2-alfresco-documentlist/demo/src/main.ts +++ b/ng2-components/ng2-alfresco-documentlist/demo/src/main.ts @@ -156,7 +156,8 @@ class DocumentListDemo implements OnInit { currentPath: string = '/'; authenticated: boolean; - public host: string = 'http://devproducts-platform.alfresco.me'; + // host: string = 'http://devproducts-platform.alfresco.me'; + host: string = 'http://127.0.0.1:8080'; token: string; diff --git a/ng2-components/ng2-alfresco-documentlist/demo/systemjs.config.js b/ng2-components/ng2-alfresco-documentlist/demo/systemjs.config.js index 13a0e340b1..4831e6d740 100644 --- a/ng2-components/ng2-alfresco-documentlist/demo/systemjs.config.js +++ b/ng2-components/ng2-alfresco-documentlist/demo/systemjs.config.js @@ -12,6 +12,7 @@ 'ng2-translate': 'node_modules/ng2-translate', 'ng2-alfresco-core': 'node_modules/ng2-alfresco-core/dist', + 'ng2-alfresco-datatable': 'node_modules/ng2-alfresco-datatable/dist', 'ng2-alfresco-documentlist': 'node_modules/ng2-alfresco-documentlist/dist' }; // packages tells the System loader how to load when no filename and/or no extension @@ -22,6 +23,7 @@ 'ng2-translate': { defaultExtension: 'js' }, 'ng2-alfresco-core': { main: 'index.js', defaultExtension: 'js' }, + 'ng2-alfresco-datatable': { main: 'index.js', defaultExtension: 'js' }, 'ng2-alfresco-documentlist': { main: 'index.js', defaultExtension: 'js' } }; var ngPackageNames = [ diff --git a/ng2-components/ng2-alfresco-documentlist/src/components/document-list.html b/ng2-components/ng2-alfresco-documentlist/src/components/document-list.html index a3cc0693ed..0a2e6fc70c 100644 --- a/ng2-components/ng2-alfresco-documentlist/src/components/document-list.html +++ b/ng2-components/ng2-alfresco-documentlist/src/components/document-list.html @@ -1,3 +1,8 @@ + + diff --git a/ng2-components/ng2-alfresco-documentlist/src/components/document-list.ts b/ng2-components/ng2-alfresco-documentlist/src/components/document-list.ts index f9f9d53a13..d70a1f43a5 100644 --- a/ng2-components/ng2-alfresco-documentlist/src/components/document-list.ts +++ b/ng2-components/ng2-alfresco-documentlist/src/components/document-list.ts @@ -30,11 +30,19 @@ import { import { DatePipe } from '@angular/common'; import { Subject } from 'rxjs/Rx'; import { CONTEXT_MENU_DIRECTIVES } from 'ng2-alfresco-core'; + +import { + ALFRESCO_DATATABLE_DIRECTIVES, + DataSorting, + DataRowEvent +} from 'ng2-alfresco-datatable'; + import { AlfrescoService } from './../services/alfresco.service'; import { MinimalNodeEntity, NodePaging } from './../models/document-library.model'; import { ContentActionModel } from './../models/content-action.model'; import { ContentColumnModel } from './../models/content-column.model'; import { ColumnSortingModel } from './../models/column-sorting.model'; +import { ShareDataTableAdapter, ShareDataRow } from './../data/share-datatable-adapter'; declare var componentHandler; declare let __moduleName: string; @@ -45,7 +53,7 @@ declare let __moduleName: string; styleUrls: ['./document-list.css'], templateUrl: './document-list.html', providers: [AlfrescoService], - directives: [CONTEXT_MENU_DIRECTIVES], + directives: [CONTEXT_MENU_DIRECTIVES, ALFRESCO_DATATABLE_DIRECTIVES], host: { '(contextmenu)': 'onShowContextMenu($event)' } @@ -124,9 +132,13 @@ export class DocumentList implements OnInit, AfterViewChecked, AfterContentInit, contextActionHandler: Subject = new Subject(); + data: ShareDataTableAdapter; + constructor( private alfrescoService: AlfrescoService, - private ngZone: NgZone) {} + private ngZone: NgZone) { + this.setupTable(); + } getContextActions(node: MinimalNodeEntity) { if (node && node.entry) { @@ -326,6 +338,7 @@ export class DocumentList implements OnInit, AfterViewChecked, AfterContentInit, folder => this.folder = this.sort(folder, this.sorting), error => this.errorMessage = error ); + this.data.loadPath(path); } } @@ -451,6 +464,42 @@ export class DocumentList implements OnInit, AfterViewChecked, AfterContentInit, return node; } + onRowClick(event: DataRowEvent) { + let item = ( event.value).node; + + if (this.navigate && this.navigationMode === DocumentList.SINGLE_CLICK_NAVIGATION) { + if (item && item.entry) { + if (item.entry.isFile) { + this.preview.emit({ + value: item + }); + } + + if (item.entry.isFolder) { + this.performNavigation(item); + } + } + } + + } + + onRowDblClick(event?: DataRowEvent) { + let item = ( event.value).node; + if (this.navigate && this.navigationMode === DocumentList.DOUBLE_CLICK_NAVIGATION) { + if (item && item.entry) { + if (item.entry.isFile) { + this.preview.emit({ + value: item + }); + } + + if (item.entry.isFolder) { + this.performNavigation(item); + } + } + } + } + private getObjectValueRaw(target: any, key: string) { let val = this.getObjectValue(target, key); @@ -468,4 +517,19 @@ export class DocumentList implements OnInit, AfterViewChecked, AfterContentInit, private isSortableColumn(column: ContentColumnModel) { return column && column.source && !column.source.startsWith('$'); } + + private setupTable() { + this.data = new ShareDataTableAdapter( + this.alfrescoService, + this.baseComponentPath, + [ + { type: 'image', key: '$thumbnail', title: '', srTitle: 'Thumbnail' }, + { type: 'text', key: 'name', title: 'Name', cssClass: 'full-width', sortable: true }, + { type: 'text', key: 'createdByUser.displayName', title: 'Created by', sortable: true }, + { type: 'date', format: 'medium', key: 'createdAt', title: 'Created on', sortable: true } + ] + ); + + this.data.setSorting(new DataSorting('id', 'asc')); + } } diff --git a/ng2-components/ng2-alfresco-documentlist/src/data/share-datatable-adapter.ts b/ng2-components/ng2-alfresco-documentlist/src/data/share-datatable-adapter.ts new file mode 100644 index 0000000000..0b0e3babe6 --- /dev/null +++ b/ng2-components/ng2-alfresco-documentlist/src/data/share-datatable-adapter.ts @@ -0,0 +1,228 @@ +/*! + * @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 { DatePipe } from '@angular/common'; +import { + DataTableAdapter, + DataRow, DataColumn, DataSorting, + ObjectDataColumn +} from 'ng2-alfresco-datatable'; + +import { NodePaging, MinimalNodeEntity } from './../models/document-library.model'; +import { AlfrescoService as DataService } from './../services/alfresco.service'; + + +export class ShareDataTableAdapter implements DataTableAdapter { + + private sorting: DataSorting; + private rows: DataRow[]; + private columns: DataColumn[]; + + constructor(private dataService: DataService, + private basePath: string, + schema: DataColumn[]) { + this.rows = []; + this.columns = []; + + if (schema && schema.length > 0) { + this.columns = schema.map(item => { + return new ObjectDataColumn(item); + }); + } + } + + getRows(): Array { + return this.rows; + } + + setRows(rows: Array) { + this.rows = rows || []; + this.sort(); + } + + getColumns(): Array { + return this.columns; + } + + setColumns(columns: Array) { + this.columns = columns || []; + } + + getValue(row: DataRow, col: DataColumn): any { + if (!row) { + throw new Error('Row not found'); + } + if (!col) { + throw new Error('Column not found'); + } + let value = row.getValue(col.key); + + if (col.type === 'date') { + let datePipe = new DatePipe(); + let format = col.format || 'medium'; + try { + return datePipe.transform(value, format); + } catch (err) { + console.error(`DocumentList: error parsing date ${value} to format ${format}`); + } + } + + if (col.type === 'image') { + + if (col.key === '$thumbnail') { + let isFolder = row.getValue('isFolder'); + if (isFolder) { + return `${this.basePath}/img/ft_ic_folder.svg`; + } + + let isFile = row.getValue('isFile'); + if (isFile) { + let mimeType = row.getValue('content.mimeType'); + if (mimeType) { + let icon = this.dataService.getMimeTypeIcon(mimeType); + if (icon) { + return `${this.basePath}/img/${icon}`; + } + } + } + + return `${this.basePath}/img/ft_ic_miscellaneous.svg`; + } + + } + + return value; + } + + getSorting(): DataSorting { + return this.sorting; + } + + setSorting(sorting: DataSorting): void { + this.sorting = sorting; + + if (sorting && sorting.key) { + this.rows.sort((a: DataRow, b: DataRow) => { + let left = a.getValue(sorting.key); + if (left) { + left = (left instanceof Date) ? left.valueOf().toString() : left.toString(); + } else { + left = ''; + } + + let right = b.getValue(sorting.key); + if (right) { + right = (right instanceof Date) ? right.valueOf().toString() : right.toString(); + } else { + right = ''; + } + + return sorting.direction === 'asc' + ? left.localeCompare(right) + : right.localeCompare(left); + }); + } + } + + sort(key?: string, direction?: string): void { + let sorting = this.sorting || new DataSorting(); + if (key) { + sorting.key = key; + sorting.direction = direction || 'asc'; + } + this.setSorting(sorting); + } + + loadPath(path: string) { + if (path && this.dataService) { + this.dataService + .getFolder(path) + .subscribe(val => { + let page = val; + let data = page.list.entries; + if (data && data.length > 0) { + this.rows = data.map(item => new ShareDataRow(item)); + // Sort by first sortable or just first column + let sortable = this.columns.filter(c => c.sortable); + if (sortable.length > 0) { + this.sort(sortable[0].key, 'asc'); + } else { + this.sort(this.columns[0].key, 'asc'); + } + } else { + this.rows = []; + } + }, + error => console.log(error)); + } + } + +} + +export class ShareDataRow implements DataRow { + isSelected: boolean = false; + + get node(): MinimalNodeEntity { + return this.obj; + } + + constructor(private obj: MinimalNodeEntity) { + if (!obj) { + throw new Error('Object source not found'); + } + } + + /** + * Gets a value from an object by composed key + * documentList.getObjectValue({ item: { nodeType: 'cm:folder' }}, 'item.nodeType') ==> 'cm:folder' + * @param target + * @param key + * @returns {string} + */ + getObjectValue(target: any, key: string): any { + + if (!target) { + return undefined; + } + + let keys = key.split('.'); + key = ''; + + do { + key += keys.shift(); + let value = target[key]; + if (value !== undefined && (typeof value === 'object' || !keys.length)) { + target = value; + key = ''; + } else if (!keys.length) { + target = undefined; + } else { + key += '.'; + } + } while (keys.length); + + return target; + } + + getValue(key: string): any { + return this.getObjectValue(this.obj.entry, key); + } + + hasValue(key: string): boolean { + return this.getValue(key) ? true : false; + } +}