diff --git a/docs/extending/components.md b/docs/extending/components.md
index 566599b8a..e94fa0011 100644
--- a/docs/extending/components.md
+++ b/docs/extending/components.md
@@ -11,13 +11,15 @@ The components are used to create custom:
- toolbar buttons
- menu items
-| Key | Type | Description |
-| -- | -- | -- |
-| app.layout.main | LayoutComponent | Main application layout with the menu bar, navigation sidebar and main content area to project your components. |
-| app.toolbar.toggleInfoDrawer | ToggleInfoDrawerComponent | The toolbar button component that toggles Info Drawer for the selection. |
-| app.toolbar.toggleFavorite | ToggleFavoriteComponent | The toolbar button component that toggles Favorite state for the selection. |
+| Key | Type | Description |
+| --------------------------------- | ------------------------------ | --------------------------------------------------------------------------------------------------------------- |
+| app.layout.main | LayoutComponent | Main application layout with the menu bar, navigation sidebar and main content area to project your components. |
+| app.toolbar.toggleInfoDrawer | ToggleInfoDrawerComponent | The toolbar button component that toggles Info Drawer for the selection. |
+| app.toolbar.toggleFavorite | ToggleFavoriteComponent | The toolbar button component that toggles Favorite state for the selection. |
+| app.toolbar.toggleFavoriteLibrary | ToggleFavoriteLibraryComponent | The toolbar button component that toggles Favorite library state for the selection. |
+
See [Registration](/extending/registration) section for more details
on how to register your own entries to be re-used at runtime.
Note that custom extensions can also replace any existing component at runtime by a known identifier,
-besides registering a new one.
\ No newline at end of file
+besides registering a new one.
diff --git a/src/app/app.module.ts b/src/app/app.module.ts
index 5f78916f9..3eb99f39c 100644
--- a/src/app/app.module.ts
+++ b/src/app/app.module.ts
@@ -72,6 +72,8 @@ import { AppHeaderModule } from './components/header/header.module';
import { environment } from '../environments/environment';
import { LibraryMembershipDirective } from './directives/library-membership.directive';
import { ToggleJoinLibraryComponent } from './components/toolbar/toggle-join-library/toggle-join-library.component';
+import { LibraryFavoriteDirective } from './directives/library-favorite.directive';
+import { ToggleFavoriteLibraryComponent } from './components/toolbar/toggle-favorite-library/toggle-favorite-library.component';
@NgModule({
imports: [
@@ -114,7 +116,9 @@ import { ToggleJoinLibraryComponent } from './components/toolbar/toggle-join-lib
NodeVersionsDialogComponent,
LibraryDialogComponent,
LibraryMembershipDirective,
- ToggleJoinLibraryComponent
+ ToggleJoinLibraryComponent,
+ LibraryFavoriteDirective,
+ ToggleFavoriteLibraryComponent
],
providers: [
{ provide: RouteReuseStrategy, useClass: AppRouteReuseStrategy },
@@ -131,7 +135,8 @@ import { ToggleJoinLibraryComponent } from './components/toolbar/toggle-join-lib
entryComponents: [
LibraryDialogComponent,
NodeVersionsDialogComponent,
- ToggleJoinLibraryComponent
+ ToggleJoinLibraryComponent,
+ ToggleFavoriteLibraryComponent
],
bootstrap: [AppComponent]
})
diff --git a/src/app/components/favorite-libraries/favorite-libraries.component.ts b/src/app/components/favorite-libraries/favorite-libraries.component.ts
index 77b6bc09b..3c9ff85df 100644
--- a/src/app/components/favorite-libraries/favorite-libraries.component.ts
+++ b/src/app/components/favorite-libraries/favorite-libraries.component.ts
@@ -70,7 +70,9 @@ export class FavoriteLibrariesComponent extends PageComponent
this.subscriptions = this.subscriptions.concat([
this.content.libraryDeleted.subscribe(() => this.reload()),
- this.content.libraryUpdated.subscribe(() => this.documentList.reload()),
+ this.content.libraryUpdated.subscribe(() => this.reload()),
+ this.content.favoriteLibraryToggle.subscribe(() => this.reload()),
+
this.breakpointObserver
.observe([Breakpoints.HandsetPortrait, Breakpoints.HandsetLandscape])
.subscribe(result => {
diff --git a/src/app/components/libraries/libraries.component.ts b/src/app/components/libraries/libraries.component.ts
index 143d1ad07..8b1affc39 100644
--- a/src/app/components/libraries/libraries.component.ts
+++ b/src/app/components/libraries/libraries.component.ts
@@ -55,7 +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.content.libraryUpdated.subscribe(() => this.reload()),
this.breakpointObserver
.observe([Breakpoints.HandsetPortrait, Breakpoints.HandsetLandscape])
diff --git a/src/app/components/toolbar/toggle-favorite-library/toggle-favorite-library.component.spec.ts b/src/app/components/toolbar/toggle-favorite-library/toggle-favorite-library.component.spec.ts
new file mode 100644
index 000000000..d217e4e32
--- /dev/null
+++ b/src/app/components/toolbar/toggle-favorite-library/toggle-favorite-library.component.spec.ts
@@ -0,0 +1,94 @@
+/*!
+ * @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 } from '@angular/core/testing';
+import {
+ setupTestBed,
+ CoreModule,
+ AlfrescoApiService,
+ AlfrescoApiServiceMock
+} from '@alfresco/adf-core';
+import { ToggleFavoriteLibraryComponent } from './toggle-favorite-library.component';
+import { LibraryFavoriteDirective } from '../../../directives/library-favorite.directive';
+import { NO_ERRORS_SCHEMA } from '@angular/core';
+import { Store } from '@ngrx/store';
+import { AppTestingModule } from '../../../testing/app-testing.module';
+import { ContentManagementService } from '../../../services/content-management.service';
+import { of } from 'rxjs';
+
+describe('ToggleFavoriteLibraryComponent', () => {
+ let fixture;
+ let component;
+ let contentManagementService;
+ const selection = { library: { entry: { id: 'libraryId' } } };
+
+ setupTestBed({
+ imports: [CoreModule, AppTestingModule],
+ declarations: [ToggleFavoriteLibraryComponent, LibraryFavoriteDirective],
+ providers: [
+ {
+ provide: AlfrescoApiService,
+ useClass: AlfrescoApiServiceMock
+ },
+ {
+ provide: Store,
+ useValue: {
+ dispatch: () => {},
+ select: () => of(selection)
+ }
+ },
+ ContentManagementService
+ ],
+ schemas: [NO_ERRORS_SCHEMA]
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(ToggleFavoriteLibraryComponent);
+ component = fixture.componentInstance;
+
+ contentManagementService = TestBed.get(ContentManagementService);
+ const api = TestBed.get(AlfrescoApiService);
+ spyOn(api.peopleApi, 'getFavoriteSite').and.returnValue(Promise.resolve());
+ });
+
+ it('should get library selection from Store', done => {
+ fixture.detectChanges();
+ component.selection$.subscribe(selected => {
+ expect(selected).toEqual(selection);
+ done();
+ });
+ });
+
+ it('should emit onToggleEvent() event', () => {
+ fixture.detectChanges();
+ spyOn(contentManagementService.favoriteLibraryToggle, 'next');
+
+ component.onToggleEvent();
+
+ expect(
+ contentManagementService.favoriteLibraryToggle.next
+ ).toHaveBeenCalled();
+ });
+});
diff --git a/src/app/components/toolbar/toggle-favorite-library/toggle-favorite-library.component.ts b/src/app/components/toolbar/toggle-favorite-library/toggle-favorite-library.component.ts
new file mode 100644
index 000000000..c33ad2a64
--- /dev/null
+++ b/src/app/components/toolbar/toggle-favorite-library/toggle-favorite-library.component.ts
@@ -0,0 +1,66 @@
+/*!
+ * @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, ViewEncapsulation, OnInit } from '@angular/core';
+import { Store } from '@ngrx/store';
+import { AppStore } from '../../../store/states';
+import { appSelection } from '../../../store/selectors/app.selectors';
+import { Observable } from 'rxjs';
+import { SelectionState } from '@alfresco/adf-extensions';
+import { ContentManagementService } from '../../../services/content-management.service';
+
+@Component({
+ selector: 'app-toggle-favorite-library',
+ template: `
+
+ `,
+ encapsulation: ViewEncapsulation.None,
+ host: { class: 'app-toggle-favorite-library' }
+})
+export class ToggleFavoriteLibraryComponent implements OnInit {
+ selection$: Observable;
+
+ constructor(
+ private store: Store,
+ private content: ContentManagementService
+ ) {}
+
+ ngOnInit() {
+ this.selection$ = this.store.select(appSelection);
+ }
+
+ onToggleEvent() {
+ this.content.favoriteLibraryToggle.next();
+ }
+}
diff --git a/src/app/directives/library-favorite.directive.spec.ts b/src/app/directives/library-favorite.directive.spec.ts
new file mode 100644
index 000000000..567591a90
--- /dev/null
+++ b/src/app/directives/library-favorite.directive.spec.ts
@@ -0,0 +1,144 @@
+/*!
+ * @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, ViewChild } from '@angular/core';
+import { LibraryFavoriteDirective } from './library-favorite.directive';
+import {
+ AlfrescoApiService,
+ AlfrescoApiServiceMock,
+ setupTestBed,
+ CoreModule
+} from '@alfresco/adf-core';
+import { TestBed, async } from '@angular/core/testing';
+
+@Component({
+ selector: 'app-test-component',
+ template: `
+
+ `
+})
+class TestComponent {
+ @ViewChild('favoriteLibrary')
+ directive: LibraryFavoriteDirective;
+
+ selection = null;
+}
+
+describe('LibraryFavoriteDirective', () => {
+ let fixture;
+ let api;
+ let component;
+ let selection;
+
+ setupTestBed({
+ imports: [CoreModule],
+ declarations: [TestComponent, LibraryFavoriteDirective],
+ providers: [
+ {
+ provide: AlfrescoApiService,
+ useClass: AlfrescoApiServiceMock
+ }
+ ]
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(TestComponent);
+ component = fixture.componentInstance;
+ api = TestBed.get(AlfrescoApiService);
+ selection = { entry: { guid: 'guid', id: 'id' } };
+ });
+
+ it('should not check for favorite if no selection exists', () => {
+ spyOn(api.peopleApi, 'getFavoriteSite');
+ fixture.detectChanges();
+
+ expect(api.peopleApi.getFavoriteSite).not.toHaveBeenCalled();
+ });
+
+ it('should mark selection as favorite when getFavoriteSite returns successfully', async(() => {
+ spyOn(api.peopleApi, 'getFavoriteSite').and.returnValue(Promise.resolve());
+ component.selection = selection;
+ fixture.detectChanges();
+
+ fixture.whenStable().then(() => {
+ expect(api.peopleApi.getFavoriteSite).toHaveBeenCalled();
+ expect(component.directive.isFavorite()).toBe(true);
+ });
+ }));
+
+ it('should mark selection not favorite when getFavoriteSite errors', async(() => {
+ spyOn(api.peopleApi, 'getFavoriteSite').and.returnValue(Promise.reject());
+ component.selection = selection;
+ fixture.detectChanges();
+
+ fixture.whenStable().then(() => {
+ expect(api.peopleApi.getFavoriteSite).toHaveBeenCalled();
+ expect(component.directive.isFavorite()).toBe(false);
+ });
+ }));
+
+ it('should call addFavorite() on click event when selection is not a favorite', async(() => {
+ spyOn(api.peopleApi, 'getFavoriteSite').and.returnValue(Promise.reject());
+ spyOn(api.peopleApi, 'addFavorite').and.returnValue(Promise.resolve());
+ component.selection = selection;
+ fixture.detectChanges();
+
+ expect(component.directive.isFavorite()).toBeFalsy();
+
+ fixture.whenStable().then(() => {
+ fixture.nativeElement
+ .querySelector('button')
+ .dispatchEvent(new MouseEvent('click'));
+
+ fixture.detectChanges();
+
+ expect(api.peopleApi.addFavorite).toHaveBeenCalled();
+ });
+ }));
+
+ it('should call removeFavoriteSite() on click event when selection is not a favorite', async(() => {
+ spyOn(api.peopleApi, 'getFavoriteSite').and.returnValue(Promise.resolve());
+ spyOn(api.peopleApi, 'removeFavoriteSite').and.returnValue(
+ Promise.resolve()
+ );
+ component.selection = selection;
+ fixture.detectChanges();
+
+ expect(component.directive.isFavorite()).toBeFalsy();
+
+ fixture.whenStable().then(() => {
+ fixture.nativeElement
+ .querySelector('button')
+ .dispatchEvent(new MouseEvent('click'));
+
+ fixture.detectChanges();
+
+ expect(api.peopleApi.removeFavoriteSite).toHaveBeenCalled();
+ });
+ }));
+});
diff --git a/src/app/directives/library-favorite.directive.ts b/src/app/directives/library-favorite.directive.ts
new file mode 100644
index 000000000..1a9c03ab7
--- /dev/null
+++ b/src/app/directives/library-favorite.directive.ts
@@ -0,0 +1,140 @@
+/*!
+ * @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,
+ OnChanges,
+ Output,
+ EventEmitter
+} from '@angular/core';
+import { SiteBody, FavoriteBody, FavoriteEntry, Site } from 'alfresco-js-api';
+import { AlfrescoApiService } from '@alfresco/adf-core';
+
+interface LibraryEntity {
+ entry: Site;
+ isLibrary: boolean;
+}
+
+interface FavoriteLibrary {
+ entry: Site;
+ isFavorite: boolean;
+}
+
+@Directive({
+ selector: '[acaFavoriteLibrary]',
+ exportAs: 'favoriteLibrary'
+})
+export class LibraryFavoriteDirective implements OnChanges {
+ @Input('acaFavoriteLibrary')
+ library: any = null;
+
+ @Output() toggle: EventEmitter = new EventEmitter();
+ @Output() error: EventEmitter = new EventEmitter();
+
+ private targetLibrary: any = null;
+
+ @HostListener('click')
+ onClick() {
+ this.toggleFavorite();
+ }
+
+ constructor(private alfrescoApiService: AlfrescoApiService) {}
+
+ ngOnChanges(changes) {
+ if (!changes.library.currentValue) {
+ return;
+ }
+
+ this.markFavoriteLibrary(changes.library.currentValue);
+ }
+
+ isFavorite() {
+ return this.targetLibrary && this.targetLibrary.isFavorite;
+ }
+
+ toggleFavorite() {
+ if (!this.targetLibrary) {
+ return;
+ }
+
+ if (this.targetLibrary.isFavorite) {
+ this.removeFavorite(this.targetLibrary.entry.guid);
+ } else {
+ const favoriteBody = this.createFavoriteBody(this.targetLibrary);
+ this.addFavorite(favoriteBody);
+ }
+ }
+
+ private async markFavoriteLibrary(library: LibraryEntity) {
+ this.targetLibrary = await this.getFavoriteSite(library);
+ }
+
+ private getFavoriteSite(library: LibraryEntity): Promise {
+ return this.alfrescoApiService.peopleApi
+ .getFavoriteSite('-me-', library.entry.id)
+ .then(() => {
+ return {
+ entry: { ...this.library.entry },
+ isFavorite: true
+ };
+ })
+ .catch(() => ({
+ entry: { ...this.library.entry },
+ isFavorite: false
+ }));
+ }
+
+ private createFavoriteBody(library: LibraryEntity): FavoriteBody {
+ return {
+ target: {
+ site: {
+ guid: library.entry.guid
+ }
+ }
+ };
+ }
+
+ private addFavorite(favoriteBody: FavoriteBody) {
+ this.alfrescoApiService.peopleApi
+ .addFavorite('-me-', favoriteBody)
+ .then((libraryEntry: FavoriteEntry) => {
+ this.targetLibrary.isFavorite = true;
+ this.toggle.emit(libraryEntry);
+ })
+ .catch(error => this.error.emit(error));
+ }
+
+ private removeFavorite(favoriteId: string) {
+ this.alfrescoApiService.peopleApi
+ .removeFavoriteSite('-me-', favoriteId)
+ .then((libraryBody: SiteBody) => {
+ this.targetLibrary.isFavorite = false;
+ this.toggle.emit(libraryBody);
+ })
+ .catch(error => this.error.emit(error));
+ }
+}
diff --git a/src/app/extensions/core.extensions.module.ts b/src/app/extensions/core.extensions.module.ts
index 65ae8adc6..f8a7a8839 100644
--- a/src/app/extensions/core.extensions.module.ts
+++ b/src/app/extensions/core.extensions.module.ts
@@ -33,6 +33,7 @@ import * as nav from './evaluators/navigation.evaluators';
import { AppExtensionService } from './extension.service';
import { ToggleInfoDrawerComponent } from '../components/toolbar/toggle-info-drawer/toggle-info-drawer.component';
import { ToggleFavoriteComponent } from '../components/toolbar/toggle-favorite/toggle-favorite.component';
+import { ToggleFavoriteLibraryComponent } from '../components/toolbar/toggle-favorite-library/toggle-favorite-library.component';
import { ToggleSharedComponent } from '../components/shared/toggle-shared/toggle-shared.component';
import { MetadataTabComponent } from '../components/info-drawer/metadata-tab/metadata-tab.component';
import { LibraryMetadataTabComponent } from '../components/info-drawer/library-metadata-tab/library-metadata-tab.component';
@@ -86,6 +87,7 @@ export class CoreExtensionsModule {
'app.components.tabs.versions': VersionsTabComponent,
'app.toolbar.toggleInfoDrawer': ToggleInfoDrawerComponent,
'app.toolbar.toggleFavorite': ToggleFavoriteComponent,
+ 'app.toolbar.toggleFavoriteLibrary': ToggleFavoriteLibraryComponent,
'app.toolbar.toggleJoinLibrary': ToggleJoinLibraryComponent,
'app.toolbar.cardView': DocumentDisplayModeComponent,
'app.shared-link.toggleSharedLink': ToggleSharedComponent,
diff --git a/src/app/services/content-management.service.ts b/src/app/services/content-management.service.ts
index 3dabf49ff..f6a19e1c4 100644
--- a/src/app/services/content-management.service.ts
+++ b/src/app/services/content-management.service.ts
@@ -88,6 +88,7 @@ export class ContentManagementService {
favoriteAdded = new Subject>();
favoriteRemoved = new Subject>();
favoriteToggle = new Subject>();
+ favoriteLibraryToggle = new Subject();
constructor(
private store: Store,
diff --git a/src/assets/app.extensions.json b/src/assets/app.extensions.json
index cfbea4a8b..a922b644a 100644
--- a/src/assets/app.extensions.json
+++ b/src/assets/app.extensions.json
@@ -94,11 +94,11 @@
]
},
{
- "id": "app.libraries.toolbar.info",
+ "id": "app.libraries.toolbar",
"type": "core.every",
"parameters": [
{ "type": "rule", "value": "app.selection.notEmpty" },
- { "type": "rule", "value": "app.navigation.isLibraries" }
+ { "type": "rule", "value": "app.selection.library" }
]
},
{
@@ -384,7 +384,7 @@
}
},
{
- "id": "app.toolbar.info",
+ "id": "app.toolbar.info.infoDrawer",
"type": "custom",
"order": 700,
"component": "app.toolbar.toggleInfoDrawer",
@@ -393,12 +393,12 @@
}
},
{
- "id": "app.libraries.toolbar.info",
+ "id": "app.libraries.toolbar.infoDrawer",
"type": "custom",
"order": 701,
"component": "app.toolbar.toggleInfoDrawer",
"rules": {
- "visible": "app.libraries.toolbar.info"
+ "visible": "app.libraries.toolbar"
}
},
{
@@ -427,6 +427,15 @@
"visible": "app.toolbar.favorite.canToggle"
}
},
+ {
+ "id": "app.libraries.toolbar.toggleFavorite",
+ "type": "custom",
+ "order": 101,
+ "component": "app.toolbar.toggleFavoriteLibrary",
+ "rules": {
+ "visible": "app.libraries.toolbar"
+ }
+ },
{
"id": "app.toolbar.favorite.add",
"order": 200,
@@ -906,7 +915,7 @@
"title": "APP.INFO_DRAWER.TABS.LIBRARY_PROPERTIES",
"component": "app.components.tabs.library.metadata",
"rules": {
- "visible": "app.libraries.toolbar.info"
+ "visible": "app.libraries.toolbar"
}
}
],