From be9f98cfd04c16a6b46b4f4f31e5ce7d3b92ffcf Mon Sep 17 00:00:00 2001 From: FinnStutzenstein Date: Thu, 20 Sep 2018 13:03:51 +0200 Subject: [PATCH] App initialization Used for internal apps as well as for plugins. The pluginpart is currently missing, in fact that the main OpenSlides part is more important. Apps can give their models and mainmenu entries. Routes are not enabled, because the routes have to be static for webpack to build the bundles. If we want to keep lazy loading, I see no possibility to encapsulate the routes from the site-routing module. --- client/src/app/app.module.ts | 13 +++- .../core/services/app-load.service.spec.ts | 12 ++++ .../src/app/core/services/app-load.service.ts | 61 +++++++++++++++++++ .../app/core/services/autoupdate.service.ts | 8 ++- .../collectionStringModelMapper.service.ts | 51 ++++++++++------ .../app/core/services/data-store.service.ts | 6 +- .../core/services/main-menu.service.spec.ts | 15 +++++ .../app/core/services/main-menu.service.ts | 61 +++++++++++++++++++ client/src/app/shared/models/agenda/item.ts | 2 - .../shared/models/assignments/assignment.ts | 2 - .../src/app/shared/models/base/base-model.ts | 10 --- .../app/shared/models/core/chat-message.ts | 2 - client/src/app/shared/models/core/config.ts | 2 - .../src/app/shared/models/core/countdown.ts | 2 - .../shared/models/core/projector-message.ts | 2 - .../src/app/shared/models/core/projector.ts | 2 - client/src/app/shared/models/core/tag.ts | 2 - .../app/shared/models/mediafiles/mediafile.ts | 2 - .../src/app/shared/models/motions/category.ts | 2 - .../app/shared/models/motions/motion-block.ts | 2 - .../models/motions/motion-change-reco.ts | 2 - .../models/motions/motion-comment-section.ts | 2 - .../src/app/shared/models/motions/motion.ts | 9 --- .../src/app/shared/models/motions/workflow.ts | 2 - client/src/app/shared/models/topics/topic.ts | 2 - client/src/app/shared/models/users/group.ts | 2 - .../app/shared/models/users/personal-note.ts | 2 - client/src/app/shared/models/users/user.ts | 2 - client/src/app/site/agenda/agenda.config.ts | 17 ++++++ .../site/assignments/assignments.config.ts | 16 +++++ client/src/app/site/base/app-config.ts | 25 ++++++++ .../app/site/common/common-routing.module.ts | 26 ++++++++ client/src/app/site/common/common.config.ts | 26 ++++++++ .../src/app/site/common/common.module.spec.ts | 13 ++++ client/src/app/site/common/common.module.ts | 14 +++++ .../legal-notice/legal-notice.component.html | 0 .../legal-notice/legal-notice.component.scss | 0 .../legal-notice.component.spec.ts | 0 .../legal-notice/legal-notice.component.ts | 0 .../privacy-policy.component.html | 0 .../privacy-policy.component.scss | 0 .../privacy-policy.component.spec.ts | 0 .../privacy-policy.component.ts | 0 .../components}/start/start.component.css | 0 .../components}/start/start.component.html | 2 - .../components}/start/start.component.spec.ts | 0 .../components}/start/start.component.ts | 39 ++---------- .../app/site/mediafiles/mediafile.config.ts | 16 +++++ client/src/app/site/motions/motions.config.ts | 28 +++++++++ .../src/app/site/settings/settings.config.ts | 16 +++++ client/src/app/site/site-routing.module.ts | 13 +--- client/src/app/site/site.component.html | 36 +++-------- client/src/app/site/site.component.ts | 11 +--- client/src/app/site/site.module.ts | 7 +-- client/src/app/site/users/users.config.ts | 22 +++++++ client/src/plugins.ts | 1 + 56 files changed, 441 insertions(+), 169 deletions(-) create mode 100644 client/src/app/core/services/app-load.service.spec.ts create mode 100644 client/src/app/core/services/app-load.service.ts create mode 100644 client/src/app/core/services/main-menu.service.spec.ts create mode 100644 client/src/app/core/services/main-menu.service.ts create mode 100644 client/src/app/site/agenda/agenda.config.ts create mode 100644 client/src/app/site/assignments/assignments.config.ts create mode 100644 client/src/app/site/base/app-config.ts create mode 100644 client/src/app/site/common/common-routing.module.ts create mode 100644 client/src/app/site/common/common.config.ts create mode 100644 client/src/app/site/common/common.module.spec.ts create mode 100644 client/src/app/site/common/common.module.ts rename client/src/app/site/{ => common/components}/legal-notice/legal-notice.component.html (100%) rename client/src/app/site/{ => common/components}/legal-notice/legal-notice.component.scss (100%) rename client/src/app/site/{ => common/components}/legal-notice/legal-notice.component.spec.ts (100%) rename client/src/app/site/{ => common/components}/legal-notice/legal-notice.component.ts (100%) rename client/src/app/site/{ => common/components}/privacy-policy/privacy-policy.component.html (100%) rename client/src/app/site/{ => common/components}/privacy-policy/privacy-policy.component.scss (100%) rename client/src/app/site/{ => common/components}/privacy-policy/privacy-policy.component.spec.ts (100%) rename client/src/app/site/{ => common/components}/privacy-policy/privacy-policy.component.ts (100%) rename client/src/app/site/{ => common/components}/start/start.component.css (100%) rename client/src/app/site/{ => common/components}/start/start.component.html (87%) rename client/src/app/site/{ => common/components}/start/start.component.spec.ts (100%) rename client/src/app/site/{ => common/components}/start/start.component.ts (87%) create mode 100644 client/src/app/site/mediafiles/mediafile.config.ts create mode 100644 client/src/app/site/motions/motions.config.ts create mode 100644 client/src/app/site/settings/settings.config.ts create mode 100644 client/src/app/site/users/users.config.ts create mode 100644 client/src/plugins.ts diff --git a/client/src/app/app.module.ts b/client/src/app/app.module.ts index ac24aacfe..d6b279638 100644 --- a/client/src/app/app.module.ts +++ b/client/src/app/app.module.ts @@ -1,7 +1,7 @@ // angular modules import { BrowserModule } from '@angular/platform-browser'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; -import { NgModule } from '@angular/core'; +import { NgModule, APP_INITIALIZER } from '@angular/core'; import { HttpClientModule, HttpClient, HttpClientXsrfModule } from '@angular/common/http'; // Elementary App Components @@ -13,6 +13,7 @@ import { CoreModule } from './core/core.module'; import { TranslateModule, TranslateLoader } from '@ngx-translate/core'; import { PruningTranslationLoader } from './core/pruning-loader'; import { LoginModule } from './site/login/login.module'; +import { AppLoadService } from './core/services/app-load.service'; /** * For the translation module. Loads a Custom 'translation loader' and provides it as loader. @@ -21,6 +22,15 @@ import { LoginModule } from './site/login/login.module'; export function HttpLoaderFactory(http: HttpClient): PruningTranslationLoader { return new PruningTranslationLoader(http); } + +/** + * Returns a function that returns a promis that will be resolved, if all apps are loaded. + * @param appLoadService The service that loads the apps. + */ +export function AppLoaderFactory(appLoadService: AppLoadService): () => Promise { + return () => appLoadService.loadApps(); +} + /** * Global App Module. Keep it as clean as possible. */ @@ -45,6 +55,7 @@ export function HttpLoaderFactory(http: HttpClient): PruningTranslationLoader { CoreModule, LoginModule ], + providers: [{ provide: APP_INITIALIZER, useFactory: AppLoaderFactory, deps: [AppLoadService], multi: true }], bootstrap: [AppComponent] }) export class AppModule {} diff --git a/client/src/app/core/services/app-load.service.spec.ts b/client/src/app/core/services/app-load.service.spec.ts new file mode 100644 index 000000000..31fa3a792 --- /dev/null +++ b/client/src/app/core/services/app-load.service.spec.ts @@ -0,0 +1,12 @@ +import { TestBed, inject } from '@angular/core/testing'; +import { NotifyService } from './notify.service'; +describe('NotifyService', () => { + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [NotifyService] + }); + }); + it('should be created', inject([NotifyService], (service: NotifyService) => { + expect(service).toBeTruthy(); + })); +}); diff --git a/client/src/app/core/services/app-load.service.ts b/client/src/app/core/services/app-load.service.ts new file mode 100644 index 000000000..943974e90 --- /dev/null +++ b/client/src/app/core/services/app-load.service.ts @@ -0,0 +1,61 @@ +import { Injectable } from '@angular/core'; +import { plugins } from '../../../plugins'; +import { CommonAppConfig } from '../../site/common/common.config'; +import { ModelConstructor, BaseModel } from '../../shared/models/base/base-model'; +import { AppConfig } from '../../site/base/app-config'; +import { CollectionStringModelMapperService } from './collectionStringModelMapper.service'; +import { MediafileAppConfig } from '../../site/mediafiles/mediafile.config'; +import { MotionsAppConfig } from '../../site/motions/motions.config'; +import { SettingsAppConfig } from '../../site/settings/settings.config'; +import { AgendaAppConfig } from '../../site/agenda/agenda.config'; +import { AssignmentsAppConfig } from '../../site/assignments/assignments.config'; +import { UsersAppConfig } from '../../site/users/users.config'; +import { MainMenuService } from './main-menu.service'; + +/** + * A list of all app configurations of all delivered apps. + */ +const appConfigs: AppConfig[] = [ + CommonAppConfig, + SettingsAppConfig, + AgendaAppConfig, + AssignmentsAppConfig, + MotionsAppConfig, + MediafileAppConfig, + UsersAppConfig +]; + +/** + * Handles all incoming and outgoing notify messages via {@link WebsocketService}. + */ +@Injectable({ + providedIn: 'root' +}) +export class AppLoadService { + public constructor( + private modelMapper: CollectionStringModelMapperService, + private mainMenuService: MainMenuService + ) {} + + public async loadApps(): Promise { + if (plugins.length) { + console.log('plugins: ', plugins); + } + /*for (const pluginName of plugins) { + const plugin = await import('../../../../../plugins/' + pluginName + '/' + pluginName); + plugin.main(); + }*/ + appConfigs.forEach((config: AppConfig) => { + if (config.models) { + config.models.forEach(entry => { + this.modelMapper.registerCollectionElement(entry.collectionString, entry.model); + }); + } + if (config.mainMenuEntries) { + this.mainMenuService.registerEntries(config.mainMenuEntries); + } + }); + } + + private registerModels(models?: { collectionString: string; model: ModelConstructor }[]): void {} +} diff --git a/client/src/app/core/services/autoupdate.service.ts b/client/src/app/core/services/autoupdate.service.ts index 35ce98358..b88165f94 100644 --- a/client/src/app/core/services/autoupdate.service.ts +++ b/client/src/app/core/services/autoupdate.service.ts @@ -21,7 +21,11 @@ export class AutoupdateService extends OpenSlidesComponent { * Constructor to create the AutoupdateService. Calls the constructor of the parent class. * @param websocketService */ - public constructor(websocketService: WebsocketService, private DS: DataStoreService) { + public constructor( + websocketService: WebsocketService, + private DS: DataStoreService, + private modelMapper: CollectionStringModelMapperService + ) { super(); websocketService.getOberservable('autoupdate').subscribe(response => { this.storeResponse(response); @@ -61,7 +65,7 @@ export class AutoupdateService extends OpenSlidesComponent { // Add the objects to the DataStore. Object.keys(autoupdate.changed).forEach(collection => { - const targetClass = CollectionStringModelMapperService.getModelConstructor(collection); + const targetClass = this.modelMapper.getModelConstructor(collection); if (!targetClass) { // TODO: throw an error later.. /*throw new Error*/ console.log(`Unregistered resource ${collection}`); diff --git a/client/src/app/core/services/collectionStringModelMapper.service.ts b/client/src/app/core/services/collectionStringModelMapper.service.ts index 58dfa9e06..0cea0511f 100644 --- a/client/src/app/core/services/collectionStringModelMapper.service.ts +++ b/client/src/app/core/services/collectionStringModelMapper.service.ts @@ -1,8 +1,12 @@ +import { Injectable } from '@angular/core'; import { ModelConstructor, BaseModel } from '../../shared/models/base/base-model'; /** * Registeres the mapping of collection strings <--> actual types. Every Model should register itself here. */ +@Injectable({ + providedIn: 'root' +}) export class CollectionStringModelMapperService { /** * Mapps collection strings to model constructors. Accessed by {@method registerCollectionElement} and @@ -10,26 +14,10 @@ export class CollectionStringModelMapperService { */ private static collectionStringsTypeMapping: { [collectionString: string]: ModelConstructor } = {}; - /** - * Registers the type to the collection string - * @param collectionString - * @param type - */ - public static registerCollectionElement(collectionString: string, type: ModelConstructor): void { - CollectionStringModelMapperService.collectionStringsTypeMapping[collectionString] = type; - } - - /** - * Returns the constructor of the requested collection or undefined, if it is not registered. - * @param collectionString the requested collection - */ - public static getModelConstructor(collectionString: string): ModelConstructor { - return CollectionStringModelMapperService.collectionStringsTypeMapping[collectionString]; - } - /** * Returns the collection string of a given ModelConstructor or undefined, if it is not registered. * @param ctor + * @deprecated Should inject this service and don't use the static functions. */ public static getCollectionString(ctor: ModelConstructor): string { return Object.keys(CollectionStringModelMapperService.collectionStringsTypeMapping).find( @@ -44,4 +32,33 @@ export class CollectionStringModelMapperService { * @param websocketService */ public constructor() {} + + /** + * Registers the type to the collection string + * @param collectionString + * @param type + */ + public registerCollectionElement(collectionString: string, type: ModelConstructor): void { + CollectionStringModelMapperService.collectionStringsTypeMapping[collectionString] = type; + } + + /** + * Returns the constructor of the requested collection or undefined, if it is not registered. + * @param collectionString the requested collection + */ + public getModelConstructor(collectionString: string): ModelConstructor { + return CollectionStringModelMapperService.collectionStringsTypeMapping[collectionString]; + } + + /** + * Returns the collection string of a given ModelConstructor or undefined, if it is not registered. + * @param ctor + */ + public getCollectionString(ctor: ModelConstructor): string { + return Object.keys(CollectionStringModelMapperService.collectionStringsTypeMapping).find( + (collectionString: string) => { + return ctor === CollectionStringModelMapperService.collectionStringsTypeMapping[collectionString]; + } + ); + } } diff --git a/client/src/app/core/services/data-store.service.ts b/client/src/app/core/services/data-store.service.ts index 713e0667a..7d39876f4 100644 --- a/client/src/app/core/services/data-store.service.ts +++ b/client/src/app/core/services/data-store.service.ts @@ -115,7 +115,7 @@ export class DataStoreService { * Empty constructor for dataStore * @param cacheService use CacheService to cache the DataStore. */ - public constructor(private cacheService: CacheService) { + public constructor(private cacheService: CacheService, private modelMapper: CollectionStringModelMapperService) { if (DataStoreService.wasInstantiated) { throw new Error('The Datastore should just be instantiated once!'); } @@ -158,7 +158,7 @@ export class DataStoreService { const storage: ModelStorage = {}; Object.keys(serializedStore).forEach(collectionString => { storage[collectionString] = {} as ModelCollection; - const target = CollectionStringModelMapperService.getModelConstructor(collectionString); + const target = this.modelMapper.getModelConstructor(collectionString); if (target) { Object.keys(serializedStore[collectionString]).forEach(id => { const data = JSON.parse(serializedStore[collectionString][id]); @@ -186,7 +186,7 @@ export class DataStoreService { if (typeof collectionType === 'string') { return collectionType; } else { - return CollectionStringModelMapperService.getCollectionString(collectionType); + return this.modelMapper.getCollectionString(collectionType); } } diff --git a/client/src/app/core/services/main-menu.service.spec.ts b/client/src/app/core/services/main-menu.service.spec.ts new file mode 100644 index 000000000..7b6ce8a2c --- /dev/null +++ b/client/src/app/core/services/main-menu.service.spec.ts @@ -0,0 +1,15 @@ +import { TestBed, inject } from '@angular/core/testing'; + +import { MainMenuService } from './main-menu.service'; + +describe('MainMenuService', () => { + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [MainMenuService] + }); + }); + + it('should be created', inject([MainMenuService], (service: MainMenuService) => { + expect(service).toBeTruthy(); + })); +}); diff --git a/client/src/app/core/services/main-menu.service.ts b/client/src/app/core/services/main-menu.service.ts new file mode 100644 index 000000000..cd61bcbe1 --- /dev/null +++ b/client/src/app/core/services/main-menu.service.ts @@ -0,0 +1,61 @@ +import { Injectable } from '@angular/core'; + +/** + * This represents one entry in the main menu + */ +export interface MainMenuEntry { + /** + * The route for the router to navigate to on click. + */ + route: string; + /** + * The display string to be shown. + */ + displayName: string; + + /** + * The font awesom icon to display. + */ + icon: string; + + /** + * For sorting the entries. + */ + weight: number; + + /** + * The permission to see the entry. + */ + permission: string; +} + +/** + * Collects main menu entries and provides them to the main menu component. + */ +@Injectable({ + providedIn: 'root' +}) +export class MainMenuService { + /** + * A list of sorted entries. + */ + private _entries: MainMenuEntry[] = []; + + /** + * Make the entries public. + */ + public get entries(): MainMenuEntry[] { + return this._entries; + } + + public constructor() {} + + /** + * Adds entries to the mainmenu. + * @param entries The entries to add + */ + public registerEntries(entries: MainMenuEntry[]): void { + this._entries.push(...entries); + this._entries = this._entries.sort((a, b) => a.weight - b.weight); + } +} diff --git a/client/src/app/shared/models/agenda/item.ts b/client/src/app/shared/models/agenda/item.ts index ce697738f..1cc600885 100644 --- a/client/src/app/shared/models/agenda/item.ts +++ b/client/src/app/shared/models/agenda/item.ts @@ -56,5 +56,3 @@ export class Item extends ProjectableBaseModel { return this.getListTitle(); } } - -ProjectableBaseModel.registerCollectionElement('agenda/item', Item); diff --git a/client/src/app/shared/models/assignments/assignment.ts b/client/src/app/shared/models/assignments/assignment.ts index fd86b28da..40931e2b6 100644 --- a/client/src/app/shared/models/assignments/assignment.ts +++ b/client/src/app/shared/models/assignments/assignment.ts @@ -56,5 +56,3 @@ export class Assignment extends AgendaBaseModel { return 'TODO'; } } - -AgendaBaseModel.registerCollectionElement('assignments/assignment', Assignment); diff --git a/client/src/app/shared/models/base/base-model.ts b/client/src/app/shared/models/base/base-model.ts index ad7463812..26d90f84d 100644 --- a/client/src/app/shared/models/base/base-model.ts +++ b/client/src/app/shared/models/base/base-model.ts @@ -1,6 +1,5 @@ import { OpenSlidesComponent } from 'app/openslides.component'; import { Deserializable } from './deserializable'; -import { CollectionStringModelMapperService } from '../../../core/services/collectionStringModelMapper.service'; import { Displayable } from './displayable'; import { Identifiable } from './identifiable'; @@ -14,15 +13,6 @@ export interface ModelConstructor> { */ export abstract class BaseModel extends OpenSlidesComponent implements Deserializable, Displayable, Identifiable { - /** - * Register the collection string to the type. - * @param collectionString - * @param type - */ - public static registerCollectionElement(collectionString: string, type: any): void { - CollectionStringModelMapperService.registerCollectionElement(collectionString, type); - } - /** * force children of BaseModel to have a collectionString. * diff --git a/client/src/app/shared/models/core/chat-message.ts b/client/src/app/shared/models/core/chat-message.ts index f9e377298..c18835264 100644 --- a/client/src/app/shared/models/core/chat-message.ts +++ b/client/src/app/shared/models/core/chat-message.ts @@ -18,5 +18,3 @@ export class ChatMessage extends BaseModel { return 'Chatmessage'; } } - -BaseModel.registerCollectionElement('core/chat-message', ChatMessage); diff --git a/client/src/app/shared/models/core/config.ts b/client/src/app/shared/models/core/config.ts index 2899902b6..402235585 100644 --- a/client/src/app/shared/models/core/config.ts +++ b/client/src/app/shared/models/core/config.ts @@ -17,5 +17,3 @@ export class Config extends BaseModel { return this.key; } } - -BaseModel.registerCollectionElement('core/config', Config); diff --git a/client/src/app/shared/models/core/countdown.ts b/client/src/app/shared/models/core/countdown.ts index 06245258e..309ebedee 100644 --- a/client/src/app/shared/models/core/countdown.ts +++ b/client/src/app/shared/models/core/countdown.ts @@ -19,5 +19,3 @@ export class Countdown extends ProjectableBaseModel { return this.description; } } - -ProjectableBaseModel.registerCollectionElement('core/countdown', Countdown); diff --git a/client/src/app/shared/models/core/projector-message.ts b/client/src/app/shared/models/core/projector-message.ts index a49dcf79e..d1d4f7208 100644 --- a/client/src/app/shared/models/core/projector-message.ts +++ b/client/src/app/shared/models/core/projector-message.ts @@ -16,5 +16,3 @@ export class ProjectorMessage extends ProjectableBaseModel { return 'Projectormessage'; } } - -ProjectableBaseModel.registerCollectionElement('core/projector-message', ProjectorMessage); diff --git a/client/src/app/shared/models/core/projector.ts b/client/src/app/shared/models/core/projector.ts index f2200aa61..e3686278a 100644 --- a/client/src/app/shared/models/core/projector.ts +++ b/client/src/app/shared/models/core/projector.ts @@ -23,5 +23,3 @@ export class Projector extends BaseModel { return this.name; } } - -BaseModel.registerCollectionElement('core/projector', Projector); diff --git a/client/src/app/shared/models/core/tag.ts b/client/src/app/shared/models/core/tag.ts index b8e05d918..79b00d027 100644 --- a/client/src/app/shared/models/core/tag.ts +++ b/client/src/app/shared/models/core/tag.ts @@ -16,5 +16,3 @@ export class Tag extends BaseModel { return this.name; } } - -BaseModel.registerCollectionElement('core/tag', Tag); diff --git a/client/src/app/shared/models/mediafiles/mediafile.ts b/client/src/app/shared/models/mediafiles/mediafile.ts index bebde6364..846cac425 100644 --- a/client/src/app/shared/models/mediafiles/mediafile.ts +++ b/client/src/app/shared/models/mediafiles/mediafile.ts @@ -28,5 +28,3 @@ export class Mediafile extends ProjectableBaseModel { return this.title; } } - -ProjectableBaseModel.registerCollectionElement('mediafiles/mediafile', Mediafile); diff --git a/client/src/app/shared/models/motions/category.ts b/client/src/app/shared/models/motions/category.ts index 7dfee4f21..ffbaeaef3 100644 --- a/client/src/app/shared/models/motions/category.ts +++ b/client/src/app/shared/models/motions/category.ts @@ -17,5 +17,3 @@ export class Category extends BaseModel { return this.prefix + ' - ' + this.name; } } - -BaseModel.registerCollectionElement('motions/category', Category); diff --git a/client/src/app/shared/models/motions/motion-block.ts b/client/src/app/shared/models/motions/motion-block.ts index da2eb781f..d63010638 100644 --- a/client/src/app/shared/models/motions/motion-block.ts +++ b/client/src/app/shared/models/motions/motion-block.ts @@ -21,5 +21,3 @@ export class MotionBlock extends AgendaBaseModel { return 'TODO'; } } - -AgendaBaseModel.registerCollectionElement('motions/motion-block', MotionBlock); diff --git a/client/src/app/shared/models/motions/motion-change-reco.ts b/client/src/app/shared/models/motions/motion-change-reco.ts index 6f2f24393..24035b643 100644 --- a/client/src/app/shared/models/motions/motion-change-reco.ts +++ b/client/src/app/shared/models/motions/motion-change-reco.ts @@ -23,5 +23,3 @@ export class MotionChangeReco extends BaseModel { return 'Changerecommendation'; } } - -BaseModel.registerCollectionElement('motions/motion-change-recommendation', MotionChangeReco); diff --git a/client/src/app/shared/models/motions/motion-comment-section.ts b/client/src/app/shared/models/motions/motion-comment-section.ts index 8217db479..7b38f191f 100644 --- a/client/src/app/shared/models/motions/motion-comment-section.ts +++ b/client/src/app/shared/models/motions/motion-comment-section.ts @@ -18,5 +18,3 @@ export class MotionCommentSection extends BaseModel { return this.name; } } - -BaseModel.registerCollectionElement('motions/motion-comment-section', MotionCommentSection); diff --git a/client/src/app/shared/models/motions/motion.ts b/client/src/app/shared/models/motions/motion.ts index 3f1427870..83c62f8be 100644 --- a/client/src/app/shared/models/motions/motion.ts +++ b/client/src/app/shared/models/motions/motion.ts @@ -1,8 +1,6 @@ import { MotionSubmitter } from './motion-submitter'; import { MotionLog } from './motion-log'; -import { Category } from './category'; import { MotionComment } from './motion-comment'; -import { Workflow } from './workflow'; import { AgendaBaseModel } from '../base/agenda-base-model'; /** @@ -99,10 +97,3 @@ export class Motion extends AgendaBaseModel { } } } - -/** - * Hack to get them loaded at last - */ -AgendaBaseModel.registerCollectionElement('motions/motion', Motion); -AgendaBaseModel.registerCollectionElement('motions/category', Category); -AgendaBaseModel.registerCollectionElement('motions/workflow', Workflow); diff --git a/client/src/app/shared/models/motions/workflow.ts b/client/src/app/shared/models/motions/workflow.ts index 5e8de65cd..866563fd9 100644 --- a/client/src/app/shared/models/motions/workflow.ts +++ b/client/src/app/shared/models/motions/workflow.ts @@ -58,5 +58,3 @@ export class Workflow extends BaseModel { return this.name; } } - -BaseModel.registerCollectionElement('motions/workflow', Workflow); diff --git a/client/src/app/shared/models/topics/topic.ts b/client/src/app/shared/models/topics/topic.ts index 96a228a3a..cf4255cf8 100644 --- a/client/src/app/shared/models/topics/topic.ts +++ b/client/src/app/shared/models/topics/topic.ts @@ -28,5 +28,3 @@ export class Topic extends AgendaBaseModel { return 'TODO'; } } - -AgendaBaseModel.registerCollectionElement('topics/topic', Topic); diff --git a/client/src/app/shared/models/users/group.ts b/client/src/app/shared/models/users/group.ts index fdca2c193..a31ded7ff 100644 --- a/client/src/app/shared/models/users/group.ts +++ b/client/src/app/shared/models/users/group.ts @@ -17,5 +17,3 @@ export class Group extends BaseModel { return this.name; } } - -BaseModel.registerCollectionElement('users/group', Group); diff --git a/client/src/app/shared/models/users/personal-note.ts b/client/src/app/shared/models/users/personal-note.ts index e9cd607ed..bb229fced 100644 --- a/client/src/app/shared/models/users/personal-note.ts +++ b/client/src/app/shared/models/users/personal-note.ts @@ -17,5 +17,3 @@ export class PersonalNote extends BaseModel { return 'Personal note'; } } - -BaseModel.registerCollectionElement('users/personal-note', PersonalNote); diff --git a/client/src/app/shared/models/users/user.ts b/client/src/app/shared/models/users/user.ts index 208c45c89..11eed4b02 100644 --- a/client/src/app/shared/models/users/user.ts +++ b/client/src/app/shared/models/users/user.ts @@ -89,5 +89,3 @@ export class User extends ProjectableBaseModel { return this.short_name; } } - -ProjectableBaseModel.registerCollectionElement('users/user', User); diff --git a/client/src/app/site/agenda/agenda.config.ts b/client/src/app/site/agenda/agenda.config.ts new file mode 100644 index 000000000..8218c9943 --- /dev/null +++ b/client/src/app/site/agenda/agenda.config.ts @@ -0,0 +1,17 @@ +import { AppConfig } from '../base/app-config'; +import { Item } from '../../shared/models/agenda/item'; +import { Topic } from '../../shared/models/topics/topic'; + +export const AgendaAppConfig: AppConfig = { + name: 'agenda', + models: [{ collectionString: 'agenda/item', model: Item }, { collectionString: 'topics/topic', model: Topic }], + mainMenuEntries: [ + { + route: '/agenda', + displayName: 'Agenda', + icon: 'calendar', + weight: 200, + permission: 'agenda.can_see' + } + ] +}; diff --git a/client/src/app/site/assignments/assignments.config.ts b/client/src/app/site/assignments/assignments.config.ts new file mode 100644 index 000000000..58414fb68 --- /dev/null +++ b/client/src/app/site/assignments/assignments.config.ts @@ -0,0 +1,16 @@ +import { AppConfig } from '../base/app-config'; +import { Assignment } from '../../shared/models/assignments/assignment'; + +export const AssignmentsAppConfig: AppConfig = { + name: 'assignments', + models: [{ collectionString: 'assignments/assignment', model: Assignment }], + mainMenuEntries: [ + { + route: '/assignments', + displayName: 'Elections', + icon: 'chart-pie', + weight: 400, + permission: 'assignments.can_see' + } + ] +}; diff --git a/client/src/app/site/base/app-config.ts b/client/src/app/site/base/app-config.ts new file mode 100644 index 000000000..e2ba1bd34 --- /dev/null +++ b/client/src/app/site/base/app-config.ts @@ -0,0 +1,25 @@ +import { ModelConstructor, BaseModel } from '../../shared/models/base/base-model'; +import { MainMenuEntry } from '../../core/services/main-menu.service'; + +/** + * The configuration of an app. + */ +export interface AppConfig { + /** + * The name. + */ + name: string; + + /** + * All models, that should be registered. + */ + models?: { + collectionString: string; + model: ModelConstructor; + }[]; + + /** + * Main menu entries. + */ + mainMenuEntries?: MainMenuEntry[]; +} diff --git a/client/src/app/site/common/common-routing.module.ts b/client/src/app/site/common/common-routing.module.ts new file mode 100644 index 000000000..87f463e52 --- /dev/null +++ b/client/src/app/site/common/common-routing.module.ts @@ -0,0 +1,26 @@ +import { NgModule } from '@angular/core'; +import { Routes, RouterModule } from '@angular/router'; +import { PrivacyPolicyComponent } from './components/privacy-policy/privacy-policy.component'; +import { StartComponent } from './components/start/start.component'; +import { LegalNoticeComponent } from './components/legal-notice/legal-notice.component'; + +const routes: Routes = [ + { + path: '', + component: StartComponent + }, + { + path: 'legalnotice', + component: LegalNoticeComponent + }, + { + path: 'privacypolicy', + component: PrivacyPolicyComponent + } +]; + +@NgModule({ + imports: [RouterModule.forChild(routes)], + exports: [RouterModule] +}) +export class CommonRoutingModule {} diff --git a/client/src/app/site/common/common.config.ts b/client/src/app/site/common/common.config.ts new file mode 100644 index 000000000..9b52d460b --- /dev/null +++ b/client/src/app/site/common/common.config.ts @@ -0,0 +1,26 @@ +import { AppConfig } from '../base/app-config'; +import { Projector } from '../../shared/models/core/projector'; +import { Countdown } from '../../shared/models/core/countdown'; +import { ChatMessage } from '../../shared/models/core/chat-message'; +import { ProjectorMessage } from '../../shared/models/core/projector-message'; +import { Tag } from '../../shared/models/core/tag'; + +export const CommonAppConfig: AppConfig = { + name: 'common', + models: [ + { collectionString: 'core/projector', model: Projector }, + { collectionString: 'core/chat-message', model: ChatMessage }, + { collectionString: 'core/countdown', model: Countdown }, + { collectionString: 'core/projector-message', model: ProjectorMessage }, + { collectionString: 'core/tag', model: Tag } + ], + mainMenuEntries: [ + { + route: '/', + displayName: 'Home', + icon: 'home', + weight: 100, + permission: 'core.can_see_frontpage' + } + ] +}; diff --git a/client/src/app/site/common/common.module.spec.ts b/client/src/app/site/common/common.module.spec.ts new file mode 100644 index 000000000..06175936f --- /dev/null +++ b/client/src/app/site/common/common.module.spec.ts @@ -0,0 +1,13 @@ +import { CommonModule } from './common.module'; + +describe('CommonModule', () => { + let commonModule: CommonModule; + + beforeEach(() => { + commonModule = new CommonModule(); + }); + + it('should create an instance', () => { + expect(commonModule).toBeTruthy(); + }); +}); diff --git a/client/src/app/site/common/common.module.ts b/client/src/app/site/common/common.module.ts new file mode 100644 index 000000000..b5fb513ce --- /dev/null +++ b/client/src/app/site/common/common.module.ts @@ -0,0 +1,14 @@ +import { NgModule } from '@angular/core'; +import { CommonModule as AngularCommonModule } from '@angular/common'; + +import { CommonRoutingModule } from './common-routing.module'; +import { SharedModule } from '../../shared/shared.module'; +import { PrivacyPolicyComponent } from './components/privacy-policy/privacy-policy.component'; +import { StartComponent } from './components/start/start.component'; +import { LegalNoticeComponent } from './components/legal-notice/legal-notice.component'; + +@NgModule({ + imports: [AngularCommonModule, CommonRoutingModule, SharedModule], + declarations: [PrivacyPolicyComponent, StartComponent, LegalNoticeComponent] +}) +export class CommonModule {} diff --git a/client/src/app/site/legal-notice/legal-notice.component.html b/client/src/app/site/common/components/legal-notice/legal-notice.component.html similarity index 100% rename from client/src/app/site/legal-notice/legal-notice.component.html rename to client/src/app/site/common/components/legal-notice/legal-notice.component.html diff --git a/client/src/app/site/legal-notice/legal-notice.component.scss b/client/src/app/site/common/components/legal-notice/legal-notice.component.scss similarity index 100% rename from client/src/app/site/legal-notice/legal-notice.component.scss rename to client/src/app/site/common/components/legal-notice/legal-notice.component.scss diff --git a/client/src/app/site/legal-notice/legal-notice.component.spec.ts b/client/src/app/site/common/components/legal-notice/legal-notice.component.spec.ts similarity index 100% rename from client/src/app/site/legal-notice/legal-notice.component.spec.ts rename to client/src/app/site/common/components/legal-notice/legal-notice.component.spec.ts diff --git a/client/src/app/site/legal-notice/legal-notice.component.ts b/client/src/app/site/common/components/legal-notice/legal-notice.component.ts similarity index 100% rename from client/src/app/site/legal-notice/legal-notice.component.ts rename to client/src/app/site/common/components/legal-notice/legal-notice.component.ts diff --git a/client/src/app/site/privacy-policy/privacy-policy.component.html b/client/src/app/site/common/components/privacy-policy/privacy-policy.component.html similarity index 100% rename from client/src/app/site/privacy-policy/privacy-policy.component.html rename to client/src/app/site/common/components/privacy-policy/privacy-policy.component.html diff --git a/client/src/app/site/privacy-policy/privacy-policy.component.scss b/client/src/app/site/common/components/privacy-policy/privacy-policy.component.scss similarity index 100% rename from client/src/app/site/privacy-policy/privacy-policy.component.scss rename to client/src/app/site/common/components/privacy-policy/privacy-policy.component.scss diff --git a/client/src/app/site/privacy-policy/privacy-policy.component.spec.ts b/client/src/app/site/common/components/privacy-policy/privacy-policy.component.spec.ts similarity index 100% rename from client/src/app/site/privacy-policy/privacy-policy.component.spec.ts rename to client/src/app/site/common/components/privacy-policy/privacy-policy.component.spec.ts diff --git a/client/src/app/site/privacy-policy/privacy-policy.component.ts b/client/src/app/site/common/components/privacy-policy/privacy-policy.component.ts similarity index 100% rename from client/src/app/site/privacy-policy/privacy-policy.component.ts rename to client/src/app/site/common/components/privacy-policy/privacy-policy.component.ts diff --git a/client/src/app/site/start/start.component.css b/client/src/app/site/common/components/start/start.component.css similarity index 100% rename from client/src/app/site/start/start.component.css rename to client/src/app/site/common/components/start/start.component.css diff --git a/client/src/app/site/start/start.component.html b/client/src/app/site/common/components/start/start.component.html similarity index 87% rename from client/src/app/site/start/start.component.html rename to client/src/app/site/common/components/start/start.component.html index 96ddaa45c..cd8d3f639 100644 --- a/client/src/app/site/start/start.component.html +++ b/client/src/app/site/common/components/start/start.component.html @@ -7,8 +7,6 @@

