[ACA-20] Favorite Libraries (#789)

* libraries submenu config

* fix item id

* favorite libraries columns

* libraries submenu text

* columns text

* submenu schema

* submenu routes

* role column component

* favorite libraries list api

* register favorite libraries presets

* favorite libraries list component

* register role column component

* dl custom node list is library route

* empty state message icon

* remove custom id

* merge subscriptions

* adapt to child route

* fix component selector

* revert to favorite library route

* sidenav main links font weight

* libraries children contants

* library expand panel method

* update e2e

* libraries children columns consistency

* isLibrary workaround for custom node list

* update isLibrary evaluator

* update e2e

* lint

* Update src/assets/app.extensions.json

Co-Authored-By: pionnegru <pionnegru@users.noreply.github.com>

* Update src/app/extensions/extension.service.ts

Co-Authored-By: pionnegru <pionnegru@users.noreply.github.com>

* Update src/app/extensions/extension.service.ts

Co-Authored-By: pionnegru <pionnegru@users.noreply.github.com>

* Update src/app/extensions/extension.service.ts

Co-Authored-By: pionnegru <pionnegru@users.noreply.github.com>

* Update src/app/extensions/extension.service.ts

Co-Authored-By: pionnegru <pionnegru@users.noreply.github.com>

* use correct preset

* update e2e

* update page titles values

* find child active link

* fix expected value

* update expected

* role column tests

* check if menu is expanded
This commit is contained in:
Cilibiu Bogdan 2018-11-09 15:53:51 +02:00 committed by Denys Vuika
parent 76fe33d734
commit 4a420cc9f9
25 changed files with 655 additions and 37 deletions

View File

@ -23,7 +23,7 @@
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
import { ElementFinder, ElementArrayFinder, by } from 'protractor';
import { ElementFinder, ElementArrayFinder, by, element } from 'protractor';
import { Menu } from '../menu/menu';
import { Component } from '../component';
import { Utils } from '../../utilities/utils';
@ -33,6 +33,8 @@ export class Sidenav extends Component {
root: 'app-sidenav',
link: '.menu__item',
label: '.item--label',
expansion_panel: ".mat-expansion-panel-header",
expansion_panel_content: ".mat-expansion-panel-body",
activeLink: '.item--active',
newButton: '[data-automation-id="create-button"]'
};
@ -64,6 +66,12 @@ export class Sidenav extends Component {
return className.includes(Sidenav.selectors.activeLink.replace('.', ''));
}
async childIsActiveByLabel(label: string) {
const labelElement = await this.getLinkByLabel(label).element(by.css('span'));
return (await labelElement.getAttribute('class'))
.includes(Sidenav.selectors.activeLink.replace('.', ''));
}
getLink(label: string) {
return this.component.element(by.cssContainingText(Sidenav.selectors.link, label));
}
@ -84,7 +92,24 @@ export class Sidenav extends Component {
return await link.click();
} catch (e){
console.log('---- sidebar navigation catch : ', e);
console.log('---- sidebar navigation catch navigateToLinkByLabel: ', e);
}
}
async expandMenu(label: string) {
try{
if (await element(by.cssContainingText('.mat-expanded', label)).isPresent()) {
return Promise.resolve();
} else {
const link = this.getLinkByLabel(label);
await Utils.waitUntilElementClickable(link);
await link.click();
await element(by.css(Sidenav.selectors.expansion_panel_content)).isPresent();
}
} catch (e) {
console.log('---- sidebar navigation catch expandMenu: ', e);
}
}
}

View File

