#1014 use document list to display search results (#1548)

* #1014 use document list to diplay search results

* #1014 refactor pagination

* #1014 documentation and scripts update

* fix random erros on tests executrion

* #1014 fix travis scripts and raise timeout jasmine

* #1014 fix appveyor script

* #1014 type nodeId
This commit is contained in:
Mario Romano
2017-01-27 12:22:25 +00:00
committed by Denys Vuika
parent 9e00b1d4f1
commit b05247dade
52 changed files with 917 additions and 414 deletions

View File

@@ -38,14 +38,21 @@ before_script:
(cd ng2-components/ng2-alfresco-datatable; npm link ng2-alfresco-core; if [ ! -d "bundles" ]; then npm link; fi);
fi
- if ([ "$MODULE" == "ng2-activiti-tasklist" ] || [ "$MODULE" == "ng2-activiti-processlist" ]); then
(cd ng2-components/ng2-alfresco-datatable; npm link ng2-alfresco-core; if [ ! -d "bundles" ]; then npm link; fi);
(cd ng2-components/ng2-activiti-form; npm link ng2-alfresco-core; if [ ! -d "bundles" ]; then npm link; fi);
fi
- if ([ "$MODULE" == "ng2-activiti-processlist" ]); then
(cd ng2-components/ng2-alfresco-datatable; npm link ng2-alfresco-core; if [ ! -d "bundles" ]; then npm link; fi);
(cd ng2-components/ng2-activiti-form; npm link ng2-alfresco-core; if [ ! -d "bundles" ]; then npm link; fi);
(cd ng2-components/ng2-activiti-tasklist; npm link ng2-alfresco-core; npm link ng2-alfresco-datatable; npm link ng2-activiti-form; if [ ! -d "bundles" ]; then npm link; fi);
fi
- if ([ "$MODULE" == "ng2-activiti-analytics" ]); then
(cd ng2-components/ng2-activiti-diagrams; npm link ng2-alfresco-core; if [ ! -d "bundles" ]; then npm link; fi);
fi
- if ([ "$MODULE" == "ng2-alfresco-search" ]); then
(cd ng2-components/ng2-alfresco-datatable; npm link ng2-alfresco-core; if [ ! -d "bundles" ]; then npm link; fi);
(cd ng2-components/ng2-alfresco-documentlist; npm link ng2-alfresco-core; npm link ng2-alfresco-datatable; if [ ! -d "bundles" ]; then npm link; fi);
fi
- cd ng2-components/$MODULE;
- npm run travis;
#npm insall run install, build and tests

View File

@@ -30,22 +30,19 @@ install:
# Get the latest stable version of Node.js or io.js
- ps: Install-Product node $env:nodejs_version
# install module
- IF %COMPONENT_NAME% NEQ ng2-alfresco-core (cd ng2-components/ng2-alfresco-core && npm install && npm link && cd ../../)
- IF %COMPONENT_NAME% EQU ng2-alfresco-documentlist (cd ng2-components/ng2-alfresco-datatable && npm link ng2-alfresco-core && npm install && npm link && cd ../../)
- IF %COMPONENT_NAME% EQU ng2-activiti-processlist (cd ng2-components/ng2-alfresco-datatable && npm link ng2-alfresco-core && npm install && npm link && cd ../../)
- IF %COMPONENT_NAME% EQU ng2-activiti-processlist (cd ng2-components/ng2-activiti-form && npm link ng2-alfresco-core && npm install && npm link && cd ../../)
- IF %COMPONENT_NAME% EQU ng2-activiti-processlist (cd ng2-components/ng2-activiti-tasklist && npm link ng2-alfresco-core && npm link ng2-alfresco-datatable && npm link ng2-activiti-form && npm install && npm link && cd ../../)
- IF %COMPONENT_NAME% EQU ng2-activiti-tasklist (cd ng2-components/ng2-alfresco-datatable && npm link ng2-alfresco-core && npm install && npm link && cd ../../)
- IF %COMPONENT_NAME% EQU ng2-activiti-tasklist (cd ng2-components/ng2-activiti-form && npm link ng2-alfresco-core && npm install && npm link && cd ../../)
- IF %COMPONENT_NAME% EQU ng2-alfresco-webscript (cd ng2-components/ng2-alfresco-datatable && npm link ng2-alfresco-core && npm install && npm link && cd ../../)
- IF %COMPONENT_NAME% EQU ng2-activiti-analytics (cd ng2-components/ng2-activiti-diagrams && npm link link ng2-alfresco-core && npm install && npm link && cd ../../)
- IF %COMPONENT_NAME% NEQ ng2-alfresco-core (cd ng2-components/ng2-alfresco-core && npm link && cd ../../)
- IF %COMPONENT_NAME% EQU ng2-alfresco-documentlist (cd ng2-components/ng2-alfresco-datatable && npm link ng2-alfresco-core && npm link && cd ../../)
- IF %COMPONENT_NAME% EQU ng2-alfresco-search (cd ng2-components/ng2-alfresco-datatable && npm link ng2-alfresco-core && npm link && cd ../../)
- IF %COMPONENT_NAME% EQU ng2-alfresco-search (cd ng2-components/ng2-alfresco-documentlist && npm link ng2-alfresco-core && npm link ng2-alfresco-datatable && npm link && cd ../../)
- IF %COMPONENT_NAME% EQU ng2-activiti-processlist (cd ng2-components/ng2-alfresco-datatable && npm link ng2-alfresco-core && npm link && cd ../../)
- IF %COMPONENT_NAME% EQU ng2-activiti-processlist (cd ng2-components/ng2-activiti-form && npm link ng2-alfresco-core && npm link && cd ../../)
- IF %COMPONENT_NAME% EQU ng2-activiti-processlist (cd ng2-components/ng2-activiti-tasklist && npm link ng2-alfresco-core && npm link ng2-alfresco-datatable && npm link ng2-activiti-form && npm link && cd ../../)
- IF %COMPONENT_NAME% EQU ng2-activiti-tasklist (cd ng2-components/ng2-alfresco-datatable && npm link ng2-alfresco-core && npm link && cd ../../)
- IF %COMPONENT_NAME% EQU ng2-activiti-tasklist (cd ng2-components/ng2-activiti-form && npm link ng2-alfresco-core && npm link && cd ../../)
- IF %COMPONENT_NAME% EQU ng2-alfresco-webscript (cd ng2-components/ng2-alfresco-datatable && npm link ng2-alfresco-core && npm link && cd ../../)
- IF %COMPONENT_NAME% EQU ng2-activiti-analytics (cd ng2-components/ng2-activiti-diagrams && npm link link ng2-alfresco-core && npm link && cd ../../)
- cd ng2-components/%COMPONENT_NAME%
- IF %COMPONENT_NAME% NEQ ng2-alfresco-core (npm link ng2-alfresco-core)
- IF %COMPONENT_NAME% EQU ng2-alfresco-documentlist (npm link ng2-alfresco-datatable)
- IF %COMPONENT_NAME% EQU ng2-activiti-processlist (npm link ng2-alfresco-datatable && npm link ng2-activiti-form && npm link ng2-activiti-tasklist)
- IF %COMPONENT_NAME% EQU ng2-activiti-tasklist (npm link ng2-alfresco-datatable && npm link ng2-activiti-form)
- IF %COMPONENT_NAME% EQU ng2-alfresco-webscript (npm link ng2-alfresco-datatable)
- IF %COMPONENT_NAME% EQU ng2-activiti-analytics (npm link ng2-activiti-diagrams)
- npm run travis
# Post-install test scripts.
test_script:

View File

@@ -96,10 +96,6 @@
</content-action>
</content-actions>
</alfresco-document-list>
<alfresco-pagination
[provider]="documentList.data"
[supportedPageSizes]="[5, 10, 15, 20]">
</alfresco-pagination>
</alfresco-upload-drag-area>
</div>

View File

@@ -1,8 +1,12 @@
<div class="search-results-container">
<h1>Search results</h1>
<alfresco-search (navigate)="onNavigateItem($event)"></alfresco-search>
<alfresco-search (preview)="showFile($event)"></alfresco-search>
</div>
<alfresco-viewer *ngIf="fileShowed" [(showViewer)]="fileShowed" [fileNodeId]="fileNodeId" [overlayMode]="true">
<div class="mdl-spinner mdl-js-spinner is-active"></div>
</alfresco-viewer>
<div *ngIf="fileShowed">
<alfresco-viewer [(showViewer)]="fileShowed"
[fileNodeId]="fileNodeId"
[overlayMode]="true">
<div class="mdl-spinner mdl-js-spinner is-active"></div>
</alfresco-viewer>
</div>

View File

