mirror of
https://github.com/Alfresco/alfresco-content-app.git
synced 2025-05-12 17:04:46 +00:00
[ACA-1743] extension settings (#1399)
* upgrade tslib * initial settings skeleton * migrate language picker setting * support string parameters * remove process extensions workaround * update extensions schema * update docs * unit tests * fix unit test
This commit is contained in:
parent
2b910d5a15
commit
5a88c8c852
@ -16,6 +16,7 @@ Learn how to extend the features of the Alfresco Content Application.
|
||||
- [Actions](/extending/actions)
|
||||
- [Application actions](/extending/application-actions)
|
||||
- [Rules](/extending/rules)
|
||||
- [Settings](/extending/settings)
|
||||
- [Application features](/extending/application-features)
|
||||
- [Custom icons](/extending/icons)
|
||||
- [Registration](/extending/registration)
|
||||
|
45
docs/extending/settings.md
Normal file
45
docs/extending/settings.md
Normal file
@ -0,0 +1,45 @@
|
||||
---
|
||||
Title: Settings
|
||||
---
|
||||
|
||||
# Settings
|
||||
|
||||
The application settings can be accessed via the `/settings` route.
|
||||
|
||||
You can project custom configuration groups via the `settings` section:
|
||||
|
||||
```json
|
||||
{
|
||||
"settings": [
|
||||
{
|
||||
"id": "extensions.ps.settings",
|
||||
"name": "Extensions: Process Services",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "Enable Process Services Extensions",
|
||||
"key": "processServices",
|
||||
"type": "boolean",
|
||||
"value": false
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
At runtime, you are going to get an extra group called "Extensions: Process Services"
|
||||
with a custom boolean setting "Enable Process Services Extensions".
|
||||
|
||||

|
||||
|
||||
## Parameters
|
||||
|
||||
Each setting parameter object supports the following properties:
|
||||
|
||||
| Property | Description |
|
||||
| -------- | ----------------------------------------------- |
|
||||
| id | (optional) Unique identifier |
|
||||
| name | Public name, can be translation key |
|
||||
| key | The key to use when saving to the storage |
|
||||
| type | The type of the value (boolean / string) |
|
||||
| value | (optional) Default value to use for the setting |
|
BIN
docs/images/aca-settings-custom-group.png
Normal file
BIN
docs/images/aca-settings-custom-group.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 65 KiB |
@ -605,6 +605,52 @@
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
},
|
||||
"settingsGroupRef": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"description": "Unique identifier",
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"description": "Category name, can be translation key",
|
||||
"type": "string"
|
||||
},
|
||||
"parameters": {
|
||||
"description": "Settings group parameters",
|
||||
"type": "array",
|
||||
"items": { "$ref": "#/definitions/settingsGroupParameterRef" },
|
||||
"minItems": 1
|
||||
}
|
||||
},
|
||||
"required": ["id", "name", "parameters"]
|
||||
},
|
||||
"settingsGroupParameterRef": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"description": "Unique identifier",
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"description": "Public name, can be a translation key",
|
||||
"type": "string"
|
||||
},
|
||||
"key": {
|
||||
"description": "The key to use when saving to the storage",
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"description": "The type of the value",
|
||||
"type": "string"
|
||||
},
|
||||
"value": {
|
||||
"description": "Default value to use for the setting",
|
||||
"type": ["boolean", "string", "number"]
|
||||
}
|
||||
},
|
||||
"required": ["name", "key", "type"]
|
||||
}
|
||||
},
|
||||
|
||||
@ -666,6 +712,12 @@
|
||||
"items": { "$ref": "#/definitions/actionRef" },
|
||||
"minItems": 1
|
||||
},
|
||||
"settings": {
|
||||
"description": "List of application-specific setting groups",
|
||||
"type": "array",
|
||||
"items": { "$ref": "#/definitions/settingsGroupRef" },
|
||||
"minItems": 1
|
||||
},
|
||||
"features": {
|
||||
"description": "Application-specific features and extensions",
|
||||
"type": "object",
|
||||
|
6
package-lock.json
generated
6
package-lock.json
generated
@ -13380,9 +13380,9 @@
|
||||
}
|
||||
},
|
||||
"tslib": {
|
||||
"version": "1.9.3",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz",
|
||||
"integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ=="
|
||||
"version": "1.11.1",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.11.1.tgz",
|
||||
"integrity": "sha512-aZW88SY8kQbU7gpV19lN24LtXh/yD4ZZg6qieAJDDg+YBsJcSmLGK9QpnUjAKVG/xefmvJGd1WUmfpT/g6AJGA=="
|
||||
},
|
||||
"tslint": {
|
||||
"version": "5.20.1",
|
||||
|
@ -91,8 +91,8 @@
|
||||
"codelyzer": "^5.2.2",
|
||||
"commander": "^4.0.1",
|
||||
"cpr": "^3.0.1",
|
||||
"dotenv": "6.2.0",
|
||||
"cspell": "^4.0.55",
|
||||
"dotenv": "6.2.0",
|
||||
"husky": "^2.4.0",
|
||||
"jasmine-core": "~2.8.0",
|
||||
"jasmine-reporters": "^2.2.1",
|
||||
@ -116,7 +116,7 @@
|
||||
"selenium-webdriver": "4.0.0-alpha.1",
|
||||
"ts-node": "^8.0.3",
|
||||
"tsickle": "0.34.0",
|
||||
"tslib": "^1.9.0",
|
||||
"tslib": "^1.11.1",
|
||||
"tslint": "^5.20.1",
|
||||
"typescript": "3.2.4",
|
||||
"wait-on": "^3.0.1",
|
||||
|
@ -30,7 +30,6 @@ import * as repository from './repository.rules';
|
||||
export interface AcaRuleContext extends RuleContext {
|
||||
languagePicker: boolean;
|
||||
withCredentials: boolean;
|
||||
processServices: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -554,5 +553,5 @@ export function canShowLogout(context: AcaRuleContext): boolean {
|
||||
* @param context Rule execution context
|
||||
*/
|
||||
export function canShowProcessServices(context: AcaRuleContext): boolean {
|
||||
return context.processServices;
|
||||
return localStorage && localStorage.getItem('processServices') === 'true';
|
||||
}
|
||||
|
@ -28,8 +28,8 @@ import { Node, Person, Group, RepositoryInfo } from '@alfresco/js-api';
|
||||
import { AppState } from '../states/app.state';
|
||||
|
||||
export enum AppActionTypes {
|
||||
SetSettingsParameter = 'SET_SETTINGS_PARAMETER',
|
||||
SetInitialState = 'SET_INITIAL_STATE',
|
||||
SetLanguagePicker = 'SET_LANGUAGE_PICKER',
|
||||
SetCurrentFolder = 'SET_CURRENT_FOLDER',
|
||||
SetCurrentUrl = 'SET_CURRENT_URL',
|
||||
SetUserProfile = 'SET_USER_PROFILE',
|
||||
@ -41,8 +41,13 @@ export enum AppActionTypes {
|
||||
ResetSelection = 'RESET_SELECTION',
|
||||
SetInfoDrawerState = 'SET_INFO_DRAWER_STATE',
|
||||
SetInfoDrawerMetadataAspect = 'SET_INFO_DRAWER_METADATA_ASPECT',
|
||||
CloseModalDialogs = 'CLOSE_MODAL_DIALOGS',
|
||||
ToggleProcessServices = 'TOGGLE_PROCESS_SERVICES'
|
||||
CloseModalDialogs = 'CLOSE_MODAL_DIALOGS'
|
||||
}
|
||||
|
||||
export class SetSettingsParameterAction implements Action {
|
||||
readonly type = AppActionTypes.SetSettingsParameter;
|
||||
|
||||
constructor(public payload: { name: string; value: any }) {}
|
||||
}
|
||||
|
||||
export class SetInitialStateAction implements Action {
|
||||
@ -51,12 +56,6 @@ export class SetInitialStateAction implements Action {
|
||||
constructor(public payload: AppState) {}
|
||||
}
|
||||
|
||||
export class SetLanguagePickerAction implements Action {
|
||||
readonly type = AppActionTypes.SetLanguagePicker;
|
||||
|
||||
constructor(public payload: boolean) {}
|
||||
}
|
||||
|
||||
export class SetCurrentFolderAction implements Action {
|
||||
readonly type = AppActionTypes.SetCurrentFolder;
|
||||
|
||||
@ -114,9 +113,3 @@ export class SetRepositoryInfoAction implements Action {
|
||||
|
||||
constructor(public payload: RepositoryInfo) {}
|
||||
}
|
||||
|
||||
export class ToggleProcessServicesAction implements Action {
|
||||
readonly type = AppActionTypes.ToggleProcessServices;
|
||||
|
||||
constructor(public payload: boolean) {}
|
||||
}
|
||||
|
@ -133,8 +133,3 @@ export const infoDrawerMetadataAspect = createSelector(
|
||||
selectApp,
|
||||
state => state.infoDrawerMetadataAspect
|
||||
);
|
||||
|
||||
export const getProcessServicesState = createSelector(
|
||||
selectApp,
|
||||
state => state.processServices
|
||||
);
|
||||
|
@ -44,7 +44,6 @@ export interface AppState {
|
||||
showFacetFilter: boolean;
|
||||
documentDisplayMode: string;
|
||||
repository: RepositoryInfo;
|
||||
processServices: boolean;
|
||||
}
|
||||
|
||||
export interface AppStore {
|
||||
|
@ -26,8 +26,6 @@
|
||||
"viewer.maxRetries": 1,
|
||||
"sharedLinkDateTimePickerType": "date",
|
||||
"headerColor": "#ffffff",
|
||||
"languagePicker": true,
|
||||
"processServices": true,
|
||||
"pagination": {
|
||||
"size": 25,
|
||||
"supportedPageSizes": [25, 50, 100]
|
||||
|
@ -42,6 +42,12 @@ describe('AppComponent', () => {
|
||||
}
|
||||
};
|
||||
|
||||
const storageMock: any = {
|
||||
getItem(): string {
|
||||
return '';
|
||||
}
|
||||
};
|
||||
|
||||
beforeAll(() => {
|
||||
component = new AppComponent(
|
||||
null,
|
||||
@ -55,7 +61,8 @@ describe('AppComponent', () => {
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
null,
|
||||
storageMock
|
||||
);
|
||||
});
|
||||
|
||||
|
@ -30,7 +30,8 @@ import {
|
||||
FileUploadErrorEvent,
|
||||
PageTitleService,
|
||||
UploadService,
|
||||
SharedLinksApiService
|
||||
SharedLinksApiService,
|
||||
StorageService
|
||||
} from '@alfresco/adf-core';
|
||||
import { Component, OnInit, OnDestroy } from '@angular/core';
|
||||
import { ActivatedRoute, Router, ActivationEnd } from '@angular/router';
|
||||
@ -73,7 +74,8 @@ export class AppComponent implements OnInit, OnDestroy {
|
||||
private extensions: AppExtensionService,
|
||||
private contentApi: ContentApiService,
|
||||
private appService: AppService,
|
||||
private sharedLinksApiService: SharedLinksApiService
|
||||
private sharedLinksApiService: SharedLinksApiService,
|
||||
private storage: StorageService
|
||||
) {}
|
||||
|
||||
ngOnInit() {
|
||||
@ -178,8 +180,7 @@ export class AppComponent implements OnInit, OnDestroy {
|
||||
|
||||
const state: AppState = {
|
||||
...INITIAL_APP_STATE,
|
||||
languagePicker: this.config.get<boolean>('languagePicker'),
|
||||
processServices: this.config.get<boolean>('processServices'),
|
||||
languagePicker: this.storage.getItem('languagePicker') === 'true',
|
||||
appName: this.config.get<string>('application.name'),
|
||||
headerColor: this.config.get<string>('headerColor'),
|
||||
logoPath: this.config.get<string>('application.logo'),
|
||||
|
@ -32,7 +32,7 @@ import { Store } from '@ngrx/store';
|
||||
import {
|
||||
AppState,
|
||||
SetUserProfileAction,
|
||||
SetLanguagePickerAction
|
||||
SetSettingsParameterAction
|
||||
} from '@alfresco/aca-shared/store';
|
||||
|
||||
describe('CurrentUserComponent', () => {
|
||||
@ -91,7 +91,9 @@ describe('CurrentUserComponent', () => {
|
||||
it('should set language picker state', done => {
|
||||
fixture.detectChanges();
|
||||
|
||||
store.dispatch(new SetLanguagePickerAction(true));
|
||||
store.dispatch(
|
||||
new SetSettingsParameterAction({ name: 'languagePicker', value: true })
|
||||
);
|
||||
|
||||
component.languagePicker$.subscribe((languagePicker: boolean) => {
|
||||
expect(languagePicker).toBe(true);
|
||||
|
@ -68,39 +68,40 @@
|
||||
</form>
|
||||
</mat-expansion-panel>
|
||||
|
||||
<mat-expansion-panel>
|
||||
<mat-expansion-panel *ngFor="let group of settingGroups">
|
||||
<mat-expansion-panel-header>
|
||||
<mat-panel-title>
|
||||
{{ 'APP.SETTINGS.APPLICATION-SETTINGS' | translate }}
|
||||
</mat-panel-title>
|
||||
</mat-expansion-panel-header>
|
||||
<mat-checkbox
|
||||
[ngModel]="languagePicker$ | async"
|
||||
(change)="onLanguagePickerValueChanged($event)"
|
||||
>
|
||||
Language Picker
|
||||
</mat-checkbox>
|
||||
</mat-expansion-panel>
|
||||
|
||||
<mat-expansion-panel>
|
||||
<mat-expansion-panel-header>
|
||||
<mat-panel-title>Extensions</mat-panel-title>
|
||||
<mat-panel-title>{{ group.name | translate }}</mat-panel-title>
|
||||
</mat-expansion-panel-header>
|
||||
|
||||
<div class="aca-settings-extensions-list">
|
||||
<mat-checkbox
|
||||
[ngModel]="aiExtensions$ | async"
|
||||
(change)="onToggleAiExtensions($event)"
|
||||
>
|
||||
Enable AI Extensions
|
||||
</mat-checkbox>
|
||||
<div class="aca-settings-parameter-list">
|
||||
<ng-container *ngFor="let param of group.parameters">
|
||||
<ng-container [ngSwitch]="param.type">
|
||||
<ng-container *ngSwitchCase="'boolean'">
|
||||
<mat-checkbox
|
||||
[checked]="getBooleanParamValue(param)"
|
||||
(change)="setParamValue(param, $event.checked)"
|
||||
>{{ param.name | translate }}</mat-checkbox
|
||||
>
|
||||
</ng-container>
|
||||
|
||||
<mat-checkbox
|
||||
[ngModel]="psExtensions$ | async"
|
||||
(change)="onTogglePsExtensions($event)"
|
||||
>
|
||||
Enable Process Services Extensions
|
||||
</mat-checkbox>
|
||||
<ng-container *ngSwitchCase="'string'">
|
||||
<mat-form-field class="settings-input" appearance="outline">
|
||||
<mat-label>{{ param.name | translate }}</mat-label>
|
||||
<input
|
||||
matInput
|
||||
type="text"
|
||||
[value]="getStringParamValue(param)"
|
||||
(blur)="setParamValue(param, $event.target.value)"
|
||||
(keyup.enter)="setParamValue(param, $event.target.value)"
|
||||
/>
|
||||
</mat-form-field>
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngSwitchDefault>
|
||||
<span>Unknown parameter type: {{ param.name | translate }}</span>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
</div>
|
||||
</mat-expansion-panel>
|
||||
</mat-accordion>
|
||||
|
@ -24,9 +24,131 @@
|
||||
*/
|
||||
|
||||
import { SettingsComponent } from './settings.component';
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { setupTestBed, StorageService } from '@alfresco/adf-core';
|
||||
import { AppSettingsModule } from './settings.module';
|
||||
import { AppTestingModule } from '../../testing/app-testing.module';
|
||||
import { SettingsParameterRef } from '../../types';
|
||||
import { AppExtensionService } from '../../extensions/extension.service';
|
||||
import { By } from '@angular/platform-browser';
|
||||
import {
|
||||
TranslateModule,
|
||||
TranslateLoader,
|
||||
TranslateFakeLoader
|
||||
} from '@ngx-translate/core';
|
||||
|
||||
describe('SettingsComponent', () => {
|
||||
it('should be defined', () => {
|
||||
expect(SettingsComponent).toBeDefined();
|
||||
let fixture: ComponentFixture<SettingsComponent>;
|
||||
let component: SettingsComponent;
|
||||
let storage: StorageService;
|
||||
let appExtensions: AppExtensionService;
|
||||
|
||||
let stringParam: SettingsParameterRef;
|
||||
let boolParam: SettingsParameterRef;
|
||||
|
||||
setupTestBed({
|
||||
imports: [
|
||||
AppSettingsModule,
|
||||
AppTestingModule,
|
||||
TranslateModule.forRoot({
|
||||
loader: { provide: TranslateLoader, useClass: TranslateFakeLoader }
|
||||
})
|
||||
]
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(SettingsComponent);
|
||||
component = fixture.componentInstance;
|
||||
|
||||
storage = TestBed.get(StorageService);
|
||||
appExtensions = TestBed.get(AppExtensionService);
|
||||
|
||||
stringParam = {
|
||||
key: 'key',
|
||||
name: 'param1',
|
||||
type: 'string',
|
||||
value: 'paramValue'
|
||||
};
|
||||
|
||||
boolParam = {
|
||||
key: 'key',
|
||||
name: 'param2',
|
||||
type: 'boolean',
|
||||
value: true
|
||||
};
|
||||
});
|
||||
|
||||
it('should retrieve string param value from storage', () => {
|
||||
spyOn(storage, 'getItem').and.returnValue('storageValue');
|
||||
|
||||
const value = component.getStringParamValue(stringParam);
|
||||
expect(value).toBe('storageValue');
|
||||
});
|
||||
|
||||
it('should use param value as fallback when storage is empty', () => {
|
||||
spyOn(storage, 'getItem').and.returnValue(null);
|
||||
|
||||
const value = component.getStringParamValue(stringParam);
|
||||
expect(value).toBe('paramValue');
|
||||
});
|
||||
|
||||
it('should save param value', () => {
|
||||
spyOn(storage, 'setItem').and.stub();
|
||||
|
||||
component.setParamValue(stringParam, 'test');
|
||||
|
||||
expect(stringParam.value).toBe('test');
|
||||
expect(storage.setItem).toHaveBeenCalledWith(
|
||||
stringParam.key,
|
||||
stringParam.value
|
||||
);
|
||||
});
|
||||
|
||||
it('should save param value only if changed', () => {
|
||||
spyOn(storage, 'setItem').and.stub();
|
||||
|
||||
component.setParamValue(stringParam, 'test');
|
||||
component.setParamValue(stringParam, 'test');
|
||||
component.setParamValue(stringParam, 'test');
|
||||
|
||||
expect(storage.setItem).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('should retrieve boolean param value', () => {
|
||||
const getItemSpy = spyOn(storage, 'getItem').and.returnValue('true');
|
||||
expect(component.getBooleanParamValue(boolParam)).toBe(true);
|
||||
|
||||
getItemSpy.and.returnValue('false');
|
||||
expect(component.getBooleanParamValue(boolParam)).toBe(false);
|
||||
});
|
||||
|
||||
it('should fallback to boolean param value when storage is empty', () => {
|
||||
spyOn(storage, 'getItem').and.returnValue(null);
|
||||
expect(component.getBooleanParamValue(boolParam)).toBe(true);
|
||||
});
|
||||
|
||||
it('should render categories as expansion panels', async () => {
|
||||
spyOn(component, 'reset').and.stub();
|
||||
|
||||
appExtensions.settingGroups = [
|
||||
{
|
||||
id: 'group1',
|
||||
name: 'Group 1',
|
||||
parameters: []
|
||||
},
|
||||
{
|
||||
id: 'group2',
|
||||
name: 'Group 2',
|
||||
parameters: []
|
||||
}
|
||||
];
|
||||
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
|
||||
const panels = fixture.debugElement.queryAll(
|
||||
By.css('.mat-expansion-panel')
|
||||
);
|
||||
expect(panels.length).toBe(3);
|
||||
});
|
||||
});
|
||||
|
@ -2,6 +2,11 @@
|
||||
$background: map-get($theme, background);
|
||||
$app-menu-height: 64px;
|
||||
|
||||
.aca-settings-parameter-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.aca-settings {
|
||||
&-extensions-list {
|
||||
display: flex;
|
||||
|
@ -30,19 +30,18 @@ import {
|
||||
OauthConfigModel
|
||||
} from '@alfresco/adf-core';
|
||||
import { Validators, FormGroup, FormBuilder } from '@angular/forms';
|
||||
import { Observable, BehaviorSubject } from 'rxjs';
|
||||
import { Observable } from 'rxjs';
|
||||
import { Store } from '@ngrx/store';
|
||||
import { MatCheckboxChange } from '@angular/material/checkbox';
|
||||
import {
|
||||
AppStore,
|
||||
SetLanguagePickerAction,
|
||||
getHeaderColor,
|
||||
getAppName,
|
||||
getUserProfile,
|
||||
getLanguagePickerState,
|
||||
ToggleProcessServicesAction
|
||||
SetSettingsParameterAction
|
||||
} from '@alfresco/aca-shared/store';
|
||||
import { ProfileState } from '@alfresco/adf-extensions';
|
||||
import { AppExtensionService } from '../../extensions/extension.service';
|
||||
import { SettingsGroupRef, SettingsParameterRef } from '../../types';
|
||||
|
||||
interface RepositoryConfig {
|
||||
ecmHost: string;
|
||||
@ -64,11 +63,13 @@ export class SettingsComponent implements OnInit {
|
||||
profile$: Observable<ProfileState>;
|
||||
appName$: Observable<string>;
|
||||
headerColor$: Observable<string>;
|
||||
languagePicker$: Observable<boolean>;
|
||||
aiExtensions$: Observable<boolean>;
|
||||
psExtensions$: Observable<boolean>;
|
||||
|
||||
get settingGroups(): SettingsGroupRef[] {
|
||||
return this.appExtensions.settingGroups;
|
||||
}
|
||||
|
||||
constructor(
|
||||
private appExtensions: AppExtensionService,
|
||||
private store: Store<AppStore>,
|
||||
private appConfig: AppConfigService,
|
||||
private storage: StorageService,
|
||||
@ -76,23 +77,14 @@ export class SettingsComponent implements OnInit {
|
||||
) {
|
||||
this.profile$ = store.select(getUserProfile);
|
||||
this.appName$ = store.select(getAppName);
|
||||
this.languagePicker$ = store.select(getLanguagePickerState);
|
||||
this.headerColor$ = store.select(getHeaderColor);
|
||||
}
|
||||
|
||||
get logo() {
|
||||
get logo(): string {
|
||||
return this.appConfig.get('application.logo', this.defaultPath);
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.aiExtensions$ = new BehaviorSubject(
|
||||
this.storage.getItem('ai') === 'true'
|
||||
);
|
||||
|
||||
this.psExtensions$ = new BehaviorSubject(
|
||||
this.storage.getItem('processServices') === 'true'
|
||||
);
|
||||
|
||||
this.form = this.fb.group({
|
||||
ecmHost: [
|
||||
'',
|
||||
@ -139,17 +131,33 @@ export class SettingsComponent implements OnInit {
|
||||
});
|
||||
}
|
||||
|
||||
onLanguagePickerValueChanged(event: MatCheckboxChange) {
|
||||
this.storage.setItem('languagePicker', event.checked.toString());
|
||||
this.store.dispatch(new SetLanguagePickerAction(event.checked));
|
||||
getStringParamValue(param: SettingsParameterRef): string {
|
||||
return this.storage.getItem(param.key) || param.value;
|
||||
}
|
||||
|
||||
onToggleAiExtensions(event: MatCheckboxChange) {
|
||||
this.storage.setItem('ai', event.checked.toString());
|
||||
setParamValue(param: SettingsParameterRef, value: any) {
|
||||
if (param.value !== value) {
|
||||
param.value = value;
|
||||
this.saveToStorage(param);
|
||||
}
|
||||
}
|
||||
|
||||
onTogglePsExtensions(event: MatCheckboxChange) {
|
||||
this.storage.setItem('processServices', event.checked.toString());
|
||||
this.store.dispatch(new ToggleProcessServicesAction(event.checked));
|
||||
getBooleanParamValue(param: SettingsParameterRef): boolean {
|
||||
const result = this.storage.getItem(param.key);
|
||||
if (result) {
|
||||
return result === 'true';
|
||||
} else {
|
||||
return param.value ? true : false;
|
||||
}
|
||||
}
|
||||
|
||||
private saveToStorage(param: SettingsParameterRef) {
|
||||
this.storage.setItem(
|
||||
param.key,
|
||||
param.value ? param.value.toString() : param.value
|
||||
);
|
||||
this.store.dispatch(
|
||||
new SetSettingsParameterAction({ name: param.key, value: param.value })
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -31,8 +31,7 @@ import { DomSanitizer } from '@angular/platform-browser';
|
||||
import {
|
||||
AppStore,
|
||||
getRuleContext,
|
||||
getLanguagePickerState,
|
||||
getProcessServicesState
|
||||
getLanguagePickerState
|
||||
} from '@alfresco/aca-shared/store';
|
||||
import { NodePermissionService } from '@alfresco/aca-shared';
|
||||
import {
|
||||
@ -60,6 +59,7 @@ import { AppConfigService, AuthenticationService } from '@alfresco/adf-core';
|
||||
import { BehaviorSubject, Observable } from 'rxjs';
|
||||
import { RepositoryInfo, NodeEntry } from '@alfresco/js-api';
|
||||
import { ViewerRules } from './viewer.rules';
|
||||
import { SettingsGroupRef } from '../types';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
@ -84,6 +84,7 @@ export class AppExtensionService implements RuleContext {
|
||||
contentMetadata: any;
|
||||
viewerRules: ViewerRules = {};
|
||||
userActions: Array<ContentActionRef> = [];
|
||||
settingGroups: Array<SettingsGroupRef> = [];
|
||||
|
||||
documentListPresets: {
|
||||
files: Array<DocumentListPresetRef>;
|
||||
@ -111,7 +112,6 @@ export class AppExtensionService implements RuleContext {
|
||||
repository: RepositoryInfo;
|
||||
withCredentials: boolean;
|
||||
languagePicker: boolean;
|
||||
processServices: boolean;
|
||||
|
||||
references$: Observable<ExtensionRef[]>;
|
||||
|
||||
@ -137,10 +137,6 @@ export class AppExtensionService implements RuleContext {
|
||||
this.store.select(getLanguagePickerState).subscribe(result => {
|
||||
this.languagePicker = result;
|
||||
});
|
||||
|
||||
this.store.select(getProcessServicesState).subscribe(result => {
|
||||
this.processServices = result;
|
||||
});
|
||||
}
|
||||
|
||||
async load() {
|
||||
@ -153,6 +149,12 @@ export class AppExtensionService implements RuleContext {
|
||||
console.error('Extension configuration not found');
|
||||
return;
|
||||
}
|
||||
|
||||
this.settingGroups = this.loader.getElements<SettingsGroupRef>(
|
||||
config,
|
||||
'settings'
|
||||
);
|
||||
|
||||
this.headerActions = this.loader.getContentActions(
|
||||
config,
|
||||
'features.header'
|
||||
|
@ -54,8 +54,7 @@ export const INITIAL_APP_STATE: AppState = {
|
||||
status: <any>{
|
||||
isQuickShareEnabled: true
|
||||
}
|
||||
},
|
||||
processServices: false
|
||||
}
|
||||
};
|
||||
|
||||
export const INITIAL_STATE: AppStore = {
|
||||
|
@ -30,7 +30,6 @@ import {
|
||||
NodeActionTypes,
|
||||
SearchActionTypes,
|
||||
SetUserProfileAction,
|
||||
SetLanguagePickerAction,
|
||||
SetCurrentFolderAction,
|
||||
SetCurrentUrlAction,
|
||||
SetInitialStateAction,
|
||||
@ -38,7 +37,7 @@ import {
|
||||
SetRepositoryInfoAction,
|
||||
SetInfoDrawerStateAction,
|
||||
SetInfoDrawerMetadataAspectAction,
|
||||
ToggleProcessServicesAction
|
||||
SetSettingsParameterAction
|
||||
} from '@alfresco/aca-shared/store';
|
||||
import { INITIAL_APP_STATE } from '../initial-state';
|
||||
|
||||
@ -52,15 +51,18 @@ export function appReducer(
|
||||
case AppActionTypes.SetInitialState:
|
||||
newState = Object.assign({}, (<SetInitialStateAction>action).payload);
|
||||
break;
|
||||
case AppActionTypes.SetSettingsParameter:
|
||||
newState = handleSettingsUpdate(
|
||||
state,
|
||||
action as SetSettingsParameterAction
|
||||
);
|
||||
break;
|
||||
case NodeActionTypes.SetSelection:
|
||||
newState = updateSelectedNodes(state, <SetSelectedNodesAction>action);
|
||||
break;
|
||||
case AppActionTypes.SetUserProfile:
|
||||
newState = updateUser(state, <SetUserProfileAction>action);
|
||||
break;
|
||||
case AppActionTypes.SetLanguagePicker:
|
||||
newState = updateLanguagePicker(state, <SetLanguagePickerAction>action);
|
||||
break;
|
||||
case AppActionTypes.SetCurrentFolder:
|
||||
newState = updateCurrentFolder(state, <SetCurrentFolderAction>action);
|
||||
break;
|
||||
@ -93,11 +95,6 @@ export function appReducer(
|
||||
case SearchActionTypes.HideFilter:
|
||||
newState = hideSearchFilter(state);
|
||||
break;
|
||||
case AppActionTypes.ToggleProcessServices:
|
||||
newState = updateProcessServices(state, <ToggleProcessServicesAction>(
|
||||
action
|
||||
));
|
||||
break;
|
||||
default:
|
||||
newState = Object.assign({}, state);
|
||||
}
|
||||
@ -123,15 +120,6 @@ function showSearchFilter(state: AppState): AppState {
|
||||
return newState;
|
||||
}
|
||||
|
||||
function updateLanguagePicker(
|
||||
state: AppState,
|
||||
action: SetLanguagePickerAction
|
||||
): AppState {
|
||||
const newState = Object.assign({}, state);
|
||||
newState.languagePicker = action.payload;
|
||||
return newState;
|
||||
}
|
||||
|
||||
function updateUser(state: AppState, action: SetUserProfileAction): AppState {
|
||||
const newState = Object.assign({}, state);
|
||||
const user = action.payload.person;
|
||||
@ -275,11 +263,15 @@ function updateRepositoryStatus(
|
||||
return newState;
|
||||
}
|
||||
|
||||
function updateProcessServices(
|
||||
function handleSettingsUpdate(
|
||||
state: AppState,
|
||||
action: ToggleProcessServicesAction
|
||||
) {
|
||||
const newState = Object.assign({}, state);
|
||||
newState.processServices = action.payload;
|
||||
action: SetSettingsParameterAction
|
||||
): AppState {
|
||||
const newState = { ...state };
|
||||
const { payload } = action;
|
||||
|
||||
if (payload.name === 'languagePicker') {
|
||||
newState.languagePicker = payload.value ? true : false;
|
||||
}
|
||||
return newState;
|
||||
}
|
||||
|
38
src/app/types.ts
Normal file
38
src/app/types.ts
Normal file
@ -0,0 +1,38 @@
|
||||
/*!
|
||||
* @license
|
||||
* Alfresco Example Content Application
|
||||
*
|
||||
* Copyright (C) 2005 - 2020 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/>.
|
||||
*/
|
||||
|
||||
export interface SettingsGroupRef {
|
||||
id: string;
|
||||
name: string;
|
||||
parameters: Array<SettingsParameterRef>;
|
||||
}
|
||||
|
||||
export interface SettingsParameterRef {
|
||||
id?: string;
|
||||
name: string;
|
||||
key: string;
|
||||
type: 'string' | 'boolean';
|
||||
value?: any;
|
||||
}
|
@ -9,6 +9,45 @@
|
||||
"$description": "Core application extensions and features",
|
||||
"$references": ["aos.plugin.json", "app.header.json"],
|
||||
|
||||
"settings": [
|
||||
{
|
||||
"id": "app.settings",
|
||||
"name": "APP.SETTINGS.APPLICATION-SETTINGS",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "Language Picker",
|
||||
"key": "languagePicker",
|
||||
"type": "boolean",
|
||||
"value": true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "extensions.ai.settings",
|
||||
"name": "Extensions: AI",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "Enable AI Extensions",
|
||||
"key": "ai",
|
||||
"type": "boolean",
|
||||
"value": false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "extensions.ps.settings",
|
||||
"name": "Extensions: Process Services",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "Enable Process Services Extensions",
|
||||
"key": "processServices",
|
||||
"type": "boolean",
|
||||
"value": false
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
|
||||
"rules": [
|
||||
{
|
||||
"id": "app.toolbar.favorite.canAdd",
|
||||
|
Loading…
x
Reference in New Issue
Block a user