@ -45,7 +45,7 @@ export const E2E_ROOT_PATH = __dirname;
// Application Routes
export const APP_ROUTES = {
FAVORITES: '/favorites',
FILE_LIBRARIES: '/libraries',
MY_LIBRARIES: '/libraries',
LOGIN: '/login',
LOGOUT: '/logout',
PERSONAL_FILES: '/personal-files',
@ -58,6 +58,8 @@ export const APP_ROUTES = {
export const SIDEBAR_LABELS = {
PERSONAL_FILES: 'Personal Files',
FILE_LIBRARIES: 'File Libraries',
MY_LIBRARIES: 'My Libraries',
FAVORITE_LIBRARIES: 'Favorite Libraries',
SHARED_FILES: 'Shared',
RECENT_FILES: 'Recent Files',
FAVORITES: 'Favorites',
@ -67,7 +69,8 @@ export const SIDEBAR_LABELS = {
// Page titles
export const PAGE_TITLES = {
VIEWER: 'Preview',
SEARCH: 'Search Results'
SEARCH: 'Search Results',
MY_LIBRARIES: 'File Libraries'
};
// Site visibility

View File

@ -53,12 +53,14 @@ export class BrowsingPage extends Page {
async clickFileLibrariesAndWait() {
await this.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FILE_LIBRARIES);
await this.sidenav.expandMenu(SIDEBAR_LABELS.FILE_LIBRARIES);
await this.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.MY_LIBRARIES);
await this.dataTable.waitForHeader();
}
async clickFileLibraries() {
await this.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FILE_LIBRARIES);
await this.sidenav.expandMenu(SIDEBAR_LABELS.FILE_LIBRARIES);
await this.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.MY_LIBRARIES);
}

View File

@ -81,10 +81,12 @@ describe('Page titles', () => {
});
it('File Libraries page - [C217158]', async () => {
const label = SIDEBAR_LABELS.FILE_LIBRARIES;
const parent = SIDEBAR_LABELS.FILE_LIBRARIES;
const label = SIDEBAR_LABELS.MY_LIBRARIES;
await page.sidenav.expandMenu(parent);
await page.sidenav.navigateToLinkByLabel(label);
expect(await browser.getTitle()).toContain(label);
expect(await browser.getTitle()).toContain(PAGE_TITLES.MY_LIBRARIES);
});
it('Shared Files page - [C217159]', async () => {

View File

@ -80,10 +80,10 @@ describe('File Libraries', () => {
});
it('has the correct columns - [C217095]', async () => {
const labels = [ 'Title', 'Visibility' ];
const labels = [ 'Name', 'My Role', 'Visibility' ];
const elements = labels.map(label => dataTable.getColumnHeaderByLabel(label));
expect(await dataTable.getColumnHeaders().count()).toBe(2 + 1, 'Incorrect number of columns');
expect(await dataTable.getColumnHeaders().count()).toBe(3 + 1, 'Incorrect number of columns');
await elements.forEach(async (element, index) => {
expect(await element.isPresent()).toBe(true, `"${labels[index]}" is missing`);
@ -106,7 +106,7 @@ describe('File Libraries', () => {
return row.all(dataTable.cell).map(async cell => await cell.getText());
});
const sitesList = rowCells.reduce((acc, cell) => {
acc[cell[1]] = cell[2].toUpperCase();
acc[cell[1]] = cell[3].toUpperCase();
return acc;
}, {});

View File

@ -45,8 +45,8 @@ describe('Sidebar', () => {
it('navigates to "File Libraries" - [C217150]', async () => {
await page.clickFileLibraries();
expect(await browser.getCurrentUrl()).toContain(APP_ROUTES.FILE_LIBRARIES);
expect(await sidenav.isActiveByLabel(SIDEBAR_LABELS.FILE_LIBRARIES)).toBe(true);
expect(await browser.getCurrentUrl()).toContain(APP_ROUTES.MY_LIBRARIES);
expect(await sidenav.childIsActiveByLabel(SIDEBAR_LABELS.MY_LIBRARIES)).toBe(true);
});
it('navigates to "Personal Files" - [C280409]', async () => {

View File

@ -202,7 +202,35 @@
"children": {
"description": "Navigation children items",
"type": "array",
"items": { "$ref": "#/definitions/navBarLinkRef" },
"items": {
"oneOf": [
{
"type": "object",
"required": [
"id",
"title",
"route"
],
"properties": {
"id": {
"description": "Unique identifier",
"type": "string"
},
"title": {
"description": "Element title",
"type": "string"
},
"route": {
"description": "Route reference identifier",
"type": "string"
}
},
"not": {
"required": ["children"]
}
}
]
},
"minItems": 1
},
"rules": {

View File

@ -44,6 +44,7 @@ import { APP_ROUTES } from './app.routes';
import { FilesComponent } from './components/files/files.component';
import { LibrariesComponent } from './components/libraries/libraries.component';
import { FavoriteLibrariesComponent } from './components/favorite-libraries/favorite-libraries.component';
import { NodeVersionsDialogComponent } from './dialogs/node-versions/node-versions.dialog';
import { LibraryDialogComponent } from './dialogs/library/library.dialog';
@ -107,6 +108,7 @@ import { environment } from '../environments/environment';
AppComponent,
FilesComponent,
LibrariesComponent,
FavoriteLibrariesComponent,
NodeVersionsDialogComponent,
LibraryDialogComponent
],

View File

@ -27,6 +27,7 @@ import { Routes } from '@angular/router';
import { AppLayoutComponent } from './components/layout/app-layout/app-layout.component';
import { FilesComponent } from './components/files/files.component';
import { LibrariesComponent } from './components/libraries/libraries.component';
import { FavoriteLibrariesComponent } from './components/favorite-libraries/favorite-libraries.component';
import { GenericErrorComponent } from './components/common/generic-error/generic-error.component';
import { SearchResultsComponent } from './components/search/search-results/search-results.component';
import { SearchLibrariesResultsComponent } from './components/search/search-libraries-results/search-libraries-results.component';
@ -105,6 +106,14 @@ export const APP_ROUTES: Routes = [
}
]
},
{
path: 'favorite/libraries',
component: FavoriteLibrariesComponent,
data: {
title: 'APP.BROWSE.LIBRARIES.MENU.FAVORITE_LIBRARIES.TITLE',
sortingPreferenceKey: 'favorite-libraries'
}
},
{
path: 'personal-files',
data: {

View File

@ -31,6 +31,7 @@ import { LocationLinkComponent } from './location-link/location-link.component';
import { NameColumnComponent } from './name-column/name-column.component';
import { LibraryNameColumnComponent } from './library-name-column/library-name-column.component';
import { LibraryStatusColumnComponent } from './library-status-column/library-status-column.component';
import { LibraryRoleColumnComponent } from './library-role-column/library-role-column.component';
import { TrashcanNameColumnComponent } from './trashcan-name-column/trashcan-name-column.component';
import { DynamicColumnComponent } from './dynamic-column/dynamic-column.component';
@ -42,6 +43,7 @@ import { DynamicColumnComponent } from './dynamic-column/dynamic-column.componen
NameColumnComponent,
LibraryNameColumnComponent,
LibraryStatusColumnComponent,
LibraryRoleColumnComponent,
TrashcanNameColumnComponent,
DynamicColumnComponent
],
@ -51,6 +53,7 @@ import { DynamicColumnComponent } from './dynamic-column/dynamic-column.componen
NameColumnComponent,
LibraryNameColumnComponent,
LibraryStatusColumnComponent,
LibraryRoleColumnComponent,
TrashcanNameColumnComponent,
DynamicColumnComponent
],
@ -59,6 +62,7 @@ import { DynamicColumnComponent } from './dynamic-column/dynamic-column.componen
NameColumnComponent,
LibraryNameColumnComponent,
LibraryStatusColumnComponent,
LibraryRoleColumnComponent,
TrashcanNameColumnComponent
]
})

View File

@ -0,0 +1,83 @@
/*!
* @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 <http://www.gnu.org/licenses/>.
*/
import { LibraryRoleColumnComponent } from './library-role-column.component';
import { TestBed, ComponentFixture } from '@angular/core/testing';
import { AppTestingModule } from '../../../testing/app-testing.module';
import { NO_ERRORS_SCHEMA } from '@angular/core';
describe('LibraryNameColumnComponent', () => {
let fixture: ComponentFixture<LibraryRoleColumnComponent>;
let component: LibraryRoleColumnComponent;
beforeEach(() => {
TestBed.configureTestingModule({
imports: [AppTestingModule],
declarations: [LibraryRoleColumnComponent],
schemas: [NO_ERRORS_SCHEMA]
});
fixture = TestBed.createComponent(LibraryRoleColumnComponent);
component = fixture.componentInstance;
});
it('should render Manager', () => {
component.context = { row: { node: { entry: { role: 'SiteManager' } } } };
fixture.detectChanges();
expect(component.displayText).toBe('APP.SITES_ROLE.MANAGER');
});
it('should render Collaborator', () => {
component.context = {
row: { node: { entry: { role: 'SiteCollaborator' } } }
};
fixture.detectChanges();
expect(component.displayText).toBe('APP.SITES_ROLE.COLLABORATOR');
});
it('should render Contributor', () => {
component.context = {
row: { node: { entry: { role: 'SiteContributor' } } }
};
fixture.detectChanges();
expect(component.displayText).toBe('APP.SITES_ROLE.CONTRIBUTOR');
});
it('should render Consumer', () => {
component.context = {
row: { node: { entry: { role: 'SiteConsumer' } } }
};
fixture.detectChanges();
expect(component.displayText).toBe('APP.SITES_ROLE.CONSUMER');
});
it('should not render text for unknown', () => {
component.context = {
row: { node: { entry: { role: 'ROLE' } } }
};
fixture.detectChanges();
expect(component.displayText).toBe('');
});
});

View File

@ -0,0 +1,65 @@
/*!
* @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 <http://www.gnu.org/licenses/>.
*/
import { Component, OnInit, Input } from '@angular/core';
@Component({
selector: 'app-library-role-column',
template: `
<span title="{{ displayText | translate }}">
{{ displayText | translate }}
</span>
`
})
export class LibraryRoleColumnComponent implements OnInit {
@Input()
context: any;
displayText: string;
ngOnInit() {
const node = this.context.row.node;
if (node && node.entry) {
const role: string = node.entry.role;
switch (role) {
case 'SiteManager':
this.displayText = 'APP.SITES_ROLE.MANAGER';
break;
case 'SiteCollaborator':
this.displayText = 'APP.SITES_ROLE.COLLABORATOR';
break;
case 'SiteContributor':
this.displayText = 'APP.SITES_ROLE.CONTRIBUTOR';
break;
case 'SiteConsumer':
this.displayText = 'APP.SITES_ROLE.CONSUMER';
break;
default:
this.displayText = '';
break;
}
}
}
}

View File

@ -0,0 +1,60 @@
<app-page-layout>
<app-page-layout-header>
<adf-breadcrumb root="APP.BROWSE.LIBRARIES.MENU.FAVORITE_LIBRARIES.TITLE">
</adf-breadcrumb>
<adf-toolbar class="inline">
<app-document-display-mode *ifExperimental="'cardview'"></app-document-display-mode>
<ng-container *ngFor="let entry of actions; trackBy: trackByActionId">
<aca-toolbar-action [actionRef]="entry"></aca-toolbar-action>
</ng-container>
</adf-toolbar>
</app-page-layout-header>
<app-page-layout-content>
<div class="main-content">
<adf-document-list #documentList acaDocumentList [display]="documentDisplayMode$ | async" [node]="favoriteList"
[loading]="dataIsLoading" selectionMode="single" [navigate]="false" [sorting]="[ 'title', 'asc' ]"
(node-dblclick)="navigateTo($event.detail?.node)" (name-click)="navigateTo($event.detail?.node)">
<empty-folder-content>
<ng-template>
<adf-empty-content icon="library_books" [title]="'APP.BROWSE.LIBRARIES.EMPTY_STATE.FAVORITE_LIBRARIES.TITLE'"
subtitle="APP.BROWSE.LIBRARIES.EMPTY_STATE.FAVORITE_LIBRARIES.TEXT">
</adf-empty-content>
</ng-template>
</empty-folder-content>
<data-columns>
<ng-container *ngFor="let column of columns; trackBy: trackById">
<ng-container *ngIf="column.template && !(column.desktopOnly && isSmallScreen)">
<data-column [key]="column.key" [title]="column.title" [type]="column.type" [format]="column.format"
[class]="column.class" [sortable]="column.sortable">
<ng-template let-context>
<app-dynamic-column [id]="column.template" [context]="context">
</app-dynamic-column>
</ng-template>
</data-column>
</ng-container>
<ng-container *ngIf="!column.template && !(column.desktopOnly && isSmallScreen)">
<data-column [key]="column.key" [title]="column.title" [type]="column.type" [format]="column.format"
[class]="column.class" [sortable]="column.sortable">
</data-column>
</ng-container>
</ng-container>
</data-columns>
</adf-document-list>
<adf-pagination acaPagination [target]="documentList">
</adf-pagination>
</div>
<div class="sidebar" *ngIf="infoDrawerOpened$ | async">
<aca-info-drawer [node]="selection.last"></aca-info-drawer>
</div>
</app-page-layout-content>
</app-page-layout>

View File

@ -0,0 +1,124 @@
/*!
* @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 <http://www.gnu.org/licenses/>.
*/
import { TestBed, ComponentFixture, async } from '@angular/core/testing';
import { NO_ERRORS_SCHEMA } from '@angular/core';
import { Router } from '@angular/router';
import {
AlfrescoApiService,
TimeAgoPipe,
NodeNameTooltipPipe,
NodeFavoriteDirective,
DataTableComponent,
AppConfigPipe
} from '@alfresco/adf-core';
import { DocumentListComponent } from '@alfresco/adf-content-services';
import { FavoriteLibrariesComponent } from './favorite-libraries.component';
import { AppTestingModule } from '../../testing/app-testing.module';
import { ContentApiService } from '../../services/content-api.service';
import { ExperimentalDirective } from '../../directives/experimental.directive';
import { EffectsModule } from '@ngrx/effects';
import { LibraryEffects } from '../../store/effects';
import { of } from 'rxjs';
describe('LibrariesComponent', () => {
let fixture: ComponentFixture<FavoriteLibrariesComponent>;
let component: FavoriteLibrariesComponent;
let alfrescoApi: AlfrescoApiService;
let contentApiService: ContentApiService;
let router: Router;
let page;
beforeEach(() => {
page = {
list: {
entries: [{ entry: { id: 1 } }, { entry: { id: 2 } }],
pagination: { data: 'data' }
}
};
});
beforeEach(() => {
TestBed.configureTestingModule({
imports: [AppTestingModule, EffectsModule.forRoot([LibraryEffects])],
declarations: [
DataTableComponent,
TimeAgoPipe,
NodeNameTooltipPipe,
NodeFavoriteDirective,
DocumentListComponent,
FavoriteLibrariesComponent,
AppConfigPipe,
ExperimentalDirective
],
schemas: [NO_ERRORS_SCHEMA]
});
fixture = TestBed.createComponent(FavoriteLibrariesComponent);
component = fixture.componentInstance;
alfrescoApi = TestBed.get(AlfrescoApiService);
contentApiService = TestBed.get(ContentApiService);
alfrescoApi.reset();
router = TestBed.get(Router);
spyOn(contentApiService, 'getNode').and.returnValue(
of({ entry: { id: 'libraryId' } })
);
});
describe('Favorite libraries data', () => {
it('should initialise with default data', () => {
expect(component.node).toBe(undefined);
expect(component.dataIsLoading).toBe(true);
});
it('should get data on initialization', async(() => {
spyOn(contentApiService, 'getFavoriteLibraries').and.returnValue(
of(page)
);
fixture.autoDetectChanges();
expect(component.favoriteList).toEqual(page);
expect(component.dataIsLoading).toBe(false);
}));
});
describe('Node navigation', () => {
it('does not navigate when id is not passed', () => {
spyOn(router, 'navigate').and.stub();
component.navigateTo(null);
expect(router.navigate).not.toHaveBeenCalled();
});
it('does not navigate when id is not passed', () => {
spyOn(router, 'navigate').and.stub();
component.navigateTo({ entry: { guid: 'guid' } });
expect(router.navigate).toHaveBeenCalledWith(['libraries', 'libraryId']);
});
});
});

View File

@ -0,0 +1,88 @@
/*!
* @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 <http://www.gnu.org/licenses/>.
*/
import { BreakpointObserver, Breakpoints } from '@angular/cdk/layout';
import { Component, OnInit } from '@angular/core';
import { Store } from '@ngrx/store';
import { SiteEntry, FavoritePaging } from 'alfresco-js-api';
import { AppExtensionService } from '../../extensions/extension.service';
import { ContentManagementService } from '../../services/content-management.service';
import { ContentApiService } from '../../services/content-api.service';
import { NavigateLibraryAction } from '../../store/actions';
import { AppStore } from '../../store/states/app.state';
import { PageComponent } from '../page.component';
@Component({
templateUrl: './favorite-libraries.component.html'
})
export class FavoriteLibrariesComponent extends PageComponent
implements OnInit {
favoriteList: FavoritePaging;
dataIsLoading = true;
isSmallScreen = false;
columns: any[] = [];
constructor(
content: ContentManagementService,
store: Store<AppStore>,
extensions: AppExtensionService,
private contentApiService: ContentApiService,
private breakpointObserver: BreakpointObserver
) {
super(store, extensions, content);
}
ngOnInit() {
super.ngOnInit();
this.contentApiService.getFavoriteLibraries().subscribe(
(favoriteLibraries: FavoritePaging) => {
this.favoriteList = favoriteLibraries;
this.dataIsLoading = false;
},
() => {
this.favoriteList = null;
this.dataIsLoading = false;
}
);
this.subscriptions = this.subscriptions.concat([
this.content.libraryDeleted.subscribe(() => this.reload()),
this.content.libraryUpdated.subscribe(() => this.documentList.reload()),
this.breakpointObserver
.observe([Breakpoints.HandsetPortrait, Breakpoints.HandsetLandscape])
.subscribe(result => {
this.isSmallScreen = result.matches;
})
]);
this.columns = this.extensions.documentListPresets.favoriteLibraries || [];
}
navigateTo(node: SiteEntry) {
if (node && node.entry && node.entry.guid) {
this.store.dispatch(new NavigateLibraryAction(node.entry.guid));
}
}
}

View File

@ -26,9 +26,9 @@
<empty-folder-content>
<ng-template>
<adf-empty-content
icon="group_work"
[title]="'APP.BROWSE.LIBRARIES.EMPTY_STATE.TITLE'"
subtitle="APP.BROWSE.LIBRARIES.EMPTY_STATE.TEXT">
icon="library_books"
[title]="'APP.BROWSE.LIBRARIES.EMPTY_STATE.FILE_LIBRARIES.TITLE'"
subtitle="APP.BROWSE.LIBRARIES.EMPTY_STATE.FILE_LIBRARIES.TEXT">
</adf-empty-content>
</ng-template>
</empty-folder-content>

View File

@ -55,6 +55,7 @@ export class LibrariesComponent extends PageComponent implements OnInit {
this.subscriptions.push(
this.content.libraryDeleted.subscribe(() => this.reload()),
this.content.libraryUpdated.subscribe(() => this.documentList.reload()),
this.breakpointObserver
.observe([Breakpoints.HandsetPortrait, Breakpoints.HandsetLandscape])
@ -63,10 +64,6 @@ export class LibrariesComponent extends PageComponent implements OnInit {
})
);
this.subscriptions = this.subscriptions.concat([
this.content.libraryUpdated.subscribe(() => this.documentList.reload())
]);
this.columns = this.extensions.documentListPresets.libraries || [];
}

View File

@ -27,7 +27,7 @@
</button>
<span #rippleTrigger
class="item--label"
class="item--label item--parent"
[routerLink]="item.url"
[attr.aria-label]="item.title | translate"
[ngClass]="{
@ -40,10 +40,10 @@
<ng-container *ngIf="item.children && item.children.length">
<mat-expansion-panel [expanded]="routerLink.isActive" [@.disabled]="true">
<mat-expansion-panel-header expandedHeight="48px" collapsedHeight="48px" [id]="item.id">
<mat-expansion-panel-header expandedHeight="48px" collapsedHeight="48px">
<mat-panel-title [attr.title]="item.description | translate">
<mat-icon [color]="routerLink.isActive? 'accent': 'primary'">{{ item.icon }}</mat-icon>
<span class="item--label"
<span class="item--label item--parent"
[ngClass]="{
'item--active': routerLink.isActive,
'item--default': !routerLink.isActive

View File

@ -36,6 +36,10 @@
height: 24px;
}
.item--parent {
font-weight: 600;
}
.item--label {
cursor: pointer;
width: 240px;

View File

@ -25,7 +25,7 @@
import { Directive, OnDestroy, OnInit, HostListener } from '@angular/core';
import { DocumentListComponent } from '@alfresco/adf-content-services';
import { ActivatedRoute } from '@angular/router';
import { ActivatedRoute, Router } from '@angular/router';
import { UserPreferencesService } from '@alfresco/adf-core';
import { Subscription } from 'rxjs';
import { Store } from '@ngrx/store';
@ -48,13 +48,17 @@ export class DocumentListDirective implements OnInit, OnDestroy {
private store: Store<AppStore>,
private documentList: DocumentListComponent,
private preferences: UserPreferencesService,
private route: ActivatedRoute
private route: ActivatedRoute,
private router: Router
) {}
ngOnInit() {
this.documentList.includeFields = ['isFavorite', 'aspectNames'];
this.documentList.allowDropFiles = false;
this.isLibrary = this.documentList.currentFolderId === '-mysites-';
this.isLibrary =
this.documentList.currentFolderId === '-mysites-' ||
// workaround for custom node list
this.router.url.endsWith('/libraries');
if (this.sortingPreferenceKey) {
const current = this.documentList.sorting;

View File

@ -42,6 +42,7 @@ import { ExtensionsModule, ExtensionService } from '@alfresco/adf-extensions';
import { AppAuthGuard } from '../guards/auth.guard';
import { NameColumnComponent } from '../components/common/name-column/name-column.component';
import { LibraryNameColumnComponent } from '../components/common/library-name-column/library-name-column.component';
import { LibraryRoleColumnComponent } from '../components/common/library-role-column/library-role-column.component';
import { LibraryStatusColumnComponent } from '../components/common/library-status-column/library-status-column.component';
import { TrashcanNameColumnComponent } from '../components/common/trashcan-name-column/trashcan-name-column.component';
import { LocationLinkComponent } from '../components/common/location-link/location-link.component';
@ -88,6 +89,7 @@ export class CoreExtensionsModule {
'app.shared-link.toggleSharedLink': ToggleSharedComponent,
'app.columns.name': NameColumnComponent,
'app.columns.libraryName': LibraryNameColumnComponent,
'app.columns.libraryRole': LibraryRoleColumnComponent,
'app.columns.libraryStatus': LibraryStatusColumnComponent,
'app.columns.trashcanName': TrashcanNameColumnComponent,
'app.columns.location': LocationLinkComponent

View File

@ -75,6 +75,7 @@ export class AppExtensionService implements RuleContext {
documentListPresets: {
files: Array<DocumentListPresetRef>;
libraries: Array<DocumentListPresetRef>;
favoriteLibraries: Array<DocumentListPresetRef>;
shared: Array<DocumentListPresetRef>;
recent: Array<DocumentListPresetRef>;
favorites: Array<DocumentListPresetRef>;
@ -83,6 +84,7 @@ export class AppExtensionService implements RuleContext {
} = {
files: [],
libraries: [],
favoriteLibraries: [],
shared: [],
recent: [],
favorites: [],
@ -162,6 +164,10 @@ export class AppExtensionService implements RuleContext {
this.documentListPresets = {
files: this.getDocumentListPreset(config, 'files'),
libraries: this.getDocumentListPreset(config, 'libraries'),
favoriteLibraries: this.getDocumentListPreset(
config,
'favoriteLibraries'
),
shared: this.getDocumentListPreset(config, 'shared'),
recent: this.getDocumentListPreset(config, 'recent'),
favorites: this.getDocumentListPreset(config, 'favorites'),

View File

@ -42,6 +42,7 @@ import {
SiteEntry,
FavoriteBody
} from 'alfresco-js-api';
import { map } from 'rxjs/operators';
@Injectable({
providedIn: 'root'
@ -185,6 +186,24 @@ export class ContentApiService {
return from(this.api.favoritesApi.getFavorites(personId, opts));
}
getFavoriteLibraries(personId: string = '-me-'): Observable<FavoritePaging> {
return this.getFavorites(personId, { where: '(EXISTS(target/site))' }).pipe(
map((response: FavoritePaging) => {
return {
list: {
entries: response.list.entries.map(({ entry }: any) => {
entry.target.site.createdAt = entry.createdAt;
return {
entry: entry.target.site
};
}),
pagination: response.list.pagination
}
};
})
);
}
findSharedLinks(opts?: any): Observable<SharedLinkPaging> {
return from(this.api.sharedLinksApi.findSharedLinks(opts));
}

View File

@ -243,12 +243,27 @@
"route": "personal-files"
},
{
"id": "app.navbar.libraries",
"id": "app.navbar.libraries.menu",
"order": 200,
"icon": "group_work",
"icon": "library_books",
"title": "APP.BROWSE.LIBRARIES.SIDENAV_LINK.LABEL",
"description": "APP.BROWSE.LIBRARIES.SIDENAV_LINK.TOOLTIP",
"route": "libraries"
"children": [
{
"id": "app.navbar.libraries.files",
"order": 100,
"title": "APP.BROWSE.LIBRARIES.MENU.FILE_LIBRARIES.SIDENAV_LINK.LABEL",
"description": "APP.BROWSE.LIBRARIES.MENU.FILE_LIBRARIES.SIDENAV_LINK.TOOLTIP",
"route": "libraries"
},
{
"id": "app.navbar.libraries.favorite",
"order": 200,
"title": "APP.BROWSE.LIBRARIES.MENU.FAVORITE_LIBRARIES.SIDENAV_LINK.LABEL",
"description": "APP.BROWSE.LIBRARIES.MENU.FAVORITE_LIBRARIES.SIDENAV_LINK.TOOLTIP",
"route": "favorite/libraries"
}
]
}
]
},
@ -863,7 +878,7 @@
}
},
{
"id": "test",
"id": "app.sidebar.library.properties",
"order": 500,
"title": "APP.INFO_DRAWER.TABS.LIBRARY_PROPERTIES",
"component": "app.components.tabs.library.metadata",
@ -962,13 +977,22 @@
{
"id": "app.libraries.name",
"key": "title",
"title": "APP.DOCUMENT_LIST.COLUMNS.TITLE",
"title": "APP.DOCUMENT_LIST.COLUMNS.NAME",
"type": "text",
"class": "adf-data-table-cell--ellipsis__name",
"sortable": true,
"template": "app.columns.libraryName",
"desktopOnly": false
},
{
"id": "app.libraries.role",
"key": "target.site.role",
"title": "APP.DOCUMENT_LIST.COLUMNS.ROLE",
"type": "text",
"sortable": true,
"template": "app.columns.libraryRole",
"desktopOnly": false
},
{
"id": "app.libraries.visibility",
"key": "visibility",
@ -979,6 +1003,44 @@
"desktopOnly": true
}
],
"favoriteLibraries": [
{
"id": "app.favorite.libraries.thumbnail",
"key": "$thumbnail",
"type": "image",
"class": "image-table-cell",
"sortable": false,
"desktopOnly": false
},
{
"id": "app.favorite.libraries.name",
"key": "title",
"title": "APP.DOCUMENT_LIST.COLUMNS.NAME",
"type": "text",
"class": "adf-data-table-cell--ellipsis__name",
"sortable": true,
"template": "app.columns.libraryName",
"desktopOnly": false
},
{
"id": "app.favorite.libraries.role",
"key": "target.site.role",
"title": "APP.DOCUMENT_LIST.COLUMNS.ROLE",
"type": "text",
"sortable": true,
"template": "app.columns.libraryRole",
"desktopOnly": false
},
{
"id": "app.favorite.libraries.visibility",
"key": "visibility",
"title": "APP.DOCUMENT_LIST.COLUMNS.VISIBILITY",
"type": "text",
"sortable": true,
"template": "app.columns.libraryStatus",
"desktopOnly": true
}
],
"shared": [
{
"id": "app.shared.thumbnail",

View File

@ -45,13 +45,36 @@
},
"LIBRARIES": {
"TITLE": "File Libraries",
"DESCRIPTION": "Access File Libraries",
"SIDENAV_LINK": {
"LABEL": "File Libraries",
"TOOLTIP": "Access File Libraries"
"TOOLTIP": "File Libraries"
},
"EMPTY_STATE": {
"TITLE": "You aren't a member of any File Libraries yet",
"TEXT": "Join libraries to upload, view, and share files."
"FILE_LIBRARIES": {
"TITLE": "You aren't a member of any File Libraries yet",
"TEXT": "Join libraries to upload, view, and share files."
},
"FAVORITE_LIBRARIES": {
"TITLE": "No Favorite Libraries",
"TEXT": "Favorite a library that you want to find easily later."
}
},
"MENU": {
"FILE_LIBRARIES": {
"TITLE": "My Libraries",
"SIDENAV_LINK": {
"LABEL": "My Libraries",
"TOOLTIP": "Access My Libraries"
}
},
"FAVORITE_LIBRARIES": {
"TITLE": "Favorite Libraries",
"SIDENAV_LINK": {
"LABEL": "Favorite Libraries",
"TOOLTIP": "Access Favorited Libraries"
}
}
}
},
"SHARED": {
@ -150,7 +173,6 @@
"DOCUMENT_LIST": {
"COLUMNS": {
"ID": "ID",
"ROLE": "Role",
"NAME": "Name",
"SIZE": "Size",
"MODIFIED_ON": "Modified",
@ -160,7 +182,8 @@
"LOCATION": "Location",
"SHARED_BY": "Shared by",
"DELETED_ON": "Deleted",
"DELETED_BY": "Deleted by"
"DELETED_BY": "Deleted by",
"ROLE": "My Role"
},
"TOOLBAR": {
"CARDVIEW": "Card view mode",
@ -172,6 +195,12 @@
"MODERATED": "Moderated",
"PRIVATE": "Private"
},
"SITES_ROLE": {
"MANAGER": "Manager",
"COLLABORATOR": "Collaborator",
"CONTRIBUTOR": "Contributor",
"CONSUMER": "Consumer"
},
"MESSAGES": {
"ERRORS":{
"MISSING_CONTENT": "This file or folder no longer exists or you don't have permission to view it.",