diff --git a/e2e/components/sidenav/sidenav.ts b/e2e/components/sidenav/sidenav.ts
index 47f1856de..ad185db1f 100755
--- a/e2e/components/sidenav/sidenav.ts
+++ b/e2e/components/sidenav/sidenav.ts
@@ -23,7 +23,7 @@
* along with Alfresco. If not, see .
*/
-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);
}
}
}
diff --git a/e2e/configs.ts b/e2e/configs.ts
index acb00f33c..fad88fc7a 100755
--- a/e2e/configs.ts
+++ b/e2e/configs.ts
@@ -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
diff --git a/e2e/pages/browsing-page.ts b/e2e/pages/browsing-page.ts
index 501f7c126..c70deeb1d 100755
--- a/e2e/pages/browsing-page.ts
+++ b/e2e/pages/browsing-page.ts
@@ -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);
}
diff --git a/e2e/suites/application/page-titles.test.ts b/e2e/suites/application/page-titles.test.ts
index 8b740413e..3caee7a3c 100755
--- a/e2e/suites/application/page-titles.test.ts
+++ b/e2e/suites/application/page-titles.test.ts
@@ -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 () => {
diff --git a/e2e/suites/list-views/file-libraries.test.ts b/e2e/suites/list-views/file-libraries.test.ts
index 7d8c3dc37..a329c61c0 100755
--- a/e2e/suites/list-views/file-libraries.test.ts
+++ b/e2e/suites/list-views/file-libraries.test.ts
@@ -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;
}, {});
diff --git a/e2e/suites/navigation/sidebar.test.ts b/e2e/suites/navigation/sidebar.test.ts
index 52a4921f5..17a80605b 100755
--- a/e2e/suites/navigation/sidebar.test.ts
+++ b/e2e/suites/navigation/sidebar.test.ts
@@ -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 () => {
diff --git a/extension.schema.json b/extension.schema.json
index a2f928797..d5e3a1b23 100644
--- a/extension.schema.json
+++ b/extension.schema.json
@@ -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": {
diff --git a/src/app/app.module.ts b/src/app/app.module.ts
index 0c8c9583d..0a0020360 100644
--- a/src/app/app.module.ts
+++ b/src/app/app.module.ts
@@ -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
],
diff --git a/src/app/app.routes.ts b/src/app/app.routes.ts
index 274b7323a..fe7451326 100644
--- a/src/app/app.routes.ts
+++ b/src/app/app.routes.ts
@@ -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: {
diff --git a/src/app/components/common/common.module.ts b/src/app/components/common/common.module.ts
index 9261d681c..2611524dc 100644
--- a/src/app/components/common/common.module.ts
+++ b/src/app/components/common/common.module.ts
@@ -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
]
})
diff --git a/src/app/components/common/library-role-column/library-role-column.component.spec.ts b/src/app/components/common/library-role-column/library-role-column.component.spec.ts
new file mode 100644
index 000000000..5219430d0
--- /dev/null
+++ b/src/app/components/common/library-role-column/library-role-column.component.spec.ts
@@ -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 .
+ */
+
+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;
+ 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('');
+ });
+});
diff --git a/src/app/components/common/library-role-column/library-role-column.component.ts b/src/app/components/common/library-role-column/library-role-column.component.ts
new file mode 100644
index 000000000..194b2b12e
--- /dev/null
+++ b/src/app/components/common/library-role-column/library-role-column.component.ts
@@ -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 .
+ */
+
+import { Component, OnInit, Input } from '@angular/core';
+
+@Component({
+ selector: 'app-library-role-column',
+ template: `
+
+ {{ displayText | translate }}
+
+ `
+})
+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;
+ }
+ }
+ }
+}
diff --git a/src/app/components/favorite-libraries/favorite-libraries.component.html b/src/app/components/favorite-libraries/favorite-libraries.component.html
new file mode 100644
index 000000000..505ada1a4
--- /dev/null
+++ b/src/app/components/favorite-libraries/favorite-libraries.component.html
@@ -0,0 +1,60 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/app/components/favorite-libraries/favorite-libraries.component.spec.ts b/src/app/components/favorite-libraries/favorite-libraries.component.spec.ts
new file mode 100644
index 000000000..c6323d494
--- /dev/null
+++ b/src/app/components/favorite-libraries/favorite-libraries.component.spec.ts
@@ -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 .
+ */
+
+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;
+ 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']);
+ });
+ });
+});
diff --git a/src/app/components/favorite-libraries/favorite-libraries.component.ts b/src/app/components/favorite-libraries/favorite-libraries.component.ts
new file mode 100644
index 000000000..77b6bc09b
--- /dev/null
+++ b/src/app/components/favorite-libraries/favorite-libraries.component.ts
@@ -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 .
+ */
+
+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,
+ 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));
+ }
+ }
+}
diff --git a/src/app/components/libraries/libraries.component.html b/src/app/components/libraries/libraries.component.html
index 158a23441..80fa77bd5 100644
--- a/src/app/components/libraries/libraries.component.html
+++ b/src/app/components/libraries/libraries.component.html
@@ -26,9 +26,9 @@
+ icon="library_books"
+ [title]="'APP.BROWSE.LIBRARIES.EMPTY_STATE.FILE_LIBRARIES.TITLE'"
+ subtitle="APP.BROWSE.LIBRARIES.EMPTY_STATE.FILE_LIBRARIES.TEXT">
diff --git a/src/app/components/libraries/libraries.component.ts b/src/app/components/libraries/libraries.component.ts
index 35e0fbbdc..143d1ad07 100644
--- a/src/app/components/libraries/libraries.component.ts
+++ b/src/app/components/libraries/libraries.component.ts
@@ -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 || [];
}
diff --git a/src/app/components/sidenav/sidenav.component.html b/src/app/components/sidenav/sidenav.component.html
index 5cf1b6ef6..a8681c32a 100644
--- a/src/app/components/sidenav/sidenav.component.html
+++ b/src/app/components/sidenav/sidenav.component.html
@@ -27,7 +27,7 @@
-
+
{{ item.icon }}
- ,
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;
diff --git a/src/app/extensions/core.extensions.module.ts b/src/app/extensions/core.extensions.module.ts
index 219ba2a03..08ed74b84 100644
--- a/src/app/extensions/core.extensions.module.ts
+++ b/src/app/extensions/core.extensions.module.ts
@@ -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
diff --git a/src/app/extensions/extension.service.ts b/src/app/extensions/extension.service.ts
index d473a5e87..5df217a87 100644
--- a/src/app/extensions/extension.service.ts
+++ b/src/app/extensions/extension.service.ts
@@ -75,6 +75,7 @@ export class AppExtensionService implements RuleContext {
documentListPresets: {
files: Array;
libraries: Array;
+ favoriteLibraries: Array;
shared: Array;
recent: Array;
favorites: Array;
@@ -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'),
diff --git a/src/app/services/content-api.service.ts b/src/app/services/content-api.service.ts
index edf699836..323be86a9 100644
--- a/src/app/services/content-api.service.ts
+++ b/src/app/services/content-api.service.ts
@@ -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 {
+ 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 {
return from(this.api.sharedLinksApi.findSharedLinks(opts));
}
diff --git a/src/assets/app.extensions.json b/src/assets/app.extensions.json
index 30387cb46..c4e01fec9 100644
--- a/src/assets/app.extensions.json
+++ b/src/assets/app.extensions.json
@@ -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",
diff --git a/src/assets/i18n/en.json b/src/assets/i18n/en.json
index d0a76aa49..597f8c793 100644
--- a/src/assets/i18n/en.json
+++ b/src/assets/i18n/en.json
@@ -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.",