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[] = [];