{{welcomeTitle | translate}}

{{welcomeText | translate}} - -

diff --git a/client/src/app/site/start/start.component.spec.ts b/client/src/app/site/common/components/start/start.component.spec.ts similarity index 100% rename from client/src/app/site/start/start.component.spec.ts rename to client/src/app/site/common/components/start/start.component.spec.ts diff --git a/client/src/app/site/start/start.component.ts b/client/src/app/site/common/components/start/start.component.ts similarity index 87% rename from client/src/app/site/start/start.component.ts rename to client/src/app/site/common/components/start/start.component.ts index 236ebdb0d..afc7ffd87 100644 --- a/client/src/app/site/start/start.component.ts +++ b/client/src/app/site/common/components/start/start.component.ts @@ -5,11 +5,10 @@ import { BaseComponent } from 'app/base.component'; import { TranslateService } from '@ngx-translate/core'; // showcase // for testing the DS and BaseModel -import { User } from 'app/shared/models/users/user'; -import { Config } from '../../shared/models/core/config'; -import { Motion } from '../../shared/models/motions/motion'; -import { MotionSubmitter } from '../../shared/models/motions/motion-submitter'; -import { DataStoreService } from '../../core/services/data-store.service'; +import { Config } from '../../../../shared/models/core/config'; +import { Motion } from '../../../../shared/models/motions/motion'; +import { MotionSubmitter } from '../../../../shared/models/motions/motion-submitter'; +import { DataStoreService } from '../../../../core/services/data-store.service'; @Component({ selector: 'os-start', @@ -74,36 +73,6 @@ export class StartComponent extends BaseComponent implements OnInit { }); } - /** - * test data store - */ - public DataStoreTest(): void { - console.log('add a user to dataStore'); - this.DS.add(new User({ id: 100 })); - console.log('add three users to dataStore'); - this.DS.add(new User({ id: 200 }), new User({ id: 201 }), new User({ id: 202 })); - console.log('use the spread operator "..." to add an array'); - const userArray = []; - for (let i = 300; i < 400; i++) { - userArray.push(new User({ id: i })); - } - this.DS.add(...userArray); - - console.log('try to get user with ID 1:'); - const user1fromStore = this.DS.get(User, 1); - console.log('the user: ', user1fromStore); - - console.log('remove a single user:'); - this.DS.remove('users/user', 100); - console.log('remove more users'); - this.DS.remove('users/user', 200, 201, 202); - console.log('remove an array of users'); - this.DS.remove('users/user', ...[321, 363, 399]); - - console.log('test filter: '); - console.log(this.DS.filter(User, user => user.id === 1)); - } - /** * function to print datastore */ diff --git a/client/src/app/site/mediafiles/mediafile.config.ts b/client/src/app/site/mediafiles/mediafile.config.ts new file mode 100644 index 000000000..e3b190c86 --- /dev/null +++ b/client/src/app/site/mediafiles/mediafile.config.ts @@ -0,0 +1,16 @@ +import { AppConfig } from '../base/app-config'; +import { Mediafile } from '../../shared/models/mediafiles/mediafile'; + +export const MediafileAppConfig: AppConfig = { + name: 'mediafiles', + models: [{ collectionString: 'mediafiles/mediafile', model: Mediafile }], + mainMenuEntries: [ + { + route: '/mediafiles', + displayName: 'Files', + icon: 'paperclip', + weight: 600, + permission: 'mediafiles.can_see' + } + ] +}; diff --git a/client/src/app/site/motions/motions.config.ts b/client/src/app/site/motions/motions.config.ts new file mode 100644 index 000000000..58ede741e --- /dev/null +++ b/client/src/app/site/motions/motions.config.ts @@ -0,0 +1,28 @@ +import { AppConfig } from '../base/app-config'; +import { Motion } from '../../shared/models/motions/motion'; +import { Category } from '../../shared/models/motions/category'; +import { Workflow } from '../../shared/models/motions/workflow'; +import { MotionCommentSection } from '../../shared/models/motions/motion-comment-section'; +import { MotionChangeReco } from '../../shared/models/motions/motion-change-reco'; +import { MotionBlock } from '../../shared/models/motions/motion-block'; + +export const MotionsAppConfig: AppConfig = { + name: 'motions', + models: [ + { collectionString: 'motions/motion', model: Motion }, + { collectionString: 'motions/category', model: Category }, + { collectionString: 'motions/workflow', model: Workflow }, + { collectionString: 'motions/motion-comment-section', model: MotionCommentSection }, + { collectionString: 'motions/motion-change-recommendation', model: MotionChangeReco }, + { collectionString: 'motions/motion-block', model: MotionBlock } + ], + mainMenuEntries: [ + { + route: '/motions', + displayName: 'Motions', + icon: 'file-alt', + weight: 300, + permission: 'motions.can_see' + } + ] +}; diff --git a/client/src/app/site/settings/settings.config.ts b/client/src/app/site/settings/settings.config.ts new file mode 100644 index 000000000..43164072c --- /dev/null +++ b/client/src/app/site/settings/settings.config.ts @@ -0,0 +1,16 @@ +import { AppConfig } from '../base/app-config'; +import { Config } from '../../shared/models/core/config'; + +export const SettingsAppConfig: AppConfig = { + name: 'settings', + models: [{ collectionString: 'core/config', model: Config }], + mainMenuEntries: [ + { + route: '/settings', + displayName: 'Settings', + icon: 'cog', + weight: 700, + permission: 'core.can_manage_config' + } + ] +}; diff --git a/client/src/app/site/site-routing.module.ts b/client/src/app/site/site-routing.module.ts index 5fd843bdd..c4e4ec20b 100644 --- a/client/src/app/site/site-routing.module.ts +++ b/client/src/app/site/site-routing.module.ts @@ -3,9 +3,6 @@ import { Routes, RouterModule } from '@angular/router'; import { SiteComponent } from './site.component'; -import { StartComponent } from './start/start.component'; -import { PrivacyPolicyComponent } from './privacy-policy/privacy-policy.component'; -import { LegalNoticeComponent } from './legal-notice/legal-notice.component'; import { AuthGuard } from '../core/services/auth-guard.service'; /** @@ -20,15 +17,7 @@ const routes: Routes = [ children: [ { path: '', - component: StartComponent - }, - { - path: 'legalnotice', - component: LegalNoticeComponent - }, - { - path: 'privacypolicy', - component: PrivacyPolicyComponent + loadChildren: './common/common.module#CommonModule' }, { path: 'agenda', diff --git a/client/src/app/site/site.component.html b/client/src/app/site/site.component.html index b62f5a6b7..2e7163034 100644 --- a/client/src/app/site/site.component.html +++ b/client/src/app/site/site.component.html @@ -45,35 +45,13 @@ - - - Home - - - - Agenda - - - - Motions - - - - Assignments - - - - Participants - - - - Files - - - - Settings - + + + + {{ entry.displayName | translate}} + + diff --git a/client/src/app/site/site.component.ts b/client/src/app/site/site.component.ts index 0d43b4c7f..8487f4e19 100644 --- a/client/src/app/site/site.component.ts +++ b/client/src/app/site/site.component.ts @@ -9,8 +9,7 @@ import { BaseComponent } from 'app/base.component'; import { pageTransition, navItemAnim } from 'app/shared/animations'; import { MatDialog, MatSidenav } from '@angular/material'; import { ViewportService } from '../core/services/viewport.service'; -import { Projector } from '../shared/models/core/projector'; -import { Tag } from '../shared/models/core/tag'; +import { MainMenuService } from '../core/services/main-menu.service'; @Component({ selector: 'os-site', @@ -51,7 +50,8 @@ export class SiteComponent extends BaseComponent implements OnInit { public operator: OperatorService, public vp: ViewportService, public translate: TranslateService, - public dialog: MatDialog + public dialog: MatDialog, + public mainMenuService: MainMenuService // used in the component ) { super(); @@ -75,11 +75,6 @@ export class SiteComponent extends BaseComponent implements OnInit { // this.translate.get('Motions').subscribe((res: string) => { // console.log('translation of motions in the target language: ' + res); // }); - - // tslint:disable-next-line - const p: Projector = new Projector(); // Needed, that the Projector.ts is loaded. Can be removed, if something else creates/uses projectors. - // tslint:disable-next-line - const t: Tag = new Tag(); // Needed, that the Tag.ts is loaded. Can be removed, if something else creates/uses tags. } /** diff --git a/client/src/app/site/site.module.ts b/client/src/app/site/site.module.ts index 8c4c355b2..bd6438e60 100644 --- a/client/src/app/site/site.module.ts +++ b/client/src/app/site/site.module.ts @@ -1,16 +1,13 @@ import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; -import { SiteRoutingModule } from './site-routing.module'; import { SharedModule } from 'app/shared/shared.module'; import { SiteComponent } from './site.component'; -import { StartComponent } from './start/start.component'; -import { LegalNoticeComponent } from './legal-notice/legal-notice.component'; -import { PrivacyPolicyComponent } from './privacy-policy/privacy-policy.component'; +import { SiteRoutingModule } from './site-routing.module'; @NgModule({ imports: [CommonModule, SharedModule, SiteRoutingModule], - declarations: [SiteComponent, StartComponent, LegalNoticeComponent, PrivacyPolicyComponent] + declarations: [SiteComponent] }) export class SiteModule {} diff --git a/client/src/app/site/users/users.config.ts b/client/src/app/site/users/users.config.ts new file mode 100644 index 000000000..18194f219 --- /dev/null +++ b/client/src/app/site/users/users.config.ts @@ -0,0 +1,22 @@ +import { AppConfig } from '../base/app-config'; +import { User } from '../../shared/models/users/user'; +import { Group } from '../../shared/models/users/group'; +import { PersonalNote } from '../../shared/models/users/personal-note'; + +export const UsersAppConfig: AppConfig = { + name: 'users', + models: [ + { collectionString: 'users/user', model: User }, + { collectionString: 'users/group', model: Group }, + { collectionString: 'users/personal-note', model: PersonalNote } + ], + mainMenuEntries: [ + { + route: '/users', + displayName: 'Participants', + icon: 'user', + weight: 500, + permission: 'users.can_see_name' + } + ] +}; diff --git a/client/src/plugins.ts b/client/src/plugins.ts new file mode 100644 index 000000000..c20b522e2 --- /dev/null +++ b/client/src/plugins.ts @@ -0,0 +1 @@ +export const plugins: string[] = [];