-
-
+
diff --git a/src/app/components/search/search.component.ts b/src/app/components/search/search.component.ts
index 9a127c429..fdb6c4b82 100644
--- a/src/app/components/search/search.component.ts
+++ b/src/app/components/search/search.component.ts
@@ -23,23 +23,28 @@
* along with Alfresco. If not, see
.
*/
-import { Component, OnInit, Optional, ViewChild } from '@angular/core';
+import { Component, OnInit, ViewChild } from '@angular/core';
import { MinimalNodeEntryEntity, NodePaging, Pagination } from 'alfresco-js-api';
import { Router, ActivatedRoute, Params } from '@angular/router';
-import { SearchQueryBuilderService, SearchComponent as AdfSearchComponent } from '@alfresco/adf-content-services';
-import { SearchConfigurationService } from '@alfresco/adf-core';
+import { SearchQueryBuilderService, SearchComponent as AdfSearchComponent, NodePermissionService } from '@alfresco/adf-content-services';
+import { SearchConfigurationService, UserPreferencesService, SearchService } from '@alfresco/adf-core';
import { PageComponent } from '../page.component';
+import { Store } from '@ngrx/store';
+import { AppStore } from '../../store/states/app.state';
+import { NavigateToLocationAction } from '../../store/actions';
@Component({
selector: 'app-search',
templateUrl: './search.component.html',
- styleUrls: ['./search.component.scss']
+ styleUrls: ['./search.component.scss'],
+ providers: [SearchService]
})
-export class SearchComponent implements OnInit {
+export class SearchComponent extends PageComponent implements OnInit {
@ViewChild('search')
search: AdfSearchComponent;
+ searchedWord: string;
queryParamName = 'q';
data: NodePaging;
totalResults = 0;
@@ -48,10 +53,15 @@ export class SearchComponent implements OnInit {
sorting = ['name', 'asc'];
constructor(
- public router: Router,
+ public permission: NodePermissionService,
private queryBuilder: SearchQueryBuilderService,
private searchConfiguration: SearchConfigurationService,
- @Optional() private route: ActivatedRoute) {
+ store: Store
,
+ router: Router,
+ preferences: UserPreferencesService,
+ route: ActivatedRoute) {
+ super(preferences, router, route, store);
+
queryBuilder.paging = {
skipCount: 0,
maxItems: 25
@@ -59,6 +69,8 @@ export class SearchComponent implements OnInit {
}
ngOnInit() {
+ super.ngOnInit();
+
this.sorting = this.getSorting();
this.queryBuilder.updated.subscribe(() => {
@@ -67,9 +79,9 @@ export class SearchComponent implements OnInit {
if (this.route) {
this.route.params.forEach((params: Params) => {
- const searchedWord = params.hasOwnProperty(this.queryParamName) ? params[this.queryParamName] : null;
- if (searchedWord) {
- const queryBody = this.searchConfiguration.generateQueryBody(searchedWord, 0, 100);
+ this.searchedWord = params.hasOwnProperty(this.queryParamName) ? params[this.queryParamName] : null;
+ if (this.searchedWord) {
+ const queryBody = this.searchConfiguration.generateQueryBody(this.searchedWord, 0, 100);
this.queryBuilder.userQuery = queryBody.query.query;
this.queryBuilder.update();
@@ -112,6 +124,10 @@ export class SearchComponent implements OnInit {
}
onNodeDoubleClick(node: MinimalNodeEntryEntity) {
+ if (node && node.isFolder) {
+ this.store.dispatch(new NavigateToLocationAction(node));
+ }
+
if (node && PageComponent.isLockedNode(node)) {
event.preventDefault();
diff --git a/src/app/directives/download-nodes.directive.ts b/src/app/directives/download-nodes.directive.ts
new file mode 100644
index 000000000..53a790404
--- /dev/null
+++ b/src/app/directives/download-nodes.directive.ts
@@ -0,0 +1,58 @@
+/*!
+ * @license
+ * Alfresco Example Content Application
+ *
+ * Copyright (C) 2005 - 2018 Alfresco Software Limited
+ *
+ * This file is part of the Alfresco Example Content Application.
+ * If the software was purchased under a paid Alfresco license, the terms of
+ * the paid license agreement will prevail. Otherwise, the software is
+ * provided under the following open source license terms:
+ *
+ * The Alfresco Example Content Application is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * The Alfresco Example Content Application is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Alfresco. If not, see .
+ */
+
+import { Directive, HostListener, Input } from '@angular/core';
+import { Store } from '@ngrx/store';
+import { AppStore } from '../store/states/app.state';
+import { MinimalNodeEntity } from '@alfresco/adf-core/node_modules/alfresco-js-api';
+import { DownloadNodesAction } from '../store/actions';
+
+@Directive({
+ selector: '[acaDownloadNodes]'
+})
+export class DownloadNodesDirective {
+ // tslint:disable-next-line:no-input-rename
+ @Input('acaDownloadNodes')
+ nodes: Array | MinimalNodeEntity;
+
+ constructor(private store: Store) {}
+
+ @HostListener('click')
+ onClick() {
+ const targets = Array.isArray(this.nodes) ? this.nodes : [this.nodes];
+ const toDownload = targets.map(node => {
+ const { id, nodeId, name, isFile, isFolder } = node.entry;
+
+ return {
+ id: nodeId || id,
+ name,
+ isFile,
+ isFolder
+ };
+ });
+
+ this.store.dispatch(new DownloadNodesAction(toDownload));
+ }
+}
diff --git a/src/app/store/actions/node.action.ts b/src/app/store/actions/node.action.ts
index e76bd7bc5..4d7cb313a 100644
--- a/src/app/store/actions/node.action.ts
+++ b/src/app/store/actions/node.action.ts
@@ -5,10 +5,13 @@ export const DELETE_NODES = 'DELETE_NODES';
export const UNDO_DELETE_NODES = 'UNDO_DELETE_NODES';
export const RESTORE_DELETED_NODES = 'RESTORE_DELETED_NODES';
export const PURGE_DELETED_NODES = 'PURGE_DELETED_NODES';
+export const DOWNLOAD_NODES = 'DOWNLOAD_NODES';
export interface NodeInfo {
id: string;
name: string;
+ isFile?: boolean;
+ isFolder?: boolean;
}
export class SetSelectedNodesAction implements Action {
@@ -35,3 +38,8 @@ export class PurgeDeletedNodesAction implements Action {
readonly type = PURGE_DELETED_NODES;
constructor(public payload: NodeInfo[] = []) {}
}
+
+export class DownloadNodesAction implements Action {
+ readonly type = DOWNLOAD_NODES;
+ constructor(public payload: NodeInfo[] = []) {}
+}
diff --git a/src/app/store/actions/router.action.ts b/src/app/store/actions/router.action.ts
index 859f4918c..5bedc85af 100644
--- a/src/app/store/actions/router.action.ts
+++ b/src/app/store/actions/router.action.ts
@@ -1,8 +1,14 @@
import { Action } from '@ngrx/store';
export const NAVIGATE_ROUTE = 'NAVIGATE_ROUTE';
+export const NAVIGATE_LOCATION = 'NAVIGATE_LOCATION';
export class NavigateRouteAction implements Action {
readonly type = NAVIGATE_ROUTE;
constructor(public payload: any[]) {}
}
+
+export class NavigateToLocationAction implements Action {
+ readonly type = NAVIGATE_LOCATION;
+ constructor(public payload: any) {}
+}
diff --git a/src/app/store/effects/download.effects.ts b/src/app/store/effects/download.effects.ts
new file mode 100644
index 000000000..224c9afef
--- /dev/null
+++ b/src/app/store/effects/download.effects.ts
@@ -0,0 +1,86 @@
+import { Effect, Actions, ofType } from '@ngrx/effects';
+import { Injectable } from '@angular/core';
+import { map } from 'rxjs/operators';
+import { DownloadNodesAction, DOWNLOAD_NODES } from '../actions';
+import { AlfrescoApiService } from '@alfresco/adf-core';
+import { MatDialog } from '@angular/material';
+import { NodeInfo } from '../actions/node.action';
+import { DownloadZipDialogComponent } from '@alfresco/adf-content-services';
+
+@Injectable()
+export class DownloadEffects {
+ constructor(
+ private actions$: Actions,
+ private apiService: AlfrescoApiService,
+ private dialog: MatDialog
+ ) {}
+
+ @Effect({ dispatch: false })
+ downloadNode$ = this.actions$.pipe(
+ ofType(DOWNLOAD_NODES),
+ map(action => {
+ if (action.payload && action.payload.length > 0) {
+ this.downloadNodes(action.payload);
+ }
+ })
+ );
+
+ private downloadNodes(nodes: Array) {
+ if (!nodes || nodes.length === 0) {
+ return;
+ }
+
+ if (nodes.length === 1) {
+ this.downloadNode(nodes[0]);
+ } else {
+ this.downloadZip(nodes);
+ }
+ }
+
+ private downloadNode(node: NodeInfo) {
+ if (node) {
+ if (node.isFolder) {
+ this.downloadZip([node]);
+ } else {
+ this.downloadFile(node);
+ }
+ }
+ }
+
+ private downloadFile(node: NodeInfo) {
+ if (node) {
+ this.download(
+ this.apiService.contentApi.getContentUrl(node.id, true),
+ node.name
+ );
+ }
+ }
+
+ private downloadZip(nodes: Array) {
+ if (nodes && nodes.length > 0) {
+ const nodeIds = nodes.map(node => node.id);
+
+ this.dialog.open(DownloadZipDialogComponent, {
+ width: '600px',
+ disableClose: true,
+ data: {
+ nodeIds
+ }
+ });
+ }
+ }
+
+ private download(url: string, fileName: string) {
+ if (url && fileName) {
+ const link = document.createElement('a');
+
+ link.style.display = 'none';
+ link.download = fileName;
+ link.href = url;
+
+ document.body.appendChild(link);
+ link.click();
+ document.body.removeChild(link);
+ }
+ }
+}
diff --git a/src/app/store/effects/router.effects.ts b/src/app/store/effects/router.effects.ts
index b5d761cda..08adb84cd 100644
--- a/src/app/store/effects/router.effects.ts
+++ b/src/app/store/effects/router.effects.ts
@@ -1,6 +1,9 @@
import { Effect, Actions, ofType } from '@ngrx/effects';
import { Injectable } from '@angular/core';
-import { NavigateRouteAction, NAVIGATE_ROUTE } from '../actions/router.action';
+import { PathInfoEntity, MinimalNodeEntryEntity } from 'alfresco-js-api';
+import {
+ NavigateRouteAction, NAVIGATE_ROUTE, NavigateToLocationAction, NAVIGATE_LOCATION
+} from '../actions/router.action';
import { map } from 'rxjs/operators';
import { Router } from '@angular/router';
@@ -15,4 +18,43 @@ export class RouterEffects {
this.router.navigate(action.payload);
})
);
+
+ @Effect({ dispatch: false })
+ navigateLocation$ = this.actions$.pipe(
+ ofType(NAVIGATE_LOCATION),
+ map(action => {
+ if (action.payload) {
+ this.navigateToLocation(action.payload);
+ }
+ })
+ );
+
+ private navigateToLocation(node: MinimalNodeEntryEntity) {
+ let link = null;
+ const { path } = node;
+
+ if (path && path.name && path.elements) {
+ const isLibraryPath = this.isLibraryContent(path);
+
+ const parent = path.elements[path.elements.length - 1];
+ const area = isLibraryPath ? '/libraries' : '/personal-files';
+
+ if (!isLibraryPath) {
+ link = [ area, parent.id ];
+ } else {
+ // parent.id could be 'Site' folder or child as 'documentLibrary'
+ link = [ area, (parent.name === 'Sites' ? {} : parent.id) ];
+ }
+ }
+
+ this.router.navigate(link);
+ }
+
+ private isLibraryContent(path: PathInfoEntity): boolean {
+ if (path && path.elements.length >= 2 && path.elements[1].name === 'Sites') {
+ return true;
+ }
+
+ return false;
+ }
}