diff --git a/projects/aca-shared/rules/src/app.rules.ts b/projects/aca-shared/rules/src/app.rules.ts
index 2f766e4e2..dfbff769e 100644
--- a/projects/aca-shared/rules/src/app.rules.ts
+++ b/projects/aca-shared/rules/src/app.rules.ts
@@ -26,6 +26,7 @@
import { RuleContext } from '@alfresco/adf-extensions';
import * as navigation from './navigation.rules';
import * as repository from './repository.rules';
+import { isAdmin } from './user.rules';
export interface AcaRuleContext extends RuleContext {
withCredentials: boolean;
@@ -81,7 +82,10 @@ export function canShareFile(context: RuleContext): boolean {
* JSON ref: `canToggleJoinLibrary`
*/
export function canToggleJoinLibrary(context: RuleContext): boolean {
- return [hasLibrarySelected(context), !isPrivateLibrary(context), hasNoLibraryRole(context)].every(Boolean);
+ return (
+ [hasLibrarySelected(context), !isPrivateLibrary(context), hasNoLibraryRole(context)].every(Boolean) ||
+ [hasLibrarySelected(context), isPrivateLibrary(context), hasNoLibraryRole(context), isAdmin(context)].every(Boolean)
+ );
}
/**
diff --git a/projects/aca-shared/store/src/actions/library.actions.ts b/projects/aca-shared/store/src/actions/library.actions.ts
index dcd03c509..c2de77ce8 100644
--- a/projects/aca-shared/store/src/actions/library.actions.ts
+++ b/projects/aca-shared/store/src/actions/library.actions.ts
@@ -31,7 +31,8 @@ export enum LibraryActionTypes {
Create = 'CREATE_LIBRARY',
Navigate = 'NAVIGATE_LIBRARY',
Update = 'UPDATE_LIBRARY',
- Leave = 'LEAVE_LIBRARY'
+ Leave = 'LEAVE_LIBRARY',
+ Reload = 'RELOAD_LIBRARY'
}
export class DeleteLibraryAction implements Action {
@@ -61,3 +62,6 @@ export class LeaveLibraryAction implements Action {
constructor(public payload?: string) {}
}
+export class ReloadLibraryAction implements Action {
+ readonly type = LibraryActionTypes.Reload;
+}
diff --git a/src/app/app.component.ts b/src/app/app.component.ts
index f9b261723..cb0398323 100644
--- a/src/app/app.component.ts
+++ b/src/app/app.component.ts
@@ -79,7 +79,7 @@ export class AppComponent implements OnInit, OnDestroy {
ngOnInit() {
this.alfrescoApiService.getInstance().on('error', (error: { status: number; response: any }) => {
- if (error.status === 401 && !this.alfrescoApiService.isExcludedErrorListener(error?.response?.req?.url)) {
+ if (error.status === 401) {
if (!this.authenticationService.isLoggedIn()) {
this.store.dispatch(new CloseModalDialogsAction());
diff --git a/src/app/components/toolbar/toggle-join-library/toggle-join-library-button.component.ts b/src/app/components/toolbar/toggle-join-library/toggle-join-library-button.component.ts
index 256cb66f8..7a75db584 100644
--- a/src/app/components/toolbar/toggle-join-library/toggle-join-library-button.component.ts
+++ b/src/app/components/toolbar/toggle-join-library/toggle-join-library-button.component.ts
@@ -23,8 +23,16 @@
* along with Alfresco. If not, see .
*/
-import { AppStore, SetSelectedNodesAction, SnackbarErrorAction, SnackbarInfoAction, getAppSelection } from '@alfresco/aca-shared/store';
-import { SelectionState } from '@alfresco/adf-extensions';
+import {
+ AppStore,
+ SetSelectedNodesAction,
+ SnackbarErrorAction,
+ SnackbarInfoAction,
+ getAppSelection,
+ getUserProfile,
+ ReloadLibraryAction
+} from '@alfresco/aca-shared/store';
+import { ProfileState, SelectionState } from '@alfresco/adf-extensions';
import { Component, ViewEncapsulation } from '@angular/core';
import { Store } from '@ngrx/store';
import { Observable } from 'rxjs';
@@ -41,6 +49,7 @@ import { ContentManagementService } from '../../../services/content-management.s
(toggle)="onToggleEvent($event)"
(error)="onErrorEvent($event)"
[acaLibraryMembership]="(selection$ | async).library"
+ [isAdmin]="(profile$ | async).isAdmin"
[attr.title]="(membership.isJoinRequested | async) ? ('APP.ACTIONS.CANCEL_JOIN' | translate) : ('APP.ACTIONS.JOIN' | translate)"
>
cancel
@@ -52,9 +61,11 @@ import { ContentManagementService } from '../../../services/content-management.s
})
export class ToggleJoinLibraryButtonComponent {
selection$: Observable;
+ profile$: Observable;
constructor(private store: Store, private content: ContentManagementService) {
this.selection$ = this.store.select(getAppSelection);
+ this.profile$ = this.store.select(getUserProfile);
}
onToggleEvent(event: LibraryMembershipToggleEvent) {
@@ -62,6 +73,7 @@ export class ToggleJoinLibraryButtonComponent {
if (event.shouldReload) {
this.content.libraryJoined.next();
+ this.store.dispatch(new ReloadLibraryAction());
} else {
if (event.updatedEntry) {
this.store.dispatch(new SetSelectedNodesAction([{ entry: event.updatedEntry, isLibrary: true } as any]));
diff --git a/src/app/components/toolbar/toggle-join-library/toggle-join-library.component.spec.ts b/src/app/components/toolbar/toggle-join-library/toggle-join-library.component.spec.ts
index 7e904ac2c..eaeec0b4e 100644
--- a/src/app/components/toolbar/toggle-join-library/toggle-join-library.component.spec.ts
+++ b/src/app/components/toolbar/toggle-join-library/toggle-join-library.component.spec.ts
@@ -29,7 +29,7 @@ import { AlfrescoApiService, AlfrescoApiServiceMock } from '@alfresco/adf-core';
import { LibraryMembershipDirective } from '../../../directives/library-membership.directive';
import { Store } from '@ngrx/store';
import { NO_ERRORS_SCHEMA } from '@angular/core';
-import { SnackbarErrorAction, SnackbarInfoAction } from '@alfresco/aca-shared/store';
+import { ReloadLibraryAction, SnackbarErrorAction, SnackbarInfoAction } from '@alfresco/aca-shared/store';
import { AppTestingModule } from '../../../testing/app-testing.module';
import { ContentManagementService } from '../../../services/content-management.service';
import { ToggleJoinLibraryButtonComponent } from './toggle-join-library-button.component';
@@ -98,6 +98,13 @@ describe('ToggleJoinLibraryComponent', () => {
expect(storeMock.dispatch).toHaveBeenCalledWith(new SnackbarInfoAction(event.i18nKey));
});
+ it('should dispatch `ReloadLibraryAction` action on onToggleEvent', () => {
+ const event = { shouldReload: true, i18nKey: 'SOME_i18nKey' };
+ component.onToggleEvent(event);
+
+ expect(storeMock.dispatch).toHaveBeenCalledWith(new ReloadLibraryAction());
+ });
+
it('should call libraryJoined.next on contentManagementService onToggleEvent', (done) => {
spyOn(contentManagementService.libraryJoined, 'next').and.callThrough();
diff --git a/src/app/directives/library-membership.directive.spec.ts b/src/app/directives/library-membership.directive.spec.ts
index 2b7b386f8..1326d7162 100644
--- a/src/app/directives/library-membership.directive.spec.ts
+++ b/src/app/directives/library-membership.directive.spec.ts
@@ -24,18 +24,19 @@
*/
import { fakeAsync, TestBed, tick } from '@angular/core/testing';
-import { AlfrescoApiService, AlfrescoApiServiceMock, AppConfigService, CoreModule, StorageService } from '@alfresco/adf-core';
+import { AlfrescoApiService, AlfrescoApiServiceMock, AppConfigService, CoreModule, SitesService, StorageService } from '@alfresco/adf-core';
import { AppTestingModule } from '../testing/app-testing.module';
import { DirectivesModule } from './directives.module';
import { LibraryMembershipDirective } from './library-membership.directive';
import { NO_ERRORS_SCHEMA, SimpleChange } from '@angular/core';
-import { throwError } from 'rxjs';
+import { of, throwError } from 'rxjs';
import { TranslateModule } from '@ngx-translate/core';
describe('LibraryMembershipDirective', () => {
let alfrescoApiService: AlfrescoApiService;
let directive: LibraryMembershipDirective;
let peopleApi;
+ let sitesService;
let addMembershipSpy;
let getMembershipSpy;
let deleteMembershipSpy;
@@ -58,8 +59,9 @@ describe('LibraryMembershipDirective', () => {
schemas: [NO_ERRORS_SCHEMA]
});
alfrescoApiService = new AlfrescoApiServiceMock(new AppConfigService(null), new StorageService());
+ sitesService = new SitesService(alfrescoApiService);
peopleApi = alfrescoApiService.getInstance().core.peopleApi;
- directive = new LibraryMembershipDirective(alfrescoApiService);
+ directive = new LibraryMembershipDirective(alfrescoApiService, sitesService);
});
describe('markMembershipRequest', () => {
@@ -141,6 +143,19 @@ describe('LibraryMembershipDirective', () => {
expect(deleteMembershipSpy).not.toHaveBeenCalled();
}));
+ it('should call API to add user to library if admin user', fakeAsync(() => {
+ const createSiteMembershipSpy = spyOn(sitesService, 'createSiteMembership').and.returnValue(of({}));
+ const selection = { entry: { id: 'no-membership-requested' } };
+ const selectionChange = new SimpleChange(null, selection, true);
+ directive.isAdmin = true;
+ directive.ngOnChanges({ selection: selectionChange });
+ tick();
+ directive.toggleMembershipRequest();
+ tick();
+ expect(createSiteMembershipSpy).toHaveBeenCalled();
+ expect(addMembershipSpy).not.toHaveBeenCalled();
+ }));
+
it('should emit error when the request to join a library fails', fakeAsync(() => {
spyOn(directive.error, 'emit');
addMembershipSpy.and.returnValue(throwError('err'));
diff --git a/src/app/directives/library-membership.directive.ts b/src/app/directives/library-membership.directive.ts
index c6a393f02..c97fb96e7 100644
--- a/src/app/directives/library-membership.directive.ts
+++ b/src/app/directives/library-membership.directive.ts
@@ -24,8 +24,8 @@
*/
import { Directive, EventEmitter, HostListener, Input, OnChanges, Output, SimpleChanges } from '@angular/core';
-import { SiteEntry, SiteMembershipRequestBody } from '@alfresco/js-api';
-import { AlfrescoApiService } from '@alfresco/adf-core';
+import { SiteEntry, SiteMemberEntry, SiteMembershipRequestBody } from '@alfresco/js-api';
+import { AlfrescoApiService, SitesService } from '@alfresco/adf-core';
import { BehaviorSubject, from } from 'rxjs';
export interface LibraryMembershipToggleEvent {
@@ -52,6 +52,10 @@ export class LibraryMembershipDirective implements OnChanges {
@Input('acaLibraryMembership')
selection: SiteEntry = null;
+ /** Site for which to toggle the membership request. */
+ @Input()
+ isAdmin = false;
+
@Output()
toggle = new EventEmitter();
@@ -64,7 +68,7 @@ export class LibraryMembershipDirective implements OnChanges {
this.toggleMembershipRequest();
}
- constructor(private alfrescoApiService: AlfrescoApiService) {}
+ constructor(private alfrescoApiService: AlfrescoApiService, private sitesService: SitesService) {}
ngOnChanges(changes: SimpleChanges) {
if (!changes.selection.currentValue || !changes.selection.currentValue.entry) {
@@ -103,7 +107,7 @@ export class LibraryMembershipDirective implements OnChanges {
);
}
- if (!this.targetSite.joinRequested) {
+ if (!this.targetSite.joinRequested && !this.isAdmin) {
this.joinLibraryRequest().subscribe(
(createdMembership) => {
this.targetSite.joinRequested = true;
@@ -145,6 +149,39 @@ export class LibraryMembershipDirective implements OnChanges {
}
);
}
+
+ if (this.isAdmin) {
+ this.joinLibrary().subscribe(
+ (createdMembership: SiteMemberEntry) => {
+ if (createdMembership.entry && createdMembership.entry.role) {
+ const info = {
+ shouldReload: true,
+ i18nKey: 'APP.MESSAGES.INFO.JOINED'
+ };
+ this.toggle.emit(info);
+ }
+ },
+ (error) => {
+ const errWithMessage = {
+ error,
+ i18nKey: 'APP.MESSAGES.ERRORS.JOIN_REQUEST_FAILED'
+ };
+
+ const senderEmailCheck = 'Failed to resolve sender mail address';
+ const receiverEmailCheck = 'All recipients for the mail action were invalid';
+
+ if (error.message) {
+ if (error.message.includes(senderEmailCheck)) {
+ errWithMessage.i18nKey = 'APP.MESSAGES.ERRORS.INVALID_SENDER_EMAIL';
+ } else if (error.message.includes(receiverEmailCheck)) {
+ errWithMessage.i18nKey = 'APP.MESSAGES.ERRORS.INVALID_RECEIVER_EMAIL';
+ }
+ }
+
+ this.error.emit(errWithMessage);
+ }
+ );
+ }
}
markMembershipRequest() {
@@ -174,6 +211,13 @@ export class LibraryMembershipDirective implements OnChanges {
return from(this.alfrescoApiService.peopleApi.addSiteMembershipRequest('-me-', memberBody));
}
+ private joinLibrary() {
+ return this.sitesService.createSiteMembership(this.targetSite.id, {
+ role: 'SiteConsumer',
+ id: '-me-'
+ });
+ }
+
private cancelJoinRequest() {
return from(this.alfrescoApiService.peopleApi.removeSiteMembershipRequest('-me-', this.targetSite.id));
}
diff --git a/src/app/store/effects/library.effects.ts b/src/app/store/effects/library.effects.ts
index abb833f0b..eee1dd424 100644
--- a/src/app/store/effects/library.effects.ts
+++ b/src/app/store/effects/library.effects.ts
@@ -33,7 +33,8 @@ import {
NavigateRouteAction,
SnackbarErrorAction,
UpdateLibraryAction,
- getAppSelection
+ getAppSelection,
+ ReloadLibraryAction
} from '@alfresco/aca-shared/store';
import { Injectable } from '@angular/core';
import { Actions, Effect, ofType } from '@ngrx/effects';
@@ -86,6 +87,7 @@ export class LibraryEffects {
}
});
}
+ this.store.dispatch(new ReloadLibraryAction());
})
);