@@ -47,18 +47,18 @@ import { MinimalNodeEntity } from 'alfresco-js-api';
})
export class SearchComponent {
fileShowed: boolean = false;
fileNodeId: string;
fileShowed: boolean = false;
constructor(public router: Router) {
}
onNavigateItem(event: MinimalNodeEntity) {
if (event.entry.isFile) {
this.fileNodeId = event.entry.id;
showFile(event) {
if (event.value.entry.isFile) {
this.fileNodeId = event.value.entry.id;
this.fileShowed = true;
} else if (event.entry.isFolder) {
this.router.navigate(['/files', event.entry.id]);
} else {
this.fileShowed = false;
}
}
}

View File

@@ -1,7 +1,14 @@
// Tun on full stack traces in errors to help debugging
Error.stackTraceLimit = Infinity;
// jasmine.DEFAULT_TIMEOUT_INTERVAL = 1000;
jasmine.DEFAULT_TIMEOUT_INTERVAL = 100000;
window.componentHandler = {
upgradeAllRegistered: function () {
},
upgradeElement: function () {
}
};
__karma__.loaded = function() {};

View File

@@ -1,7 +1,14 @@
// Tun on full stack traces in errors to help debugging
Error.stackTraceLimit = Infinity;
// jasmine.DEFAULT_TIMEOUT_INTERVAL = 1000;
jasmine.DEFAULT_TIMEOUT_INTERVAL = 100000;
window.componentHandler = {
upgradeAllRegistered: function () {
},
upgradeElement: function () {
}
};
__karma__.loaded = function() {};

View File

@@ -1,7 +1,14 @@
// Tun on full stack traces in errors to help debugging
Error.stackTraceLimit = Infinity;
// jasmine.DEFAULT_TIMEOUT_INTERVAL = 1000;
jasmine.DEFAULT_TIMEOUT_INTERVAL = 100000;
window.componentHandler = {
upgradeAllRegistered: function () {
},
upgradeElement: function () {
}
};
__karma__.loaded = function() {};

View File

@@ -1,7 +1,14 @@
// Tun on full stack traces in errors to help debugging
Error.stackTraceLimit = Infinity;
// jasmine.DEFAULT_TIMEOUT_INTERVAL = 1000;
jasmine.DEFAULT_TIMEOUT_INTERVAL = 100000;
window.componentHandler = {
upgradeAllRegistered: function () {
},
upgradeElement: function () {
}
};
__karma__.loaded = function() {};

View File

@@ -1,7 +1,14 @@
// Tun on full stack traces in errors to help debugging
Error.stackTraceLimit = Infinity;
// jasmine.DEFAULT_TIMEOUT_INTERVAL = 1000;
jasmine.DEFAULT_TIMEOUT_INTERVAL = 100000;
window.componentHandler = {
upgradeAllRegistered: function () {
},
upgradeElement: function () {
}
};
__karma__.loaded = function() {};

View File

@@ -1,7 +1,14 @@
// Tun on full stack traces in errors to help debugging
Error.stackTraceLimit = Infinity;
// jasmine.DEFAULT_TIMEOUT_INTERVAL = 1000;
jasmine.DEFAULT_TIMEOUT_INTERVAL = 100000;
window.componentHandler = {
upgradeAllRegistered: function () {
},
upgradeElement: function () {
}
};
__karma__.loaded = function() {};

View File

@@ -441,6 +441,31 @@ let schema = ObjectDataTableAdapter.generateSchema(data);
```
# Pagination Component
The pagination object is a generic component to paginate component. The Alfresco API are paginated and returns a Pagination object. You can use the pagination object to feed the pagination component and then listen to the event which return the current pagination and query again the API with the options choose by the user.
![DataTable demo](docs/assets/pagination-demo.png)
### Properties
| Name | Type | Default | Description
| --- | --- | --- | --- |
| `supportedPageSizes` | numer[] | [5, 10, 20, 50, 100] | This array describe the set of options showed in the pick list |
| `maxItems` | boolean | false | Max number of element showed per page. If you pick another size from the pick list this option will be overwritten |
| `pagination` | Pagination | {count: 0, totalItems: 0, skipCount: 0, maxItems: 20 , hasMoreItems: true} | The Alfresco Api return a pagination object, you can use it to feed the pagination component, or create your own. |
### Events
| Name | Description
| --- | --- |
| `changePageSize` | Emitted when user picks one of the options from the pick list |
| `nextPage` | Emitted when user clicks next page button |
| `prevPage` | Emitted when user clicks prev page button |
All the events carry with them the current pagination object.
## Build from sources
Alternatively you can build component from sources with the following commands:

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View File

@@ -1,7 +1,14 @@
// Tun on full stack traces in errors to help debugging
Error.stackTraceLimit = Infinity;
// jasmine.DEFAULT_TIMEOUT_INTERVAL = 1000;
jasmine.DEFAULT_TIMEOUT_INTERVAL = 100000;
window.componentHandler = {
upgradeAllRegistered: function () {
},
upgradeElement: function () {
}
};
__karma__.loaded = function() {};

View File

@@ -15,5 +15,4 @@
* limitations under the License.
*/
export * from './paginationProvider.interface';
export * from './pagination.component';

View File

@@ -1,7 +1,7 @@
<div *ngIf="provider" class="mdl-paging">
<div class="mdl-paging">
<span class="mdl-paging__per-page">
<span class="mdl-paging__per-page-label">Rows per page:</span>
<span class="mdl-paging__per-page-value" [attr.data-automation-id]="'rows_per_page_' + pageSize">{{pageSize}}</span>
<span class="mdl-paging__per-page-value" [attr.data-automation-id]="'rows_per_page_' + pageSize">{{pagination.maxItems}}</span>
<button alfresco-mdl-button id="pageSizePicker" class="mdl-button--icon mdl-paging__per-page-dropdown">
<i class="material-icons">arrow_drop_down</i>
</button>
@@ -15,12 +15,12 @@
</span>
<span class="mdl-paging__count">{{summary}}</span>
<button (click)="showPrevPage()"
[disabled]="!prevPageAvail"
[disabled]="!prevPageAvail()"
alfresco-mdl-button class="mdl-button--icon mdl-paging__prev" [attr.data-automation-id]="prev_page">
<i class="material-icons">keyboard_arrow_left</i>
</button>
<button (click)="showNextPage()"
[disabled]="!nextPageAvail"
[disabled]="!nextPageAvail()"
alfresco-mdl-button class="mdl-button--icon mdl-paging__next" [attr.data-automation-id]="next_page">
<i class="material-icons">keyboard_arrow_right</i>
</button>

View File

@@ -16,17 +16,9 @@
*/
import { PaginationComponent } from '../pagination/pagination.component';
import { PaginationProvider, DataLoadedEventEmitter} from '../pagination/paginationProvider.interface';
import { Injector } from '@angular/core';
import { getTestBed, TestBed} from '@angular/core/testing';
class CustomPaginationProvider implements PaginationProvider {
skipCount: number = 0;
dataLoaded: DataLoadedEventEmitter;
count: number = 200;
hasMoreItems: boolean = false;
maxItems: number = 20;
}
import { PaginationData } from '../../models/pagination.data';
import { Injector, SimpleChange } from '@angular/core';
import { getTestBed, TestBed } from '@angular/core/testing';
describe('PaginationComponent', () => {
let injector: Injector;
@@ -40,7 +32,13 @@ describe('PaginationComponent', () => {
});
injector = getTestBed();
paginationComponent = injector.get(PaginationComponent);
paginationComponent.provider = new CustomPaginationProvider();
paginationComponent.pagination = new PaginationData(0, 0, 0, 20, true);
});
it('should create Pagination object on init if no object pagination is passed', () => {
paginationComponent.pagination = null;
paginationComponent.ngOnInit();
expect(paginationComponent.pagination).not.toBe(null);
});
it('is defined', () => {
@@ -48,35 +46,63 @@ describe('PaginationComponent', () => {
});
it('page size', () => {
expect(paginationComponent.pageSize).toBe(20);
expect(paginationComponent.pagination.maxItems).toBe(20);
});
it('set page size', () => {
paginationComponent.pageSize = 100;
expect(paginationComponent.pageSize).toBe(100);
paginationComponent.pagination.maxItems = 100;
expect(paginationComponent.pagination.maxItems).toBe(100);
});
it('prevPageAvail', () => {
expect(paginationComponent.prevPageAvail).toBe(false);
it('prevPageAvail dafault false', () => {
expect(paginationComponent.prevPageAvail()).toBe(false);
});
it('nextPageAvail', () => {
expect(paginationComponent.nextPageAvail).toBe(false);
it('nextPageAvail default true', () => {
expect(paginationComponent.nextPageAvail()).toBe(true);
});
it('showNextPage', () => {
expect(paginationComponent.provider.skipCount).toBe(0);
expect(paginationComponent.pagination.skipCount).toBe(0);
paginationComponent.showNextPage();
expect(paginationComponent.provider.skipCount).toBe(20);
expect(paginationComponent.pagination.skipCount).toBe(20);
});
it('showPrevPage', () => {
paginationComponent.provider.skipCount = 100;
paginationComponent.pagination.skipCount = 100;
paginationComponent.showPrevPage();
expect(paginationComponent.provider.skipCount).toBe(80);
expect(paginationComponent.pagination.skipCount).toBe(80);
});
it('PaginationProvider', () => {
expect(paginationComponent.provider instanceof CustomPaginationProvider).toBeTruthy();
it('should update the summary on nextpage click', () => {
spyOn(paginationComponent, 'updateSummary');
paginationComponent.showNextPage();
expect(paginationComponent.updateSummary).toHaveBeenCalled();
});
it('should update the summary on prevpage click', () => {
spyOn(paginationComponent, 'updateSummary');
paginationComponent.showPrevPage();
expect(paginationComponent.updateSummary).toHaveBeenCalled();
});
it('should update the summary on chage page size click', () => {
spyOn(paginationComponent, 'updateSummary');
paginationComponent.setPageSize(100);
expect(paginationComponent.updateSummary).toHaveBeenCalled();
});
it('should update the summary on input pagination parameter change', () => {
spyOn(paginationComponent, 'updateSummary');
paginationComponent.ngOnChanges({pagination: new SimpleChange(null, new PaginationData(0, 0, 0, 20, true))});
expect(paginationComponent.updateSummary).toHaveBeenCalled();
});
});

View File

@@ -15,8 +15,9 @@
* limitations under the License.
*/
import { Component, Input, OnInit } from '@angular/core';
import { PaginationProvider } from './paginationProvider.interface';
import { SimpleChanges, OnChanges, EventEmitter, Output, Component, Input, OnInit } from '@angular/core';
import { PaginationData } from '../../models/pagination.data';
import { Pagination } from 'alfresco-js-api';
@Component({
moduleId: module.id,
@@ -24,68 +25,81 @@ import { PaginationProvider } from './paginationProvider.interface';
templateUrl: './pagination.component.html',
styleUrls: ['./pagination.component.css']
})
export class PaginationComponent implements OnInit {
export class PaginationComponent implements OnInit, OnChanges {
DEFAULT_PAGE_SIZE: number = 20;
static DEFAULT_PAGE_SIZE: number = 20;
private _summary: string = '';
private summary: string = '';
@Input()
supportedPageSizes: number[] = [5, 10, 20, 50, 100];
@Input()
provider: PaginationProvider;
maxItems: number = PaginationComponent.DEFAULT_PAGE_SIZE;
get pageSize(): number {
if (this.provider) {
return this.provider.maxItems;
}
return this.DEFAULT_PAGE_SIZE;
@Input()
pagination: Pagination;
@Output()
changePageSize: EventEmitter<Pagination> = new EventEmitter();
@Output()
nextPage: EventEmitter<Pagination> = new EventEmitter();
@Output()
prevPage: EventEmitter<Pagination> = new EventEmitter();
constructor() {
}
set pageSize(value: number) {
if (this.provider) {
this.provider.maxItems = value;
ngOnInit() {
if (!this.pagination) {
this.pagination = new PaginationData(0, 0, 0, this.maxItems, true);
}
}
ngOnChanges(changes: SimpleChanges) {
if (changes['pagination']) {
if (changes['pagination'].currentValue) {
this.pagination = changes['pagination'].currentValue;
this.updateSummary();
}
}
}
setPageSize(value: number) {
this.pageSize = value;
this.pagination.maxItems = value;
this.updateSummary();
this.changePageSize.emit(this.pagination);
}
get summary(): string {
return this._summary;
nextPageAvail(): boolean {
return this.pagination.hasMoreItems;
}
get nextPageAvail(): boolean {
return this.provider.hasMoreItems;
}
get prevPageAvail(): boolean {
return this.provider.skipCount > 0;
prevPageAvail(): boolean {
return this.pagination.skipCount > 0;
}
showNextPage() {
this.provider.skipCount += this.provider.maxItems;
this.pagination.skipCount += this.pagination.maxItems;
this.updateSummary();
this.nextPage.emit(this.pagination);
}
showPrevPage() {
this.provider.skipCount -= this.provider.maxItems;
this.pagination.skipCount -= this.pagination.maxItems;
this.updateSummary();
this.prevPage.emit(this.pagination);
}
ngOnInit() {
this.provider.dataLoaded.subscribe(() => {
this.updateSummary();
});
}
private updateSummary() {
let from = this.provider.skipCount;
updateSummary() {
let from = this.pagination.skipCount;
if (from === 0) {
from = 1;
}
let to = this.provider.skipCount + this.provider.count;
let of = this.provider.totalItems;
this._summary = `${from}-${to} of ${of}`;
let to = this.pagination.skipCount + this.pagination.count;
let of = this.pagination.totalItems;
this.summary = `${from}-${to} of ${of}`;
}
}

View File

@@ -15,9 +15,9 @@
* limitations under the License.
*/
import { Subject } from 'rxjs/Rx';
import { Pagination } from 'alfresco-js-api';
export interface PaginationProvider {
export class PaginationData implements Pagination {
/**
* The number of objects in the collection.
@@ -34,7 +34,7 @@ export interface PaginationProvider {
* An integer describing the total number of entities in the collection.
* The API might not be able to determine this value, in which case this property will not be present.
*/
totalItems?: number;
totalItems: number;
/**
* An integer describing how many entities exist in the collection before those included in this list.
@@ -47,18 +47,11 @@ export interface PaginationProvider {
*/
maxItems: number;
/**
* An event that is emitted every time data is loaded.
*/
dataLoaded: DataLoadedEventEmitter;
}
export class DataLoadedEventEmitter extends Subject<any> {
constructor() {
super();
}
emit(value) {
super.next(value);
constructor(count: number, totalItems: number, skipCount: number, maxItems: number, hasMoreItems: boolean) {
this.count = count;
this.hasMoreItems = hasMoreItems;
this.totalItems = totalItems;
this.skipCount = skipCount;
this.maxItems = maxItems;
}
}

View File

@@ -168,10 +168,13 @@ platformBrowserDynamic().bootstrapModule(AppModule);
### Properties
The properties currentFolderId, folderNode and node are the entry initialization properties of the document list. They can not be used together, choose the one that suites more your use case.
| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `currentFolderId` | string | null | Initial node ID of displayed folder. Can be `-root-`, `-shared-`, `-my-`, or a fixed node ID |
| `folderNode` | `MinimalNodeEntryEntity` | null | Currently displayed folder node |
| `node` | `NodePaging` | null | Document list will show all the node contained in the NodePaging entity |
| `navigate` | boolean | true | Toggles navigation to folder content or file preview |
| `navigationMode` | string (click\|dblclick) | dblclick | User interaction for folder navigation or file preview |
| `thumbnails` | boolean | false | Show document thumbnails rather than icons |
@@ -179,6 +182,7 @@ platformBrowserDynamic().bootstrapModule(AppModule);
| `multiselect` | boolean | false | Toggles multiselect mode |
| `contentActions` | boolean | false | Toggles content actions for each row |
| `contextMenuActions` | boolean | false | Toggles context menus for each row |
| `enablePagination` | boolean | true | Shows pagination |
| `creationMenuActions` | boolean | true | Toggles the creation menu actions|
| `rowFilter` | `RowFilter` | | Custom row filter, [see more](#custom-row-filter).
| `imageResolver` | `ImageResolver` | | Custom image resolver, [see more](#custom-image-resolver).

View File

@@ -1,7 +1,14 @@
// Tun on full stack traces in errors to help debugging
Error.stackTraceLimit = Infinity;
// jasmine.DEFAULT_TIMEOUT_INTERVAL = 1000;
jasmine.DEFAULT_TIMEOUT_INTERVAL = 100000;
window.componentHandler = {
upgradeAllRegistered: function () {
},
upgradeElement: function () {
}
};
__karma__.loaded = function() {};

View File

@@ -27,4 +27,11 @@
</no-content-template>
</div>
</alfresco-datatable>
<alfresco-pagination *ngIf="enablePagination"
(changePageSize)="onChangePageSize($event)"
(nextPage)="onNextPage($event)"
(prevPage)="onPrevPage($event)"
[pagination]="pagination"
[supportedPageSizes]="[5, 10, 15, 20]">
</alfresco-pagination>

View File

@@ -22,7 +22,7 @@ import { DocumentListComponent } from './document-list.component';
import { DocumentListServiceMock } from './../assets/document-list.service.mock';
import { ContentActionModel } from '../models/content-action.model';
import { FileNode, FolderNode } from '../assets/document-library.model.mock';
import { NodeMinimalEntry } from '../models/document-library.model';
import { NodeMinimalEntry, NodeMinimal, NodePaging } from '../models/document-library.model';
import { ShareDataRow, RowFilter, ImageResolver } from './../data/share-datatable-adapter';
describe('DocumentList', () => {
@@ -291,10 +291,10 @@ describe('DocumentList', () => {
});
/*
it('should not get node path for null node', () => {
expect(documentList.getNodePath(null)).toBeNull();
});
*/
it('should not get node path for null node', () => {
expect(documentList.getNodePath(null)).toBeNull();
});
*/
it('should require valid node for file preview', () => {
let file = new FileNode();
@@ -326,12 +326,30 @@ describe('DocumentList', () => {
expect(documentList.performNavigation).not.toHaveBeenCalled();
});
it('should display folder content on reload', () => {
it('should display folder content from loadFolder on reload if folderNode defined', () => {
documentList.folderNode = new NodeMinimal();
spyOn(documentList, 'loadFolder').and.callThrough();
documentList.reload();
expect(documentList.loadFolder).toHaveBeenCalled();
});
it('should display folder content from loadFolderByNodeId on reload if currentFolderId defined', () => {
documentList.currentFolderId = 'id-folder';
spyOn(documentListService, 'getFolderNode').and.returnValue(Promise.reject(false));
spyOn(documentList, 'loadFolderByNodeId').and.callThrough();
documentList.reload();
expect(documentList.loadFolderByNodeId).toHaveBeenCalled();
});
it('should display folder content from loadFolderByNodeId on reload if node defined', () => {
documentList.node = new NodePaging();
spyOn(documentList.data, 'loadPage').and.callThrough();
documentList.reload();
expect(documentList.data.loadPage).toHaveBeenCalled();
});
it('should require node to resolve context menu actions', () => {
expect(documentList.getContextActions(null)).toBeNull();
@@ -428,6 +446,7 @@ describe('DocumentList', () => {
it('should set row filter for underlying adapter', () => {
let filter = <RowFilter> {};
documentList.currentFolderId = 'id';
spyOn(documentList.data, 'setFilter').and.callThrough();
documentList.rowFilter = filter;
@@ -445,7 +464,7 @@ describe('DocumentList', () => {
it('should emit [nodeClick] event on row click', () => {
let node = new NodeMinimalEntry();
let row = new ShareDataRow(node);
let event = <DataRowEvent> { value: row };
let event = <DataRowEvent> {value: row};
spyOn(documentList, 'onNodeClick').and.callThrough();
documentList.onRowClick(event);
@@ -455,7 +474,7 @@ describe('DocumentList', () => {
it('should emit [nodeDblClick] event on row double-click', () => {
let node = new NodeMinimalEntry();
let row = new ShareDataRow(node);
let event = <DataRowEvent> { value: row };
let event = <DataRowEvent> {value: row};
spyOn(documentList, 'onNodeDblClick').and.callThrough();
documentList.onRowDblClick(event);
@@ -464,8 +483,8 @@ describe('DocumentList', () => {
it('should load folder by ID on init', () => {
documentList.currentFolderId = '1d26e465-dea3-42f3-b415-faa8364b9692';
spyOn(documentList.data, 'loadById').and.returnValue(Promise.resolve());
documentList.ngOnInit();
expect(documentList.data.loadById).toHaveBeenCalled();
spyOn(documentList, 'loadFolderNodesByFolderNodeId').and.returnValue(Promise.resolve());
documentList.ngOnChanges({folderNode: new SimpleChange(null, documentList.currentFolderId)});
expect(documentList.loadFolderNodesByFolderNodeId).toHaveBeenCalled();
});
});

View File

@@ -15,9 +15,22 @@
* limitations under the License.
*/
import { Component, OnInit, Input, OnChanges, Output, SimpleChanges, EventEmitter, AfterContentInit, TemplateRef, NgZone, ViewChild, HostListener } from '@angular/core';
import {
Component,
OnInit,
Input,
OnChanges,
Output,
SimpleChanges,
EventEmitter,
AfterContentInit,
TemplateRef,
NgZone,
ViewChild,
HostListener
} from '@angular/core';
import { Subject } from 'rxjs/Rx';
import { MinimalNodeEntity, MinimalNodeEntryEntity } from 'alfresco-js-api';
import { MinimalNodeEntity, MinimalNodeEntryEntity, NodePaging, Pagination } from 'alfresco-js-api';
import { AlfrescoTranslationService } from 'ng2-alfresco-core';
import { DataRowEvent, DataTableComponent, ObjectDataColumn } from 'ng2-alfresco-datatable';
import { DocumentListService } from './../services/document-list.service';
@@ -55,6 +68,9 @@ export class DocumentListComponent implements OnInit, OnChanges, AfterContentIni
@Input()
multiselect: boolean = false;
@Input()
enablePagination: boolean = true;
@Input()
contentActions: boolean = false;
@@ -67,10 +83,15 @@ export class DocumentListComponent implements OnInit, OnChanges, AfterContentIni
@Input()
pageSize: number = DocumentListComponent.DEFAULT_PAGE_SIZE;
skipCount: number = 0;
pagination: Pagination;
@Input()
set rowFilter(value: RowFilter) {
if (this.data) {
if (this.data && value && this.currentFolderId) {
this.data.setFilter(value);
this.loadFolderNodesByFolderNodeId(this.currentFolderId, this.pageSize, this.skipCount);
}
};
@@ -81,6 +102,16 @@ export class DocumentListComponent implements OnInit, OnChanges, AfterContentIni
}
}
// The identifier of a node. You can also use one of these well-known aliases: -my- | -shared- | -root-
@Input()
currentFolderId: string = null;
@Input()
folderNode: MinimalNodeEntryEntity = null;
@Input()
node: NodePaging = null;
@Output()
nodeClick: EventEmitter<any> = new EventEmitter();
@@ -102,25 +133,17 @@ export class DocumentListComponent implements OnInit, OnChanges, AfterContentIni
@ViewChild(DataTableComponent)
dataTable: DataTableComponent;
// The identifier of a node. You can also use one of these well-known aliases: -my- | -shared- | -root-
@Input()
currentFolderId: string = null;
@Input()
folderNode: MinimalNodeEntryEntity = null;
errorMessage;
actions: ContentActionModel[] = [];
emptyFolderTemplate: TemplateRef<any>;
contextActionHandler: Subject<any> = new Subject();
data: ShareDataTableAdapter;
constructor(
private documentListService: DocumentListService,
private ngZone: NgZone,
private translateService: AlfrescoTranslationService) {
constructor(private documentListService: DocumentListService,
private ngZone: NgZone,
private translateService: AlfrescoTranslationService) {
this.data = new ShareDataTableAdapter(this.documentListService, './', []);
this.data = new ShareDataTableAdapter(this.documentListService, this.baseComponentPath, []);
if (translateService) {
translateService.addTranslationFolder('ng2-alfresco-documentlist', 'node_modules/ng2-alfresco-documentlist/src');
@@ -151,15 +174,15 @@ export class DocumentListComponent implements OnInit, OnChanges, AfterContentIni
ngOnInit() {
this.data.thumbnails = this.thumbnails;
this.data.maxItems = this.pageSize;
this.contextActionHandler.subscribe(val => this.contextActionCallback(val));
// Automatically enforce single-click navigation for mobile browsers
this.enforceSingleClickNavigationForMobile();
}
private enforceSingleClickNavigationForMobile(): void {
if (this.isMobile()) {
this.navigationMode = DocumentListComponent.SINGLE_CLICK_NAVIGATION;
}
this.loadFolder();
}
ngAfterContentInit() {
@@ -172,9 +195,37 @@ export class DocumentListComponent implements OnInit, OnChanges, AfterContentIni
ngOnChanges(changes: SimpleChanges) {
if (changes['folderNode'] && changes['folderNode'].currentValue) {
this.loadFolder();
} else if (changes['currentFolderId'] && changes['currentFolderId'].currentValue) {
this.loadFolderByNodeId(changes['currentFolderId'].currentValue);
return;
}
if (changes['currentFolderId'] && changes['currentFolderId'].currentValue) {
this.loadFolderByNodeId(changes['currentFolderId'].currentValue);
return;
}
if (changes['node'] && changes['node'].currentValue) {
this.data.loadPage(changes['node'].currentValue);
return;
}
}
reload() {
this.ngZone.run(() => {
if (this.folderNode) {
this.loadFolder();
return;
}
if (this.currentFolderId) {
this.loadFolderByNodeId(this.currentFolderId);
return;
}
if (this.node) {
this.data.loadPage(this.node);
return;
}
});
}
isEmptyTemplateDefined() {
@@ -229,7 +280,7 @@ export class DocumentListComponent implements OnInit, OnChanges, AfterContentIni
this.folderNode = node.entry;
this.loadFolder();
this.folderChange.emit({ node: node.entry });
this.folderChange.emit({node: node.entry});
return true;
}
return false;
@@ -246,21 +297,10 @@ export class DocumentListComponent implements OnInit, OnChanges, AfterContentIni
}
}
loadFolderById(id: string): Promise<any> {
return this.data.loadById(id);
}
reload() {
this.ngZone.run(() => {
this.loadFolder();
});
}
loadFolder() {
let nodeId = this.folderNode ? this.folderNode.id : this.currentFolderId;
if (nodeId) {
this.loadFolderById(nodeId)
.catch(err => this.error.emit(err));
this.loadFolderNodesByFolderNodeId(nodeId, this.pageSize, this.skipCount).catch(err => this.error.emit(err));
}
}
@@ -269,9 +309,33 @@ export class DocumentListComponent implements OnInit, OnChanges, AfterContentIni
this.documentListService.getFolderNode(nodeId).then(node => {
this.folderNode = node;
this.currentFolderId = node.id;
this.data.loadById(node.id).catch(err => this.error.emit(err));
this.loadFolderNodesByFolderNodeId(node.id, this.pageSize, this.skipCount).catch(err => this.error.emit(err));
})
.catch(err => this.error.emit(err));
.catch(err => this.error.emit(err));
}
loadFolderNodesByFolderNodeId(id: string, maxItems: number, skipCount: number): Promise<any> {
return new Promise((resolve, reject) => {
if (id && this.documentListService) {
this.documentListService
.getFolder(null, {
maxItems: maxItems,
skipCount: skipCount,
rootFolderId: id
})
.subscribe(val => {
this.data.loadPage(<NodePaging>val);
this.pagination = val.list.pagination;
resolve(true);
},
error => {
reject(error);
});
} else {
resolve(false);
}
});
}
/**
@@ -387,4 +451,19 @@ export class DocumentListComponent implements OnInit, OnChanges, AfterContentIni
this.reload();
this.success.emit(event);
}
public onChangePageSize(event: Pagination): void {
this.pageSize = event.maxItems;
this.reload();
}
public onNextPage(event: Pagination): void {
this.skipCount = event.skipCount;
this.reload();
}
public onPrevPage(event: Pagination): void {
this.skipCount = event.skipCount;
this.reload();
}
}

View File

@@ -17,80 +17,34 @@
import { DatePipe } from '@angular/common';
import { ObjectUtils } from 'ng2-alfresco-core';
import { PaginationProvider, DataLoadedEventEmitter, DataTableAdapter, DataRow, DataColumn, DataSorting } from 'ng2-alfresco-datatable';
import { DataTableAdapter, DataRow, DataColumn, DataSorting } from 'ng2-alfresco-datatable';
import { NodePaging, NodeMinimalEntry } from './../models/document-library.model';
import { DocumentListService } from './../services/document-list.service';
export class ShareDataTableAdapter implements DataTableAdapter, PaginationProvider {
export class ShareDataTableAdapter implements DataTableAdapter {
ERR_ROW_NOT_FOUND: string = 'Row not found';
ERR_COL_NOT_FOUND: string = 'Column not found';
DEFAULT_DATE_FORMAT: string = 'medium';
DEFAULT_PAGE_SIZE: number = 20;
MIN_PAGE_SIZE: number = 5;
private sorting: DataSorting;
private rows: DataRow[];
private columns: DataColumn[];
private page: NodePaging;
private folderNodeId: string;
private filter: RowFilter;
private imageResolver: ImageResolver;
private _count: number = 0;
private _hasMoreItems: boolean = false;
private _totalItems: number = 0;
private _skipCount: number = 0;
private _maxItems: number = this.DEFAULT_PAGE_SIZE;
thumbnails: boolean = false;
dataLoaded: DataLoadedEventEmitter;
selectedRow: DataRow;
constructor(private documentListService: DocumentListService,
private basePath: string,
schema: DataColumn[]) {
this.dataLoaded = new DataLoadedEventEmitter();
this.rows = [];
this.columns = schema || [];
this.resetPagination();
}
get count(): number {
return this._count;
}
get hasMoreItems(): boolean {
return this._hasMoreItems;
}
get totalItems(): number {
return this._totalItems;
}
get skipCount(): number {
return this._skipCount;
}
set skipCount(value: number) {
if (value !== this._skipCount) {
this._skipCount = value > 0 ? value : 0;
this.loadById(this.folderNodeId);
}
}
get maxItems(): number {
return this._maxItems;
}
set maxItems(value: number) {
if (value !== this._maxItems) {
this._maxItems = value > this.MIN_PAGE_SIZE ? value : this.MIN_PAGE_SIZE;
this.loadById(this.folderNodeId);
}
}
getRows(): Array<DataRow> {
@@ -198,37 +152,8 @@ export class ShareDataTableAdapter implements DataTableAdapter, PaginationProvid
this.setSorting(sorting);
}
loadById(id: string): Promise<any> {
return new Promise((resolve, reject) => {
if (id && this.documentListService) {
this.documentListService
.getFolder(null, {
maxItems: this._maxItems,
skipCount: this._skipCount,
rootFolderId: id
})
.subscribe(val => {
this.folderNodeId = id;
this.loadPage(<NodePaging>val);
this.dataLoaded.emit(null);
resolve(true);
},
error => {
reject(error);
});
} else {
resolve(false);
}
});
}
setFilter(filter: RowFilter) {
this.filter = filter;
if (this.filter && this.folderNodeId) {
this.loadById(this.folderNodeId);
}
}
setImageResolver(resolver: ImageResolver) {
@@ -263,9 +188,8 @@ export class ShareDataTableAdapter implements DataTableAdapter, PaginationProvid
}
}
private loadPage(page: NodePaging) {
public loadPage(page: NodePaging) {
this.page = page;
this.resetPagination();
let rows = [];
@@ -293,15 +217,6 @@ export class ShareDataTableAdapter implements DataTableAdapter, PaginationProvid
}
}
}
let pagination = page.list.pagination;
if (pagination) {
this._count = pagination.count;
this._hasMoreItems = pagination.hasMoreItems;
this._maxItems = pagination.maxItems;
this._skipCount = pagination.skipCount;
this._totalItems = pagination.totalItems;
}
}
this.rows = rows;
@@ -310,14 +225,6 @@ export class ShareDataTableAdapter implements DataTableAdapter, PaginationProvid
getImagePath(id: string): any {
return `${this.basePath}/assets/images/${id}`;
}
private resetPagination() {
this._count = 0;
this._hasMoreItems = false;
this._totalItems = 0;
this._skipCount = 0;
this._maxItems = this.DEFAULT_PAGE_SIZE;
}
}
export class ShareDataRow implements DataRow {

View File

@@ -124,7 +124,6 @@ export class DocumentListService {
include: ['path', 'properties']
};
// see https://github.com/Alfresco/alfresco-js-api/issues/140
let nodes: any = this.apiService.getInstance().nodes;
return nodes.getNodeInfo(nodeId, opts);
}

View File

@@ -1,7 +1,14 @@
// Tun on full stack traces in errors to help debugging
Error.stackTraceLimit = Infinity;
// jasmine.DEFAULT_TIMEOUT_INTERVAL = 1000;
jasmine.DEFAULT_TIMEOUT_INTERVAL = 100000;
window.componentHandler = {
upgradeAllRegistered: function () {
},
upgradeElement: function () {
}
};
__karma__.loaded = function() {};

View File

@@ -89,6 +89,7 @@ Follow the 3 steps below:
- ng2-alfresco-core
- alfresco-js-api
- ng2-alfresco-search
- ng2-alfresco-documentlist
Please refer to the following example file: [systemjs.config.js](demo/systemjs
.config.js) .
@@ -165,7 +166,7 @@ platformBrowserDynamic().bootstrapModule(AppModule);
| `searchTerm` | {string} | (optional) | "" | Search term to pre-populate the field with |
| `inputType` | {string} | (optional) | "text" | Type of the input field to render, e.g. "search" or "text" (default) |
| `expandable` | {boolean} | (optional) | true | Whether to use an expanding search control, if false then a regular input is used. |
| `autocomplet` | {boolean} | (optional) | true | Whether the browser should offer field auto-completion for the input field to the user. |
| `autocomplete` | {boolean} | (optional) | true | Whether the browser should offer field auto-completion for the input field to the user. |
| `liveSearchEnabled` | {boolean} | (optional) | true | Whether find-as-you-type suggestions should be offered for matching content items. Set to false to disable. |
| `liveSearchRoot` | {boolean} | (optional) | "-root-" | NodeRef or node name where the search should start. |
| `liveSearchResultType` | {boolean} | (optional) | (none) | Node type to filter live search results by, e.g. 'cm:content'. |
@@ -228,7 +229,7 @@ platformBrowserDynamic().bootstrapModule(AppModule);
| Name | Description |
| --- | --- |
| `navigate` | Emitted when a search result is clicked or double-clicked |
| `preview` | emitted when user acts upon files with either single or double click (depends on `navigation-mode`), recommended for Viewer components integration |
| `resultsLoad` | Emitted when search results have fully loaded |
#### Options

View File

@@ -1,4 +0,0 @@
{
"SEARCH": {
}
}

View File

@@ -70,6 +70,8 @@
"ng2-translate": "2.5.0",
"alfresco-js-api": "^1.0.0",
"ng2-alfresco-core": "1.0.0",
"ng2-alfresco-datatable": "1.0.0",
"ng2-alfresco-documentlist": "1.0.0",
"ng2-alfresco-search": "^1.0.0"
},
"devDependencies": {

View File

@@ -26,6 +26,8 @@
'ng2-translate': 'npm:ng2-translate',
'alfresco-js-api': 'npm:alfresco-js-api/dist',
'ng2-alfresco-core': 'npm:ng2-alfresco-core',
'ng2-alfresco-documentlist': 'npm:ng2-alfresco-documentlist',
'ng2-alfresco-datatable': 'npm:ng2-alfresco-datatable',
'ng2-alfresco-search': 'npm:ng2-alfresco-search'
},
// packages tells the System loader how to load when no filename and/or no extension
@@ -40,6 +42,8 @@
'ng2-translate': { defaultExtension: 'js' },
'alfresco-js-api': { main: './alfresco-js-api.js', defaultExtension: 'js'},
'ng2-alfresco-core': { main: './index.js', defaultExtension: 'js'},
'ng2-alfresco-documentlist': { main: './index.js', defaultExtension: 'js'},
'ng2-alfresco-datatable': { main: './index.js', defaultExtension: 'js'},
'ng2-alfresco-search': { main: './index.js', defaultExtension: 'js'}
}
});

View File

@@ -18,12 +18,12 @@
import { NgModule, ModuleWithProviders } from '@angular/core';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { CoreModule } from 'ng2-alfresco-core';
import { AlfrescoSearchService } from './src/services/alfresco-search.service';
import { AlfrescoThumbnailService } from './src/services/alfresco-thumbnail.service';
import { AlfrescoSearchComponent } from './src/components/alfresco-search.component';
import { AlfrescoSearchControlComponent } from './src/components/alfresco-search-control.component';
import { AlfrescoSearchAutocompleteComponent } from './src/components/alfresco-search-autocomplete.component';
import { DocumentListModule } from 'ng2-alfresco-documentlist';
// services
export * from './src/services/alfresco-search.service';
@@ -45,6 +45,7 @@ export const ALFRESCO_SEARCH_PROVIDERS: [any] = [
@NgModule({
imports: [
DocumentListModule.forRoot(),
CoreModule,
FormsModule,
ReactiveFormsModule

View File

@@ -1,7 +1,14 @@
// Tun on full stack traces in errors to help debugging
Error.stackTraceLimit = Infinity;
// jasmine.DEFAULT_TIMEOUT_INTERVAL = 1000;
jasmine.DEFAULT_TIMEOUT_INTERVAL = 100000;
window.componentHandler = {
upgradeAllRegistered: function () {
},
upgradeElement: function () {
}
};
__karma__.loaded = function() {};
@@ -56,7 +63,9 @@ var map = {
'ng2-translate': 'npm:ng2-translate',
'alfresco-js-api': 'npm:alfresco-js-api/dist',
'ng2-alfresco-core': 'npm:ng2-alfresco-core'
'ng2-alfresco-core': 'npm:ng2-alfresco-core',
'ng2-alfresco-datatable': 'npm:ng2-alfresco-datatable',
'ng2-alfresco-documentlist': 'npm:ng2-alfresco-documentlist'
};
var packages = {
@@ -65,7 +74,9 @@ var packages = {
'ng2-translate': { defaultExtension: 'js' },
'alfresco-js-api': { main: './alfresco-js-api.js', defaultExtension: 'js'},
'ng2-alfresco-core': { main: './index.js', 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 config = {

View File

@@ -50,6 +50,12 @@ module.exports = function (config) {
{ pattern: 'node_modules/ng2-alfresco-core/src/**/*.js', included: false, served: true, watched: false },
{ pattern: 'node_modules/ng2-alfresco-core/index.js', included: false, served: true, watched: false },
{ pattern: 'node_modules/ng2-alfresco-datatable/src/**/*.js', included: false, served: true, watched: false },
{ pattern: 'node_modules/ng2-alfresco-datatable/index.js', included: false, served: true, watched: false },
{ pattern: 'node_modules/ng2-alfresco-documentlist/src/**/*.js', included: false, served: true, watched: false },
{ pattern: 'node_modules/ng2-alfresco-documentlist/index.js', included: false, served: true, watched: false },
// paths to support debugging with source maps in dev tools
{pattern: 'src/**/*.ts', included: false, watched: false},
{pattern: 'src/**/*.js.map', included: false, watched: false}

View File

@@ -17,7 +17,7 @@
"posttest": "remap-istanbul -i coverage/report/coverage-final.json -o coverage/report -t html && remap-istanbul -i coverage/report/coverage-final.json -o coverage/report/coverage-final.json",
"coverage": "npm run test && wsrv -o -p 9875 ./coverage/report",
"prepublish": "npm run test",
"travis": "npm link ng2-alfresco-core",
"travis": "npm link ng2-alfresco-core ng2-alfresco-datatable ng2-alfresco-documentlist",
"gulp": "gulp",
"build.umd": "gulp build.prod --color --env-config prod --build-type prod",
"reinstall": "npm cache clean && npm install"
@@ -70,7 +70,9 @@
"zone.js": "^0.6.23",
"ng2-translate": "2.5.0",
"alfresco-js-api": "^1.0.0",
"ng2-alfresco-core": "1.0.0"
"ng2-alfresco-core": "1.0.0",
"ng2-alfresco-datatable": "1.0.0",
"ng2-alfresco-documentlist": "1.0.0"
},
"devDependencies": {
"@types/jasmine": "^2.2.33",

Binary file not shown.

Before

Width:  |  Height:  |  Size: 202 KiB

View File

@@ -0,0 +1,208 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="566px" height="165px" viewBox="0 0 566 165" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 3.8.3 (29802) - http://www.bohemiancoding.com/sketch -->
<title>empty_doc_lib</title>
<desc>Created with Sketch.</desc>
<defs>
<rect id="path-1" x="5.68434189e-14" y="-1.01962883e-12" width="78.1679389" height="78.1679389" rx="2"></rect>
<filter x="-50%" y="-50%" width="200%" height="200%" filterUnits="objectBoundingBox" id="filter-2">
<feOffset dx="0" dy="2" in="SourceAlpha" result="shadowOffsetOuter1"></feOffset>
<feGaussianBlur stdDeviation="1" in="shadowOffsetOuter1" result="shadowBlurOuter1"></feGaussianBlur>
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.24 0" type="matrix" in="shadowBlurOuter1" result="shadowMatrixOuter1"></feColorMatrix>
<feOffset dx="0" dy="0" in="SourceAlpha" result="shadowOffsetOuter2"></feOffset>
<feGaussianBlur stdDeviation="1" in="shadowOffsetOuter2" result="shadowBlurOuter2"></feGaussianBlur>
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.12 0" type="matrix" in="shadowBlurOuter2" result="shadowMatrixOuter2"></feColorMatrix>
<feMerge>
<feMergeNode in="shadowMatrixOuter1"></feMergeNode>
<feMergeNode in="shadowMatrixOuter2"></feMergeNode>
</feMerge>
</filter>
<rect id="path-3" x="-4.54747351e-13" y="5.68434189e-13" width="78.1679389" height="78.1679389" rx="2"></rect>
<filter x="-50%" y="-50%" width="200%" height="200%" filterUnits="objectBoundingBox" id="filter-4">
<feOffset dx="0" dy="2" in="SourceAlpha" result="shadowOffsetOuter1"></feOffset>
<feGaussianBlur stdDeviation="1" in="shadowOffsetOuter1" result="shadowBlurOuter1"></feGaussianBlur>
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.24 0" type="matrix" in="shadowBlurOuter1" result="shadowMatrixOuter1"></feColorMatrix>
<feOffset dx="0" dy="0" in="SourceAlpha" result="shadowOffsetOuter2"></feOffset>
<feGaussianBlur stdDeviation="1" in="shadowOffsetOuter2" result="shadowBlurOuter2"></feGaussianBlur>
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.12 0" type="matrix" in="shadowBlurOuter2" result="shadowMatrixOuter2"></feColorMatrix>
<feMerge>
<feMergeNode in="shadowMatrixOuter1"></feMergeNode>
<feMergeNode in="shadowMatrixOuter2"></feMergeNode>
</feMerge>
</filter>
<rect id="path-5" x="-1.08002496e-12" y="7.81597009e-14" width="78.1679389" height="78.1679389" rx="2"></rect>
<filter x="-50%" y="-50%" width="200%" height="200%" filterUnits="objectBoundingBox" id="filter-6">
<feOffset dx="0" dy="2" in="SourceAlpha" result="shadowOffsetOuter1"></feOffset>
<feGaussianBlur stdDeviation="1" in="shadowOffsetOuter1" result="shadowBlurOuter1"></feGaussianBlur>
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.24 0" type="matrix" in="shadowBlurOuter1" result="shadowMatrixOuter1"></feColorMatrix>
<feOffset dx="0" dy="0" in="SourceAlpha" result="shadowOffsetOuter2"></feOffset>
<feGaussianBlur stdDeviation="1" in="shadowOffsetOuter2" result="shadowBlurOuter2"></feGaussianBlur>
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.12 0" type="matrix" in="shadowBlurOuter2" result="shadowMatrixOuter2"></feColorMatrix>
<feMerge>
<feMergeNode in="shadowMatrixOuter1"></feMergeNode>
<feMergeNode in="shadowMatrixOuter2"></feMergeNode>
</feMerge>
</filter>
<rect id="path-7" x="1.29318778e-12" y="9.23705556e-14" width="78.1679389" height="78.1679389" rx="2"></rect>
<filter x="-50%" y="-50%" width="200%" height="200%" filterUnits="objectBoundingBox" id="filter-8">
<feOffset dx="0" dy="2" in="SourceAlpha" result="shadowOffsetOuter1"></feOffset>
<feGaussianBlur stdDeviation="1" in="shadowOffsetOuter1" result="shadowBlurOuter1"></feGaussianBlur>
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.24 0" type="matrix" in="shadowBlurOuter1" result="shadowMatrixOuter1"></feColorMatrix>
<feOffset dx="0" dy="0" in="SourceAlpha" result="shadowOffsetOuter2"></feOffset>
<feGaussianBlur stdDeviation="1" in="shadowOffsetOuter2" result="shadowBlurOuter2"></feGaussianBlur>
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.12 0" type="matrix" in="shadowBlurOuter2" result="shadowMatrixOuter2"></feColorMatrix>
<feMerge>
<feMergeNode in="shadowMatrixOuter1"></feMergeNode>
<feMergeNode in="shadowMatrixOuter2"></feMergeNode>
</feMerge>
</filter>
<rect id="path-9" x="-2.96651592e-13" y="-7.60280727e-13" width="78.1679389" height="78.1679389" rx="2"></rect>
<filter x="-50%" y="-50%" width="200%" height="200%" filterUnits="objectBoundingBox" id="filter-10">
<feOffset dx="0" dy="2" in="SourceAlpha" result="shadowOffsetOuter1"></feOffset>
<feGaussianBlur stdDeviation="1" in="shadowOffsetOuter1" result="shadowBlurOuter1"></feGaussianBlur>
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.24 0" type="matrix" in="shadowBlurOuter1" result="shadowMatrixOuter1"></feColorMatrix>
<feOffset dx="0" dy="0" in="SourceAlpha" result="shadowOffsetOuter2"></feOffset>
<feGaussianBlur stdDeviation="1" in="shadowOffsetOuter2" result="shadowBlurOuter2"></feGaussianBlur>
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.12 0" type="matrix" in="shadowBlurOuter2" result="shadowMatrixOuter2"></feColorMatrix>
<feMerge>
<feMergeNode in="shadowMatrixOuter1"></feMergeNode>
<feMergeNode in="shadowMatrixOuter2"></feMergeNode>
</feMerge>
</filter>
<rect id="path-11" x="3.48165941e-13" y="2.27373675e-13" width="78.1679389" height="78.1679389" rx="2"></rect>
<filter x="-50%" y="-50%" width="200%" height="200%" filterUnits="objectBoundingBox" id="filter-12">
<feOffset dx="0" dy="2" in="SourceAlpha" result="shadowOffsetOuter1"></feOffset>
<feGaussianBlur stdDeviation="1" in="shadowOffsetOuter1" result="shadowBlurOuter1"></feGaussianBlur>
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.24 0" type="matrix" in="shadowBlurOuter1" result="shadowMatrixOuter1"></feColorMatrix>
<feOffset dx="0" dy="0" in="SourceAlpha" result="shadowOffsetOuter2"></feOffset>
<feGaussianBlur stdDeviation="1" in="shadowOffsetOuter2" result="shadowBlurOuter2"></feGaussianBlur>
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.12 0" type="matrix" in="shadowBlurOuter2" result="shadowMatrixOuter2"></feColorMatrix>
<feMerge>
<feMergeNode in="shadowMatrixOuter1"></feMergeNode>
<feMergeNode in="shadowMatrixOuter2"></feMergeNode>
</feMerge>
</filter>
<rect id="path-13" x="0" y="-5.40012479e-13" width="78.1679389" height="78.1679389" rx="2"></rect>
<filter x="-50%" y="-50%" width="200%" height="200%" filterUnits="objectBoundingBox" id="filter-14">
<feOffset dx="0" dy="2" in="SourceAlpha" result="shadowOffsetOuter1"></feOffset>
<feGaussianBlur stdDeviation="1" in="shadowOffsetOuter1" result="shadowBlurOuter1"></feGaussianBlur>
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.24 0" type="matrix" in="shadowBlurOuter1" result="shadowMatrixOuter1"></feColorMatrix>
<feOffset dx="0" dy="0" in="SourceAlpha" result="shadowOffsetOuter2"></feOffset>
<feGaussianBlur stdDeviation="1" in="shadowOffsetOuter2" result="shadowBlurOuter2"></feGaussianBlur>
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.12 0" type="matrix" in="shadowBlurOuter2" result="shadowMatrixOuter2"></feColorMatrix>
<feMerge>
<feMergeNode in="shadowMatrixOuter1"></feMergeNode>
<feMergeNode in="shadowMatrixOuter2"></feMergeNode>
</feMerge>
</filter>
<rect id="path-15" x="0" y="0" width="78.1679389" height="78.1679389" rx="2"></rect>
<filter x="-50%" y="-50%" width="200%" height="200%" filterUnits="objectBoundingBox" id="filter-16">
<feOffset dx="0" dy="2" in="SourceAlpha" result="shadowOffsetOuter1"></feOffset>
<feGaussianBlur stdDeviation="1" in="shadowOffsetOuter1" result="shadowBlurOuter1"></feGaussianBlur>
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.24 0" type="matrix" in="shadowBlurOuter1" result="shadowMatrixOuter1"></feColorMatrix>
<feOffset dx="0" dy="0" in="SourceAlpha" result="shadowOffsetOuter2"></feOffset>
<feGaussianBlur stdDeviation="1" in="shadowOffsetOuter2" result="shadowBlurOuter2"></feGaussianBlur>
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.12 0" type="matrix" in="shadowBlurOuter2" result="shadowMatrixOuter2"></feColorMatrix>
<feMerge>
<feMergeNode in="shadowMatrixOuter1"></feMergeNode>
<feMergeNode in="shadowMatrixOuter2"></feMergeNode>
</feMerge>
</filter>
</defs>
<g id="Symbols" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="empty-folder-state-desktop" transform="translate(-37.000000, -168.000000)">
<g id="empty_doc_lib" transform="translate(38.000000, 169.000000)">
<g id="Group-5" transform="translate(241.569490, 92.634375) rotate(-355.000000) translate(-241.569490, -92.634375) translate(202.069490, 53.134375)">
<g id="Rectangle-1196-Copy-2">
<use fill="black" fill-opacity="1" filter="url(#filter-2)" xlink:href="#path-1"></use>
<use fill="#FFFFFF" fill-rule="evenodd" xlink:href="#path-1"></use>
</g>
<g id="filetype_video" transform="translate(9.770992, 9.770992)">
<polygon id="Fill-1" points="0 58.6259542 58.6259542 58.6259542 58.6259542 0 0 0"></polygon>
<path d="M39.0839695,21.9847328 L43.9694656,21.9847328 L43.9694656,17.0992366 L39.0839695,17.0992366 L39.0839695,21.9847328 Z M39.0839695,31.7557252 L43.9694656,31.7557252 L43.9694656,26.870229 L39.0839695,26.870229 L39.0839695,31.7557252 Z M39.0839695,41.5267176 L43.9694656,41.5267176 L43.9694656,36.6412214 L39.0839695,36.6412214 L39.0839695,41.5267176 Z M14.6564885,21.9847328 L19.5419847,21.9847328 L19.5419847,17.0992366 L14.6564885,17.0992366 L14.6564885,21.9847328 Z M14.6564885,31.7557252 L19.5419847,31.7557252 L19.5419847,26.870229 L14.6564885,26.870229 L14.6564885,31.7557252 Z M14.6564885,41.5267176 L19.5419847,41.5267176 L19.5419847,36.6412214 L14.6564885,36.6412214 L14.6564885,41.5267176 Z M43.9694656,7.32824427 L43.9694656,12.2137405 L39.0839695,12.2137405 L39.0839695,7.32824427 L19.5419847,7.32824427 L19.5419847,12.2137405 L14.6564885,12.2137405 L14.6564885,7.32824427 L9.77099237,7.32824427 L9.77099237,51.2977099 L14.6564885,51.2977099 L14.6564885,46.4122137 L19.5419847,46.4122137 L19.5419847,51.2977099 L39.0839695,51.2977099 L39.0839695,46.4122137 L43.9694656,46.4122137 L43.9694656,51.2977099 L48.8549618,51.2977099 L48.8549618,7.32824427 L43.9694656,7.32824427 Z" id="Fill-2" fill="#FFC107"></path>
</g>
</g>
<g id="Group-7" transform="translate(515.948329, 81.354522) rotate(-345.000000) translate(-515.948329, -81.354522) translate(476.448329, 41.854522)">
<g id="Rectangle-1196-Copy-3">
<use fill="black" fill-opacity="1" filter="url(#filter-4)" xlink:href="#path-3"></use>
<use fill="#FFFFFF" fill-rule="evenodd" xlink:href="#path-3"></use>
</g>
<g id="filetype_image" transform="translate(9.770992, 9.770992)">
<polygon id="Fill-1" points="0 58.6259542 58.6259542 58.6259542 58.6259542 0 0 0"></polygon>
<path d="M20.7636031,32.9773435 L26.8704733,40.3178015 L35.4200916,29.3132214 L46.412458,43.9697099 L12.2139847,43.9697099 L20.7636031,32.9773435 Z M51.2979542,46.412458 L51.2979542,12.2139847 C51.2979542,9.51474809 49.1116947,7.32848855 46.412458,7.32848855 L12.2139847,7.32848855 C9.51474809,7.32848855 7.32848855,9.51474809 7.32848855,12.2139847 L7.32848855,46.412458 C7.32848855,49.1116947 9.51474809,51.2979542 12.2139847,51.2979542 L46.412458,51.2979542 C49.1116947,51.2979542 51.2979542,49.1116947 51.2979542,46.412458 L51.2979542,46.412458 Z" id="Fill-2" fill="#22BE73"></path>
</g>
</g>
<g id="Group-8" transform="translate(309.051884, 62.261808) rotate(-5.000000) translate(-309.051884, -62.261808) translate(269.551884, 22.761808)">
<g id="Rectangle-1196-Copy-4">
<use fill="black" fill-opacity="1" filter="url(#filter-6)" xlink:href="#path-5"></use>
<use fill="#FFFFFF" fill-rule="evenodd" xlink:href="#path-5"></use>
</g>
<g id="filetype_googledocs" transform="translate(9.770992, 9.770992)">
<polygon id="Fill-1" points="6.82121026e-13 58.6259542 58.6259542 58.6259542 58.6259542 -2.98427949e-13 6.82121026e-13 -2.98427949e-13"></polygon>
<g id="Group-6" transform="translate(9.770992, 4.885496)">
<path d="M7.32824427,21.9847328 L31.7557252,21.9847328 L31.7557252,19.5419847 L7.32824427,19.5419847 L7.32824427,21.9847328 Z M7.32824427,26.870229 L31.7557252,26.870229 L31.7557252,24.4274809 L7.32824427,24.4274809 L7.32824427,26.870229 Z M7.32824427,31.7557252 L31.7557252,31.7557252 L31.7557252,29.3129771 L7.32824427,29.3129771 L7.32824427,31.7557252 Z M7.32824427,36.6412214 L21.9847328,36.6412214 L21.9847328,34.1984733 L7.32824427,34.1984733 L7.32824427,36.6412214 Z M29.3129771,0 L4.88549618,0 C2.18625954,0 0.0244274809,2.18625954 0.0244274809,4.88549618 L0,43.9694656 C0,46.6687023 2.16183206,48.8549618 4.8610687,48.8549618 L34.1984733,48.8549618 C36.8977099,48.8549618 39.0839695,46.6687023 39.0839695,43.9694656 L39.0839695,9.77099237 L29.3129771,0 Z" id="Fill-2" fill="#2979FF"></path>
<polygon id="Fill-4" fill-opacity="0.5" fill="#FFFFFF" points="29.3129771 9.77099237 29.3129771 -2.84217094e-14 39.0839695 9.77099237"></polygon>
<polygon id="Fill-5" fill-opacity="0.2" fill="#000000" points="39.0839695 9.77099237 39.0839695 19.5419847 29.3129771 9.77099237"></polygon>
</g>
</g>
</g>
<g id="Group-9" transform="translate(155.408682, 49.364493) rotate(-345.000000) translate(-155.408682, -49.364493) translate(115.908682, 9.864493)">
<g id="Rectangle-1196-Copy-5">
<use fill="black" fill-opacity="1" filter="url(#filter-8)" xlink:href="#path-7"></use>
<use fill="#FFFFFF" fill-rule="evenodd" xlink:href="#path-7"></use>
</g>
<g id="filetype_pdf" transform="translate(9.770992, 9.770992)">
<polygon id="Fill-1" points="0 58.6259542 58.6259542 58.6259542 58.6259542 0 0 0"></polygon>
<path d="M45.1888855,25.5877863 L45.1888855,21.9187786 L37.853313,21.9187786 L37.853313,36.5923664 L41.5198779,36.5923664 L41.5198779,31.7777099 L45.1888855,31.7777099 L45.1888855,28.1087023 L41.5198779,28.1087023 L41.5198779,25.5877863 L45.1888855,25.5877863 Z M29.3696489,32.9233588 L31.7757557,32.9233588 L31.7757557,25.5877863 L29.3696489,25.5877863 L29.3696489,32.9233588 Z M35.4447634,32.9233588 L35.4447634,25.5877863 C35.4447634,24.5960305 35.1027786,23.7361832 34.4139237,23.0082443 C33.7275115,22.2827481 32.8481221,21.9187786 31.7781985,21.9187786 L25.703084,21.9187786 L25.703084,36.5923664 L31.7781985,36.5923664 C32.8481221,36.5923664 33.7275115,36.2308397 34.4139237,35.5029008 C35.1027786,34.7774046 35.4447634,33.9175573 35.4447634,32.9233588 L35.4447634,32.9233588 Z M17.1070534,28.1087023 L19.5131603,28.1087023 L19.5131603,25.5877863 L17.1070534,25.5877863 L17.1070534,28.1087023 Z M23.1821679,28.1087023 L23.1821679,25.5877863 C23.1821679,24.5960305 22.8181985,23.7361832 22.0927023,23.0082443 C21.3672061,22.2827481 20.5073588,21.9187786 19.5131603,21.9187786 L13.4380458,21.9187786 L13.4380458,36.5923664 L17.1070534,36.5923664 L17.1070534,31.7777099 L19.5131603,31.7777099 C20.5073588,31.7777099 21.3672061,31.4161832 22.0927023,30.6882443 C22.8181985,29.9627481 23.1821679,29.1029008 23.1821679,28.1087023 L23.1821679,28.1087023 Z M46.483542,7.32824427 C47.783084,7.32824427 48.9091908,7.8070229 49.8643053,8.7621374 C50.8218626,9.71725191 51.2981985,10.8433588 51.2981985,12.1429008 L51.2981985,46.3682443 C51.2981985,47.670229 50.8218626,48.8158779 49.8643053,49.8076336 C48.9091908,50.8018321 47.783084,51.2977099 46.483542,51.2977099 L12.2581985,51.2977099 C10.9586565,51.2977099 9.81300763,50.8018321 8.81880916,49.8076336 C7.82461069,48.8158779 7.32873282,47.670229 7.32873282,46.3682443 L7.32873282,12.1429008 C7.32873282,10.8433588 7.82461069,9.71725191 8.81880916,8.7621374 C9.81300763,7.8070229 10.9586565,7.32824427 12.2581985,7.32824427 L46.483542,7.32824427 Z" id="Fill-2" fill="#E91E63"></path>
</g>
</g>
<g id="Group-12" transform="translate(49.364493, 62.584254) rotate(-15.000000) translate(-49.364493, -62.584254) translate(9.864493, 23.084254)">
<g id="Rectangle-1196-Copy-7">
<use fill="black" fill-opacity="1" filter="url(#filter-10)" xlink:href="#path-9"></use>
<use fill="#FFFFFF" fill-rule="evenodd" xlink:href="#path-9"></use>
</g>
<g id="filetype_forms" transform="translate(9.770992, 9.770992)">
<polygon id="Fill-1" points="0 58.6259542 58.6259542 58.6259542 58.6259542 0 0 0"></polygon>
<path d="M24.4274809,24.4250382 L41.5267176,24.4250382 L41.5267176,19.539542 L24.4274809,19.539542 L24.4274809,24.4250382 Z M24.4274809,31.7532824 L41.5267176,31.7532824 L41.5267176,26.8677863 L24.4274809,26.8677863 L24.4274809,31.7532824 Z M24.4274809,39.0839695 L41.5267176,39.0839695 L41.5267176,34.1984733 L24.4274809,34.1984733 L24.4274809,39.0839695 Z M17.0992366,24.4250382 L21.9847328,24.4250382 L21.9847328,19.539542 L17.0992366,19.539542 L17.0992366,24.4250382 Z M17.0992366,31.7532824 L21.9847328,31.7532824 L21.9847328,26.8677863 L17.0992366,26.8677863 L17.0992366,31.7532824 Z M17.0992366,39.0839695 L21.9847328,39.0839695 L21.9847328,34.1984733 L17.0992366,34.1984733 L17.0992366,39.0839695 Z M43.9694656,9.77099237 L14.6564885,9.77099237 C11.9694656,9.77099237 9.77099237,11.9694656 9.77099237,14.6564885 L9.77099237,43.9694656 C9.77099237,46.6564885 11.9694656,48.8549618 14.6564885,48.8549618 L43.9694656,48.8549618 C46.6564885,48.8549618 48.8549618,46.6564885 48.8549618,43.9694656 L48.8549618,14.6564885 C48.8549618,11.9694656 46.6564885,9.77099237 43.9694656,9.77099237 L43.9694656,9.77099237 Z" id="Fill-2" fill="#651FFF"></path>
</g>
</g>
<g id="Group-11" transform="translate(107.814782, 114.998541) rotate(-10.000000) translate(-107.814782, -114.998541) translate(68.314782, 75.498541)">
<g id="Rectangle-1196-Copy-6">
<use fill="black" fill-opacity="1" filter="url(#filter-12)" xlink:href="#path-11"></use>
<use fill="#FFFFFF" fill-rule="evenodd" xlink:href="#path-11"></use>
</g>
<g id="filetype_excel" transform="translate(9.770992, 9.770992)">
<polygon id="Fill-1" points="-5.68434189e-14 58.6259542 58.6259542 58.6259542 58.6259542 -1.56319402e-13 -5.68434189e-14 -1.56319402e-13"></polygon>
<g id="Group-10" transform="translate(4.885496, 4.885496)" fill="#22BE73">
<path d="M47.1621374,41.632 L28.848855,41.632 L28.848855,38.3025344 L33.2873282,38.3025344 L33.2873282,34.4161221 L28.848855,34.4161221 L28.848855,32.1981069 L33.2873282,32.1981069 L33.2873282,28.3116947 L28.848855,28.3116947 L28.848855,26.0936794 L33.2873282,26.0936794 L33.2873282,22.2072672 L28.848855,22.2072672 L28.848855,19.9868092 L33.2873282,19.9868092 L33.2873282,16.1028397 L28.848855,16.1028397 L28.848855,13.8823817 L33.2873282,13.8823817 L33.2873282,9.99841221 L28.848855,9.99841221 L28.848855,6.66894656 L47.1621374,6.66894656 L47.1621374,41.632 L47.1621374,41.632 Z M16.3444275,33.1141374 C15.4015267,30.7984122 14.2509924,28.5632977 13.5743511,26.1425344 C12.819542,28.3947481 11.7422901,30.5223817 10.8775573,32.730626 C9.6610687,32.7135267 8.4470229,32.6646718 7.23053435,32.613374 C8.65709924,29.821313 10.0348092,27.0072672 11.5053435,24.2323053 C10.2546565,21.3742901 8.88427481,18.572458 7.59694656,15.731542 C8.81832061,15.6582595 10.0396947,15.5874198 11.2610687,15.5190229 C12.0867176,17.690626 12.9929771,19.832916 13.6745038,22.0582595 C14.4073282,19.6985649 15.5016794,17.4781069 16.4372519,15.1990229 C17.6928244,15.1086412 18.9532824,15.032916 20.2112977,14.9718473 C18.7309924,18.0057405 17.2433588,21.0420763 15.7337405,24.0661985 C17.260458,27.1758168 18.8189313,30.2610076 20.3505344,33.3681832 C19.0143511,33.2900153 17.6806107,33.2069618 16.3444275,33.1141374 L16.3444275,33.1141374 Z M48.8329771,37.2203969 C48.8280916,27.591084 48.8158779,17.961771 48.8427481,8.32757252 C48.8036641,7.38467176 48.8720611,6.34161832 48.300458,5.51841221 C47.4845802,4.9590229 46.4512977,5.0249771 45.5132824,4.98589313 C39.9584733,5.01520611 34.4036641,5.00299237 28.848855,5.00299237 L28.848855,0.564519084 L25.551145,0.564519084 C17.0381679,2.06680916 8.5178626,3.52757252 4.68958206e-13,5.00787786 L4.68958206e-13,43.852458 C8.46900763,45.3327634 16.9429008,46.7544427 25.4021374,48.2909313 L28.848855,48.2909313 L28.848855,43.2979542 C34.6039695,43.2857405 40.359084,43.3126107 46.1068702,43.2979542 C47.0351145,43.2588702 48.4225954,43.2295573 48.6448855,42.0765802 C48.9819847,40.4839084 48.8036641,38.8350534 48.8329771,37.2203969 L48.8329771,37.2203969 Z" id="Fill-2"></path>
<path d="M35.5077863,13.8821374 L43.2781679,13.8821374 L43.2781679,9.99816794 L35.5077863,9.99816794 L35.5077863,13.8821374 Z M35.5077863,19.9865649 L43.2781679,19.9865649 L43.2781679,16.1025954 L35.5077863,16.1025954 L35.5077863,19.9865649 Z M35.5077863,26.0909924 L43.2781679,26.0909924 L43.2781679,22.2070229 L35.5077863,22.2070229 L35.5077863,26.0909924 Z M35.5077863,32.1954198 L43.2781679,32.1954198 L43.2781679,28.3114504 L35.5077863,28.3114504 L35.5077863,32.1954198 Z M35.5077863,38.3022901 L43.2781679,38.3022901 L43.2781679,34.4183206 L35.5077863,34.4183206 L35.5077863,38.3022901 Z" id="Combined-Shape"></path>
</g>
</g>
</g>
<g id="Group-4" transform="translate(388.820630, 86.064353) rotate(-350.000000) translate(-388.820630, -86.064353) translate(349.320630, 46.564353)">
<g id="Rectangle-1196-Copy">
<use fill="black" fill-opacity="1" filter="url(#filter-14)" xlink:href="#path-13"></use>
<use fill="#FFFFFF" fill-rule="evenodd" xlink:href="#path-13"></use>
</g>
<g id="filetype_audio" transform="translate(9.770992, 9.770992)">
<polygon id="Fill-1" points="-1.13686838e-13 58.6259542 58.6259542 58.6259542 58.6259542 -6.39488462e-14 -1.13686838e-13 -6.39488462e-14"></polygon>
<path d="M29.3129771,7.32824427 L43.9694656,7.32824427 L43.9694656,17.0601527 L34.2375573,17.0601527 L34.2375573,41.5658015 C34.2375573,44.2381679 33.2629008,46.5270229 31.3160305,48.4348092 C29.3691603,50.3450382 27.0607634,51.2977099 24.3883969,51.2977099 C21.7160305,51.2977099 19.4271756,50.3450382 17.5193893,48.4348092 C15.6091603,46.5270229 14.6564885,44.2381679 14.6564885,41.5658015 C14.6564885,38.8934351 15.6091603,36.5850382 17.5193893,34.6381679 C19.4271756,32.6912977 21.7160305,31.7166412 24.3883969,31.7166412 C25.9932824,31.7166412 27.6323664,32.1758779 29.3129771,33.0919084 L29.3129771,7.32824427 Z" id="Fill-2" fill="#E91E63"></path>
</g>
</g>
<g id="Group-2" transform="translate(411.603053, 1.221374)">
<g id="Group-3">
<g id="Rectangle-1196">
<use fill="black" fill-opacity="1" filter="url(#filter-16)" xlink:href="#path-15"></use>
<use fill="#FFFFFF" fill-rule="evenodd" xlink:href="#path-15"></use>
</g>
<polygon id="Fill-1" points="9.77099237 68.3969466 68.3969466 68.3969466 68.3969466 9.77099237 9.77099237 9.77099237"></polygon>
</g>
<g id="filetype_book" transform="translate(9.770992, 9.770992)">
<polygon id="Fill-1" points="0 58.6259542 58.6259542 58.6259542 58.6259542 0 0 0"></polygon>
<path d="M14.6564885,9.77099237 L26.870229,9.77099237 L26.870229,29.3129771 L20.7633588,25.648855 L14.6564885,29.3129771 L14.6564885,9.77099237 Z M43.9694656,4.88549618 L14.6564885,4.88549618 C11.9572519,4.88549618 9.77099237,7.07175573 9.77099237,9.77099237 L9.77099237,48.8549618 C9.77099237,51.5541985 11.9572519,53.740458 14.6564885,53.740458 L43.9694656,53.740458 C46.6687023,53.740458 48.8549618,51.5541985 48.8549618,48.8549618 L48.8549618,9.77099237 C48.8549618,7.07175573 46.6687023,4.88549618 43.9694656,4.88549618 L43.9694656,4.88549618 Z" id="Fill-2" fill="#FF6D40"></path>
</g>
</g>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 26 KiB

View File

@@ -68,7 +68,7 @@ export class AlfrescoSearchAutocompleteComponent implements OnInit, OnChanges {
@ViewChild('resultsTableBody', {}) resultsTableBody: ElementRef;
baseComponentPath: string = module.id.replace('/components/alfresco-search.component.js', '');
baseComponentPath: string = module.id.replace('/components/alfresco-search-autocomplete.component.js', '');
constructor(private searchService: AlfrescoSearchService,
private translateService: AlfrescoTranslationService,
@@ -128,13 +128,14 @@ export class AlfrescoSearchAutocompleteComponent implements OnInit, OnChanges {
if (node.entry.content && node.entry.content.mimeType) {
let icon = this.thumbnailService.getMimeTypeIcon(node.entry.content.mimeType);
return this.resolveIconPath(icon);
} else if (node.entry.isFolder) {
return `${this.baseComponentPath}/../assets/images/ft_ic_folder.svg`;
}
if (node.entry.isFolder) {
return `${this.baseComponentPath}/assets/images/ft_ic_folder.svg`;
}
}
resolveIconPath(icon: string): string {
return `${this.baseComponentPath}/../assets/images/${icon}`;
return `${this.baseComponentPath}/assets/images/${icon}`;
}
/**

View File

@@ -13,3 +13,25 @@
overflow: hidden;
text-overflow: ellipsis;
}
.no-result-message {
height: 32px;
opacity: 0.26;
font-family: Muli, Helvetica, Arial, sans-serif;
font-size: 24px;
line-height: 1.33;
letter-spacing: -1px;
color: #000000;
}
.no-result__empty_doc_lib {
width: 565px;
height: 161px;
object-fit: contain;
margin-top: 17px;
}
.empty_template {
text-align: center;
margin-top: 20px;
margin-bottom: 20px;
}

View File

@@ -1,38 +1,79 @@
<table data-automation-id="search_result_table" *ngIf="results && results.length && searchTerm" class="mdl-data-table mdl-js-data-table mdl-shadow--2dp full-width">
<caption data-automation-id="search_result_found">{{ 'SEARCH.RESULTS.SUMMARY' | translate:{numResults: results.length, searchTerm: searchTerm} }}</caption>
<thead>
<tr>
<td class="mdl-data-table__cell--non-numeric col-mimetype-icon"></td>
<th class="mdl-data-table__cell--non-numeric col-display-name">
{{'SEARCH.RESULTS.COLUMNS.NAME' | translate}}
</th>
<th class="mdl-data-table__cell--non-numeric col-modified-by">
{{'SEARCH.RESULTS.COLUMNS.MODIFIED_BY' | translate}}
</th>
<th class="mdl-data-table__cell--non-numeric col-modified-at">
{{'SEARCH.RESULTS.COLUMNS.MODIFIED_AT' | translate}}
</th>
</tr>
</thead>
<tbody>
<div data-automation-id="search_result_table"
class="mdl-data-table mdl-js-data-table mdl-shadow--2dp full-width">
<p data-automation-id="search_error_message" *ngIf="errorMessage">{{ 'SEARCH.RESULTS.ERROR' | translate:{errorMessage: errorMessage} }}</p>
<div class="container">
<alfresco-document-list
[node]="nodeResults"
[contextMenuActions]="true"
[creationMenuActions]="false"
[contentActions]="true"
[navigationMode]="navigationMode"
[enablePagination]="false"
(error)="onNavigationError($event)"
(success)="resetError()"
(preview)="onPreviewFile($event)">
<empty-folder-content>
<template>
<div class="empty_template">
<div class="no-result-message">{{ 'SEARCH.RESULTS.NONE' | translate:{searchTerm: searchTerm} }}</div>
<img [src]="baseComponentPath + '/assets/images/empty_doc_lib.svg'" class="no-result__empty_doc_lib">
</div>
</template>
<template>
</template>
</empty-folder-content>
<tr id="result_row_{{idx}}" tabindex="0" *ngFor="let result of results; let idx = index" (click)="onItemClick(result, $event)" (dblclick)="onItemDblClick(result, $event)" (keyup.enter)="onItemClick(result, $event)">
<td class="col-mimetype-icon"><img src="{{getMimeTypeIcon(result)}}" alt="{{getMimeTypeKey(result)|translate}}" /></td>
<td id="result_name_{{idx}}" class="mdl-data-table__cell--non-numeric col-display-name"
attr.data-automation-id=file_{{result.entry.name}} >{{result.entry.name}}</td>
<td id="result_user_{{idx}}" class="mdl-data-table__cell--non-numeric col-modified-by"
attr.data-automation-id=file_{{result.entry.name}}_{{result.entry.modifiedByUser.displayName}}>{{result.entry.modifiedByUser.displayName}}</td>
<td class="col-modified-at">{{result.entry.modifiedAt | date}}</td>
</tr>
<content-columns>
<content-column key="$thumbnail" type="image"></content-column>
<content-column
title="{{'SEARCH.DOCUMENT_LIST.COLUMNS.DISPLAY_NAME' | translate}}"
key="name"
sortable="true"
class="full-width ellipsis-cell">
</content-column>
<content-column
title="{{'SEARCH.DOCUMENT_LIST.COLUMNS.CREATED_BY' | translate}}"
key="createdByUser.displayName"
sortable="true"
class="desktop-only">
</content-column>
<content-column
title="{{'SEARCH.DOCUMENT_LIST.COLUMNS.CREATED_ON' | translate}}"
key="createdAt"
type="date"
format="medium"
sortable="true"
class="desktop-only">
</content-column>
</content-columns>
</tbody>
</table>
<table id="search_no_result" data-automation-id="search_no_result_found" *ngIf="results && results.length === 0"
class="mdl-data-table mdl-js-data-table mdl-shadow--2dp full-width">
<tbody>
<tr>
<td class="mdl-data-table__cell--non-numeric">{{ 'SEARCH.RESULTS.NONE' | translate:{searchTerm: searchTerm} }}</td>
</tr>
</tbody>
</table>
<p data-automation-id="search_error_message" *ngIf="errorMessage">{{ 'SEARCH.RESULTS.ERROR' | translate:{errorMessage: errorMessage} }}</p>
<content-actions>
<!-- folder actions -->
<content-action
target="folder"
title="{{'SEARCH.DOCUMENT_LIST.ACTIONS.FOLDER.DELETE' | translate}}"
handler="delete">
</content-action>
<!-- document actions -->
<content-action
target="document"
title="{{'SEARCH.DOCUMENT_LIST.ACTIONS.DOCUMENT.DOWNLOAD' | translate}}"
handler="download">
</content-action>
<content-action
target="document"
title="{{'SEARCH.DOCUMENT_LIST.ACTIONS.DOCUMENT.DELETE' | translate}}"
handler="delete">
</content-action>
</content-actions>
</alfresco-document-list>
<alfresco-pagination
(changePageSize)="onChangePageSize($event)"
(nextPage)="onNextPage($event)"
(prevPage)="onPrevPage($event)"
[pagination]="pagination"
[maxItems]="maxResults"
[supportedPageSizes]="[5, 10, 15, 20]">
</alfresco-pagination>
</div>
</div>

View File

@@ -16,12 +16,10 @@
*/
import { DebugElement, ReflectiveInjector, SimpleChange } from '@angular/core';
import { By } from '@angular/platform-browser';
import { ComponentFixture, TestBed, async } from '@angular/core/testing';
import { ActivatedRoute } from '@angular/router';
import { Observable } from 'rxjs/Rx';
import { AlfrescoSearchComponent } from './alfresco-search.component';
import { AlfrescoThumbnailService } from './../services/alfresco-thumbnail.service';
import { TranslationMock } from './../assets/translation.service.mock';
import { AlfrescoSearchService } from '../services/alfresco-search.service';
import {
@@ -33,6 +31,7 @@ import {
StorageService,
LogService
} from 'ng2-alfresco-core';
import { DocumentListModule } from 'ng2-alfresco-documentlist';
describe('AlfrescoSearchComponent', () => {
@@ -69,8 +68,8 @@ describe('AlfrescoSearchComponent', () => {
entry: {
id: '123',
name: 'MyFolder',
isFile : false,
isFolder : true,
isFile: false,
isFolder: true,
createdByUser: {
displayName: 'John Doe'
},
@@ -102,13 +101,13 @@ describe('AlfrescoSearchComponent', () => {
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [
CoreModule.forRoot()
CoreModule.forRoot(),
DocumentListModule.forRoot()
],
declarations: [ AlfrescoSearchComponent ], // declare the test component
declarations: [AlfrescoSearchComponent], // declare the test component
providers: [
AlfrescoSearchService,
{provide: AlfrescoTranslationService, useClass: TranslationMock},
AlfrescoThumbnailService
{provide: AlfrescoTranslationService, useClass: TranslationMock}
]
}).compileComponents().then(() => {
fixture = TestBed.createComponent(AlfrescoSearchComponent);
@@ -123,9 +122,9 @@ describe('AlfrescoSearchComponent', () => {
it('should take the provided search term from query param provided via RouteParams', () => {
let injector = ReflectiveInjector.resolveAndCreate([
{ provide: ActivatedRoute, useValue: { params: Observable.from([{ q: 'exampleTerm692' }]) } }
{provide: ActivatedRoute, useValue: {params: Observable.from([{q: 'exampleTerm692'}])}}
]);
let search = new AlfrescoSearchComponent(null, null, null, injector.get(ActivatedRoute));
let search = new AlfrescoSearchComponent(null, null, injector.get(ActivatedRoute));
search.ngOnInit();
expect(search.searchTerm).toBe('exampleTerm692');
});
@@ -138,9 +137,9 @@ describe('AlfrescoSearchComponent', () => {
AlfrescoApiService,
StorageService,
LogService,
{ provide: ActivatedRoute, useValue: { params: Observable.from([{}]) } }
{provide: ActivatedRoute, useValue: {params: Observable.from([{}])}}
]);
let search = new AlfrescoSearchComponent(injector.get(AlfrescoSearchService), null, null, injector.get(ActivatedRoute));
let search = new AlfrescoSearchComponent(injector.get(AlfrescoSearchService), null, injector.get(ActivatedRoute));
search.ngOnInit();
expect(search.searchTerm).toBeNull();
});
@@ -157,6 +156,7 @@ describe('AlfrescoSearchComponent', () => {
it('should call search service with the correct parameters', (done) => {
let searchTerm = 'searchTerm63688', options = {
include: ['path'],
skipCount: 0,
rootNodeId: '-my-',
nodeType: 'my:type',
maxItems: 20,
@@ -186,9 +186,9 @@ describe('AlfrescoSearchComponent', () => {
component.resultsLoad.subscribe(() => {
fixture.detectChanges();
expect(searchService.getQueryNodesPromise).toHaveBeenCalled();
expect(element.querySelector('#result_user_0')).not.toBeNull();
expect(element.querySelector('#result_user_0').innerHTML).toBe('John Doe');
expect(element.querySelector('#result_name_0').innerHTML).toBe('MyDoc');
let resultsEl = element.querySelector('[data-automation-id="text_MyDoc"]');
expect(resultsEl).not.toBeNull();
expect(resultsEl.innerHTML.trim()).toBe('MyDoc');
done();
});
@@ -204,7 +204,7 @@ describe('AlfrescoSearchComponent', () => {
component.resultsLoad.subscribe(() => {
fixture.detectChanges();
expect(element.querySelector('#search_no_result')).not.toBeNull();
expect(element.querySelector('.no-result-message')).not.toBeNull();
done();
});
@@ -218,11 +218,10 @@ describe('AlfrescoSearchComponent', () => {
spyOn(searchService, 'getQueryNodesPromise')
.and.returnValue(Promise.reject(errorJson));
component.resultsLoad.subscribe(() => {}, () => {
component.resultsLoad.subscribe(() => {
}, () => {
fixture.detectChanges();
let resultsEl = element.querySelector('[data-automation-id="search_result_table"]');
let errorEl = element.querySelector('[data-automation-id="search_error_message"]');
expect(resultsEl).toBeNull();
expect(errorEl).not.toBeNull();
expect((<any>errorEl).innerText).toBe('SEARCH.RESULTS.ERROR');
done();
@@ -240,10 +239,10 @@ describe('AlfrescoSearchComponent', () => {
component.resultsLoad.subscribe(() => {
fixture.detectChanges();
expect(searchService.getQueryNodesPromise.calls.mostRecent().args[0]).toBe('searchTerm2');
expect(element.querySelector('#result_user_0')).not.toBeNull();
expect(element.querySelector('#result_user_0').innerHTML).toBe('John Doe');
expect(element.querySelector('#result_name_0').innerHTML).toBe('MyDoc');
expect(searchService.getQueryNodesPromise).toHaveBeenCalled();
let resultsEl = element.querySelector('[data-automation-id="text_MyDoc"]');
expect(resultsEl).not.toBeNull();
expect(resultsEl.innerHTML.trim()).toBe('MyDoc');
done();
});
@@ -257,13 +256,12 @@ describe('AlfrescoSearchComponent', () => {
let searchService: AlfrescoSearchService;
let querySpy: jasmine.Spy;
let emitSpy: jasmine.Spy;
const rowSelector = '[data-automation-id="search_result_table"] tbody tr';
beforeEach(() => {
debugElement = fixture.debugElement;
searchService = fixture.debugElement.injector.get(AlfrescoSearchService);
querySpy = spyOn(searchService, 'getQueryNodesPromise').and.returnValue(Promise.resolve(result));
emitSpy = spyOn(component.navigate, 'emit');
emitSpy = spyOn(component.preview, 'emit');
});
describe('click results', () => {
@@ -272,12 +270,14 @@ describe('AlfrescoSearchComponent', () => {
component.navigationMode = AlfrescoSearchComponent.SINGLE_CLICK_NAVIGATION;
});
it('should emit navigation event when file item clicked', (done) => {
it('should emit preview event when file item clicked', (done) => {
component.resultsLoad.subscribe(() => {
fixture.detectChanges();
debugElement.query(By.css(rowSelector)).triggerEventHandler('click', {});
expect(emitSpy).toHaveBeenCalled();
let resultsEl = element.querySelector('[data-automation-id="text_MyDoc"]');
resultsEl.dispatchEvent(new Event('click'));
done();
});
@@ -285,14 +285,16 @@ describe('AlfrescoSearchComponent', () => {
component.ngOnInit();
});
it('should emit navigation event when non-file item is clicked', (done) => {
it('should emit preview event when non-file item is clicked', (done) => {
querySpy.and.returnValue(Promise.resolve(folderResult));
component.resultsLoad.subscribe(() => {
fixture.detectChanges();
debugElement.query(By.css(rowSelector)).triggerEventHandler('click', {});
expect(emitSpy).toHaveBeenCalled();
let resultsEl = element.querySelector('[data-automation-id="text_MyFolder"]');
resultsEl.dispatchEvent(new Event('click'));
done();
});
@@ -307,12 +309,14 @@ describe('AlfrescoSearchComponent', () => {
component.navigationMode = AlfrescoSearchComponent.DOUBLE_CLICK_NAVIGATION;
});
it('should emit navigation event when file item clicked', (done) => {
it('should emit preview event when file item clicked', (done) => {
component.resultsLoad.subscribe(() => {
fixture.detectChanges();
debugElement.query(By.css(rowSelector)).triggerEventHandler('dblclick', {});
expect(emitSpy).toHaveBeenCalled();
let resultsEl = element.querySelector('[data-automation-id="text_MyDoc"]');
resultsEl.dispatchEvent(new Event('dblclick'));
done();
});
@@ -320,14 +324,16 @@ describe('AlfrescoSearchComponent', () => {
component.ngOnInit();
});
it('should emit navigation event when non-file item is clicked', (done) => {
it('should emit preview event when non-file item is clicked', (done) => {
querySpy.and.returnValue(Promise.resolve(folderResult));
component.resultsLoad.subscribe(() => {
fixture.detectChanges();
debugElement.query(By.css(rowSelector)).triggerEventHandler('dblclick', {});
expect(emitSpy).toHaveBeenCalled();
let resultsEl = element.querySelector('[data-automation-id="text_MyFolder"]');
resultsEl.dispatchEvent(new Event('dblclick'));
done();
});

View File

@@ -18,9 +18,8 @@
import { Component, EventEmitter, Input, Output, Optional, OnChanges, SimpleChanges, OnInit } from '@angular/core';
import { ActivatedRoute, Params } from '@angular/router';
import { AlfrescoSearchService, SearchOptions } from './../services/alfresco-search.service';
import { AlfrescoThumbnailService } from './../services/alfresco-thumbnail.service';
import { AlfrescoTranslationService } from 'ng2-alfresco-core';
import { MinimalNodeEntity } from 'alfresco-js-api';
import { NodePaging, Pagination } from 'alfresco-js-api';
@Component({
moduleId: module.id,
@@ -51,23 +50,28 @@ export class AlfrescoSearchComponent implements OnChanges, OnInit {
@Input()
navigationMode: string = AlfrescoSearchComponent.DOUBLE_CLICK_NAVIGATION; // click|dblclick
@Output()
navigate: EventEmitter<MinimalNodeEntity> = new EventEmitter<MinimalNodeEntity>();
@Output()
resultsLoad = new EventEmitter();
@Output()
preview: EventEmitter<any> = new EventEmitter<any>();
results: any = null;
pagination: Pagination;
errorMessage;
queryParamName = 'q';
skipCount: number = 0;
nodeResults: NodePaging;
baseComponentPath: string = module.id.replace('/components/alfresco-search.component.js', '');
constructor(private searchService: AlfrescoSearchService,
private translateService: AlfrescoTranslationService,
private thumbnailService: AlfrescoThumbnailService,
@Optional() private route: ActivatedRoute) {
}
@@ -75,6 +79,7 @@ export class AlfrescoSearchComponent implements OnChanges, OnInit {
if (this.translateService !== null) {
this.translateService.addTranslationFolder('ng2-alfresco-search', 'node_modules/ng2-alfresco-search/src');
}
if (this.route) {
this.route.params.forEach((params: Params) => {
this.searchTerm = params.hasOwnProperty(this.queryParamName) ? params[this.queryParamName] : null;
@@ -88,38 +93,16 @@ export class AlfrescoSearchComponent implements OnChanges, OnInit {
ngOnChanges(changes: SimpleChanges) {
if (changes['searchTerm']) {
this.searchTerm = changes['searchTerm'].currentValue;
this.skipCount = 0;
this.displaySearchResults(this.searchTerm);
}
}
/**
* Gets thumbnail URL for the given document node.
* @param node Node to get URL for.
* @returns {string} URL address.
*/
getMimeTypeIcon(node: any): string {
if (node.entry.content && node.entry.content.mimeType) {
let icon = this.thumbnailService.getMimeTypeIcon(node.entry.content.mimeType);
return this.resolveIconPath(icon);
} else if (node.entry.isFolder) {
return `${this.baseComponentPath}/../assets/images/ft_ic_folder.svg`;
}
}
private resolveIconPath(icon: string): string {
return `${this.baseComponentPath}/../assets/images/${icon}`;
}
/**
* Gets thumbnail message key for the given document node, which can be used to look up alt text
* @param node Node to get URL for.
* @returns {string} URL address.
*/
getMimeTypeKey(node: any): string {
if (node.entry.content && node.entry.content.mimeType) {
return 'SEARCH.ICONS.' + this.thumbnailService.getMimeTypeKey(node.entry.content.mimeType);
} else {
return '';
onPreviewFile(event: any) {
if (event.value) {
this.preview.emit({
value: event.value
});
}
}
@@ -131,6 +114,7 @@ export class AlfrescoSearchComponent implements OnChanges, OnInit {
if (searchTerm && this.searchService) {
let searchOpts: SearchOptions = {
include: ['path'],
skipCount: this.skipCount,
rootNodeId: this.rootNodeId,
nodeType: this.resultType,
maxItems: this.maxResults,
@@ -140,33 +124,35 @@ export class AlfrescoSearchComponent implements OnChanges, OnInit {
.getNodeQueryResults(searchTerm, searchOpts)
.subscribe(
results => {
this.nodeResults = results;
this.results = results.list.entries;
this.pagination = results.list.pagination;
this.resultsLoad.emit(this.results);
this.errorMessage = null;
},
error => {
this.results = null;
this.errorMessage = <any>error;
this.resultsLoad.error(error);
if (error.status !== 400) {
this.results = null;
this.errorMessage = <any>error;
this.resultsLoad.error(error);
}
}
);
}
}
onItemClick(node, event?: Event) {
if (this.navigate && this.navigationMode === AlfrescoSearchComponent.SINGLE_CLICK_NAVIGATION) {
if (node && node.entry) {
this.navigate.emit(node);
}
}
public onChangePageSize(event: Pagination): void {
this.maxResults = event.maxItems;
this.displaySearchResults(this.searchTerm);
}
onItemDblClick(node: MinimalNodeEntity) {
if (this.navigate && this.navigationMode === AlfrescoSearchComponent.DOUBLE_CLICK_NAVIGATION) {
if (node && node.entry) {
this.navigate.emit(node);
}
}
public onNextPage(event: Pagination): void {
this.skipCount = event.skipCount;
this.displaySearchResults(this.searchTerm);
}
public onPrevPage(event: Pagination): void {
this.skipCount = event.skipCount;
this.displaySearchResults(this.searchTerm);
}
}

View File

@@ -5,7 +5,7 @@
},
"RESULTS": {
"SUMMARY": "Found {{numResults}} results for {{searchTerm}}",
"NONE": "No results found for {{searchTerm}}",
"NONE": "No results found searching for {{searchTerm}}",
"ERROR": "An error occurred while running the search",
"COLUMNS": {
"NAME": "Display name",
@@ -25,6 +25,22 @@
"ft_ic_archive": "Acrchive file",
"ft_ic_presentation": "Presentation file",
"ft_ic_spreadsheet": "Spreadsheet file"
},
"DOCUMENT_LIST": {
"COLUMNS": {
"DISPLAY_NAME": "Display name",
"CREATED_BY": "Created by",
"CREATED_ON": "Created on"
},
"ACTIONS": {
"FOLDER": {
"DELETE": "Delete"
},
"DOCUMENT": {
"DOWNLOAD": "Download",
"DELETE": "Delete"
}
}
}
}
}

View File

@@ -18,6 +18,7 @@
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs/Rx';
import { AlfrescoAuthenticationService, AlfrescoApiService } from 'ng2-alfresco-core';
import { NodePaging } from 'alfresco-js-api';
/**
* Internal service used by Document List component.
@@ -36,13 +37,13 @@ export class AlfrescoSearchService {
* @param options Additional options passed to the search
* @returns {Observable<NodePaging>} Search results
*/
public getNodeQueryResults(term: string, options?: SearchOptions): Observable<any> {
public getNodeQueryResults(term: string, options?: SearchOptions): Observable<NodePaging> {
return Observable.fromPromise(this.getQueryNodesPromise(term, options))
.map(res => <any> res)
.map(res => <NodePaging> res)
.catch(err => this.handleError(err));
}
private getQueryNodesPromise(term: string, opts: SearchOptions) {
private getQueryNodesPromise(term: string, opts: SearchOptions): Promise<NodePaging> {
return this.apiService.getInstance().core.queriesApi.findNodes(term, opts);
}

View File

@@ -1,7 +1,14 @@
// Tun on full stack traces in errors to help debugging
Error.stackTraceLimit = Infinity;
// jasmine.DEFAULT_TIMEOUT_INTERVAL = 1000;
jasmine.DEFAULT_TIMEOUT_INTERVAL = 100000;
window.componentHandler = {
upgradeAllRegistered: function () {
},
upgradeElement: function () {
}
};
__karma__.loaded = function() {};

View File

@@ -1,7 +1,14 @@
// Tun on full stack traces in errors to help debugging
Error.stackTraceLimit = Infinity;
// jasmine.DEFAULT_TIMEOUT_INTERVAL = 1000;
jasmine.DEFAULT_TIMEOUT_INTERVAL = 100000;
window.componentHandler = {
upgradeAllRegistered: function () {
},
upgradeElement: function () {
}
};
__karma__.loaded = function() {};

View File

@@ -1,7 +1,14 @@
// Tun on full stack traces in errors to help debugging
Error.stackTraceLimit = Infinity;
// jasmine.DEFAULT_TIMEOUT_INTERVAL = 1000;
jasmine.DEFAULT_TIMEOUT_INTERVAL = 100000;
window.componentHandler = {
upgradeAllRegistered: function () {
},
upgradeElement: function () {
}
};
__karma__.loaded = function() {};

View File

@@ -1,7 +1,14 @@
// Tun on full stack traces in errors to help debugging
Error.stackTraceLimit = Infinity;
// jasmine.DEFAULT_TIMEOUT_INTERVAL = 1000;
jasmine.DEFAULT_TIMEOUT_INTERVAL = 100000;
window.componentHandler = {
upgradeAllRegistered: function () {
},
upgradeElement: function () {
}
};
__karma__.loaded = function() {};

View File

@@ -1,7 +1,14 @@
// Tun on full stack traces in errors to help debugging
Error.stackTraceLimit = Infinity;
// jasmine.DEFAULT_TIMEOUT_INTERVAL = 1000;
jasmine.DEFAULT_TIMEOUT_INTERVAL = 100000;
window.componentHandler = {
upgradeAllRegistered: function () {
},
upgradeElement: function () {
}
};
__karma__.loaded = function() {};

View File

@@ -72,10 +72,18 @@ npm link ng2-activiti-diagrams
npm link
npm run build
#LINK SEARCH
echo "====== linking component: ng2-alfresco-search ====="
cd "$DIR/../ng2-components/ng2-alfresco-search"
npm link ng2-alfresco-core
npm link ng2-alfresco-datatable
npm link ng2-alfresco-documentlist
npm link
npm run build
#LINK ALL THE OTHERS COMPONENTS
for PACKAGE in \
ng2-alfresco-login \
ng2-alfresco-search \
ng2-alfresco-userinfo \
ng2-alfresco-upload \
ng2-alfresco-tag \

View File

@@ -71,10 +71,19 @@ npm link ng2-alfresco-core
npm link ng2-activiti-diagrams
npm install
#LINK SEARCH
echo "====== linking component: ng2-alfresco-search ====="
cd "$DIR/../ng2-components/ng2-alfresco-search/demo"
npm link ng2-alfresco-core
npm link ng2-alfresco-datatable
npm link ng2-alfresco-documentlist
npm link ng2-alfresco-search
npm link
npm run build
#LINK ALL THE OTHERS COMPONENTS
for PACKAGE in \
ng2-alfresco-login \
ng2-alfresco-search \
ng2-alfresco-userinfo \
ng2-alfresco-upload \
ng2-alfresco-tag \

View File

@@ -72,10 +72,18 @@ npm link ng2-activiti-diagrams
npm link
npm run build
#LINK SEARCH
echo "====== linking component: ng2-alfresco-search ====="
cd "$DIR/../ng2-components/ng2-alfresco-search"
npm link ng2-alfresco-core
npm link ng2-alfresco-datatable
npm link ng2-alfresco-documentlist
npm link
npm run build
#LINK ALL THE OTHERS COMPONENTS
for PACKAGE in \
ng2-alfresco-login \
ng2-alfresco-search \
ng2-alfresco-userinfo \
ng2-alfresco-upload \
ng2-alfresco-tag \