diff --git a/docs/extending/README.md b/docs/extending/README.md index 1a4c68a45..abe4ea059 100644 --- a/docs/extending/README.md +++ b/docs/extending/README.md @@ -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) diff --git a/docs/extending/settings.md b/docs/extending/settings.md new file mode 100644 index 000000000..581714336 --- /dev/null +++ b/docs/extending/settings.md @@ -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". + +![Custom settings group](../images/aca-settings-custom-group.png) + +## 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 | diff --git a/docs/images/aca-settings-custom-group.png b/docs/images/aca-settings-custom-group.png new file mode 100644 index 000000000..02360ac10 Binary files /dev/null and b/docs/images/aca-settings-custom-group.png differ diff --git a/extension.schema.json b/extension.schema.json index 99f2bebcf..0cdb45549 100644 --- a/extension.schema.json +++ b/extension.schema.json @@ -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", diff --git a/package-lock.json b/package-lock.json index c5377ff39..5fca7650f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -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", diff --git a/package.json b/package.json index 1db0d185e..a47dadc3a 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/projects/aca-shared/rules/src/app.rules.ts b/projects/aca-shared/rules/src/app.rules.ts index 822275c36..ab1e1e74d 100644 --- a/projects/aca-shared/rules/src/app.rules.ts +++ b/projects/aca-shared/rules/src/app.rules.ts @@ -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'; } diff --git a/projects/aca-shared/store/src/actions/app.actions.ts b/projects/aca-shared/store/src/actions/app.actions.ts index 62553ac57..0297858c4 100644 --- a/projects/aca-shared/store/src/actions/app.actions.ts +++ b/projects/aca-shared/store/src/actions/app.actions.ts @@ -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) {} -} diff --git a/projects/aca-shared/store/src/selectors/app.selectors.ts b/projects/aca-shared/store/src/selectors/app.selectors.ts index 46068b032..4ded19b5a 100644 --- a/projects/aca-shared/store/src/selectors/app.selectors.ts +++ b/projects/aca-shared/store/src/selectors/app.selectors.ts @@ -133,8 +133,3 @@ export const infoDrawerMetadataAspect = createSelector( selectApp, state => state.infoDrawerMetadataAspect ); - -export const getProcessServicesState = createSelector( - selectApp, - state => state.processServices -); diff --git a/projects/aca-shared/store/src/states/app.state.ts b/projects/aca-shared/store/src/states/app.state.ts index 70e8b4885..0f1d5feaf 100644 --- a/projects/aca-shared/store/src/states/app.state.ts +++ b/projects/aca-shared/store/src/states/app.state.ts @@ -44,7 +44,6 @@ export interface AppState { showFacetFilter: boolean; documentDisplayMode: string; repository: RepositoryInfo; - processServices: boolean; } export interface AppStore { diff --git a/src/app.config.json b/src/app.config.json index 1c41521d2..2ee711048 100644 --- a/src/app.config.json +++ b/src/app.config.json @@ -26,8 +26,6 @@ "viewer.maxRetries": 1, "sharedLinkDateTimePickerType": "date", "headerColor": "#ffffff", - "languagePicker": true, - "processServices": true, "pagination": { "size": 25, "supportedPageSizes": [25, 50, 100] diff --git a/src/app/app.component.spec.ts b/src/app/app.component.spec.ts index 339dc0170..829ee2784 100644 --- a/src/app/app.component.spec.ts +++ b/src/app/app.component.spec.ts @@ -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 ); }); diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 687d21318..03647143f 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -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('languagePicker'), - processServices: this.config.get('processServices'), + languagePicker: this.storage.getItem('languagePicker') === 'true', appName: this.config.get('application.name'), headerColor: this.config.get('headerColor'), logoPath: this.config.get('application.logo'), diff --git a/src/app/components/current-user/current-user.component.spec.ts b/src/app/components/current-user/current-user.component.spec.ts index 56528415d..ae733fe1c 100644 --- a/src/app/components/current-user/current-user.component.spec.ts +++ b/src/app/components/current-user/current-user.component.spec.ts @@ -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); diff --git a/src/app/components/settings/settings.component.html b/src/app/components/settings/settings.component.html index c6c2f6070..46f981a76 100644 --- a/src/app/components/settings/settings.component.html +++ b/src/app/components/settings/settings.component.html @@ -68,39 +68,40 @@ - + - - {{ 'APP.SETTINGS.APPLICATION-SETTINGS' | translate }} - - - - Language Picker - - - - - - Extensions + {{ group.name | translate }} -
- - Enable AI Extensions - +
+ + + + {{ param.name | translate }} + - - Enable Process Services Extensions - + + + {{ param.name | translate }} + + + + + + Unknown parameter type: {{ param.name | translate }} + + +
diff --git a/src/app/components/settings/settings.component.spec.ts b/src/app/components/settings/settings.component.spec.ts index 8baf89d4d..22dfda089 100644 --- a/src/app/components/settings/settings.component.spec.ts +++ b/src/app/components/settings/settings.component.spec.ts @@ -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; + 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); }); }); diff --git a/src/app/components/settings/settings.component.theme.scss b/src/app/components/settings/settings.component.theme.scss index 96644da0a..793cf9fff 100644 --- a/src/app/components/settings/settings.component.theme.scss +++ b/src/app/components/settings/settings.component.theme.scss @@ -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; diff --git a/src/app/components/settings/settings.component.ts b/src/app/components/settings/settings.component.ts index a47ea2910..bf48908ac 100644 --- a/src/app/components/settings/settings.component.ts +++ b/src/app/components/settings/settings.component.ts @@ -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; appName$: Observable; headerColor$: Observable; - languagePicker$: Observable; - aiExtensions$: Observable; - psExtensions$: Observable; + + get settingGroups(): SettingsGroupRef[] { + return this.appExtensions.settingGroups; + } constructor( + private appExtensions: AppExtensionService, private store: Store, 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 }) + ); } } diff --git a/src/app/extensions/extension.service.ts b/src/app/extensions/extension.service.ts index cb6b8da5b..c189c22b9 100644 --- a/src/app/extensions/extension.service.ts +++ b/src/app/extensions/extension.service.ts @@ -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 = []; + settingGroups: Array = []; documentListPresets: { files: Array; @@ -111,7 +112,6 @@ export class AppExtensionService implements RuleContext { repository: RepositoryInfo; withCredentials: boolean; languagePicker: boolean; - processServices: boolean; references$: Observable; @@ -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( + config, + 'settings' + ); + this.headerActions = this.loader.getContentActions( config, 'features.header' diff --git a/src/app/store/initial-state.ts b/src/app/store/initial-state.ts index b733e7484..7cf21fd0b 100644 --- a/src/app/store/initial-state.ts +++ b/src/app/store/initial-state.ts @@ -54,8 +54,7 @@ export const INITIAL_APP_STATE: AppState = { status: { isQuickShareEnabled: true } - }, - processServices: false + } }; export const INITIAL_STATE: AppStore = { diff --git a/src/app/store/reducers/app.reducer.ts b/src/app/store/reducers/app.reducer.ts index 7197764f4..da60fe598 100644 --- a/src/app/store/reducers/app.reducer.ts +++ b/src/app/store/reducers/app.reducer.ts @@ -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({}, (action).payload); break; + case AppActionTypes.SetSettingsParameter: + newState = handleSettingsUpdate( + state, + action as SetSettingsParameterAction + ); + break; case NodeActionTypes.SetSelection: newState = updateSelectedNodes(state, action); break; case AppActionTypes.SetUserProfile: newState = updateUser(state, action); break; - case AppActionTypes.SetLanguagePicker: - newState = updateLanguagePicker(state, action); - break; case AppActionTypes.SetCurrentFolder: newState = updateCurrentFolder(state, action); break; @@ -93,11 +95,6 @@ export function appReducer( case SearchActionTypes.HideFilter: newState = hideSearchFilter(state); break; - case AppActionTypes.ToggleProcessServices: - newState = updateProcessServices(state, ( - 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; } diff --git a/src/app/types.ts b/src/app/types.ts new file mode 100644 index 000000000..37d291eca --- /dev/null +++ b/src/app/types.ts @@ -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 . + */ + +export interface SettingsGroupRef { + id: string; + name: string; + parameters: Array; +} + +export interface SettingsParameterRef { + id?: string; + name: string; + key: string; + type: 'string' | 'boolean'; + value?: any; +} diff --git a/src/assets/app.extensions.json b/src/assets/app.extensions.json index 8c4301175..19771bebf 100644 --- a/src/assets/app.extensions.json +++ b/src/assets/app.extensions.json @@ -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",