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.
This commit is contained in:
parent
b6ad0d759c
commit
be9f98cfd0
@ -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<void> {
|
||||
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 {}
|
||||
|
12
client/src/app/core/services/app-load.service.spec.ts
Normal file
12
client/src/app/core/services/app-load.service.spec.ts
Normal file
@ -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();
|
||||
}));
|
||||
});
|
61
client/src/app/core/services/app-load.service.ts
Normal file
61
client/src/app/core/services/app-load.service.ts
Normal file
@ -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<void> {
|
||||
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<BaseModel> }[]): void {}
|
||||
}
|
@ -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<any>('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}`);
|
||||
|
@ -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<BaseModel> } = {};
|
||||
|
||||
/**
|
||||
* Registers the type to the collection string
|
||||
* @param collectionString
|
||||
* @param type
|
||||
*/
|
||||
public static registerCollectionElement(collectionString: string, type: ModelConstructor<BaseModel>): 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<BaseModel> {
|
||||
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<BaseModel>): 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<BaseModel>): 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<BaseModel> {
|
||||
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<BaseModel>): string {
|
||||
return Object.keys(CollectionStringModelMapperService.collectionStringsTypeMapping).find(
|
||||
(collectionString: string) => {
|
||||
return ctor === CollectionStringModelMapperService.collectionStringsTypeMapping[collectionString];
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
15
client/src/app/core/services/main-menu.service.spec.ts
Normal file
15
client/src/app/core/services/main-menu.service.spec.ts
Normal file
@ -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();
|
||||
}));
|
||||
});
|
61
client/src/app/core/services/main-menu.service.ts
Normal file
61
client/src/app/core/services/main-menu.service.ts
Normal file
@ -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);
|
||||
}
|
||||
}
|
@ -56,5 +56,3 @@ export class Item extends ProjectableBaseModel {
|
||||
return this.getListTitle();
|
||||
}
|
||||
}
|
||||
|
||||
ProjectableBaseModel.registerCollectionElement('agenda/item', Item);
|
||||
|
@ -56,5 +56,3 @@ export class Assignment extends AgendaBaseModel {
|
||||
return 'TODO';
|
||||
}
|
||||
}
|
||||
|
||||
AgendaBaseModel.registerCollectionElement('assignments/assignment', Assignment);
|
||||
|
@ -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<T extends BaseModel<T>> {
|
||||
*/
|
||||
export abstract class BaseModel<T = object> 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.
|
||||
*
|
||||
|
@ -18,5 +18,3 @@ export class ChatMessage extends BaseModel<ChatMessage> {
|
||||
return 'Chatmessage';
|
||||
}
|
||||
}
|
||||
|
||||
BaseModel.registerCollectionElement('core/chat-message', ChatMessage);
|
||||
|
@ -17,5 +17,3 @@ export class Config extends BaseModel {
|
||||
return this.key;
|
||||
}
|
||||
}
|
||||
|
||||
BaseModel.registerCollectionElement('core/config', Config);
|
||||
|
@ -19,5 +19,3 @@ export class Countdown extends ProjectableBaseModel {
|
||||
return this.description;
|
||||
}
|
||||
}
|
||||
|
||||
ProjectableBaseModel.registerCollectionElement('core/countdown', Countdown);
|
||||
|
@ -16,5 +16,3 @@ export class ProjectorMessage extends ProjectableBaseModel {
|
||||
return 'Projectormessage';
|
||||
}
|
||||
}
|
||||
|
||||
ProjectableBaseModel.registerCollectionElement('core/projector-message', ProjectorMessage);
|
||||
|
@ -23,5 +23,3 @@ export class Projector extends BaseModel<Projector> {
|
||||
return this.name;
|
||||
}
|
||||
}
|
||||
|
||||
BaseModel.registerCollectionElement('core/projector', Projector);
|
||||
|
@ -16,5 +16,3 @@ export class Tag extends BaseModel<Tag> {
|
||||
return this.name;
|
||||
}
|
||||
}
|
||||
|
||||
BaseModel.registerCollectionElement('core/tag', Tag);
|
||||
|
@ -28,5 +28,3 @@ export class Mediafile extends ProjectableBaseModel {
|
||||
return this.title;
|
||||
}
|
||||
}
|
||||
|
||||
ProjectableBaseModel.registerCollectionElement('mediafiles/mediafile', Mediafile);
|
||||
|
@ -17,5 +17,3 @@ export class Category extends BaseModel<Category> {
|
||||
return this.prefix + ' - ' + this.name;
|
||||
}
|
||||
}
|
||||
|
||||
BaseModel.registerCollectionElement('motions/category', Category);
|
||||
|
@ -21,5 +21,3 @@ export class MotionBlock extends AgendaBaseModel {
|
||||
return 'TODO';
|
||||
}
|
||||
}
|
||||
|
||||
AgendaBaseModel.registerCollectionElement('motions/motion-block', MotionBlock);
|
||||
|
@ -23,5 +23,3 @@ export class MotionChangeReco extends BaseModel<MotionChangeReco> {
|
||||
return 'Changerecommendation';
|
||||
}
|
||||
}
|
||||
|
||||
BaseModel.registerCollectionElement('motions/motion-change-recommendation', MotionChangeReco);
|
||||
|
@ -18,5 +18,3 @@ export class MotionCommentSection extends BaseModel<MotionCommentSection> {
|
||||
return this.name;
|
||||
}
|
||||
}
|
||||
|
||||
BaseModel.registerCollectionElement('motions/motion-comment-section', MotionCommentSection);
|
||||
|
@ -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);
|
||||
|
@ -58,5 +58,3 @@ export class Workflow extends BaseModel<Workflow> {
|
||||
return this.name;
|
||||
}
|
||||
}
|
||||
|
||||
BaseModel.registerCollectionElement('motions/workflow', Workflow);
|
||||
|
@ -28,5 +28,3 @@ export class Topic extends AgendaBaseModel {
|
||||
return 'TODO';
|
||||
}
|
||||
}
|
||||
|
||||
AgendaBaseModel.registerCollectionElement('topics/topic', Topic);
|
||||
|
@ -17,5 +17,3 @@ export class Group extends BaseModel<Group> {
|
||||
return this.name;
|
||||
}
|
||||
}
|
||||
|
||||
BaseModel.registerCollectionElement('users/group', Group);
|
||||
|
@ -17,5 +17,3 @@ export class PersonalNote extends BaseModel<PersonalNote> {
|
||||
return 'Personal note';
|
||||
}
|
||||
}
|
||||
|
||||
BaseModel.registerCollectionElement('users/personal-note', PersonalNote);
|
||||
|
@ -89,5 +89,3 @@ export class User extends ProjectableBaseModel {
|
||||
return this.short_name;
|
||||
}
|
||||
}
|
||||
|
||||
ProjectableBaseModel.registerCollectionElement('users/user', User);
|
||||
|
17
client/src/app/site/agenda/agenda.config.ts
Normal file
17
client/src/app/site/agenda/agenda.config.ts
Normal file
@ -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'
|
||||
}
|
||||
]
|
||||
};
|
16
client/src/app/site/assignments/assignments.config.ts
Normal file
16
client/src/app/site/assignments/assignments.config.ts
Normal file
@ -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'
|
||||
}
|
||||
]
|
||||
};
|
25
client/src/app/site/base/app-config.ts
Normal file
25
client/src/app/site/base/app-config.ts
Normal file
@ -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<BaseModel>;
|
||||
}[];
|
||||
|
||||
/**
|
||||
* Main menu entries.
|
||||
*/
|
||||
mainMenuEntries?: MainMenuEntry[];
|
||||
}
|
26
client/src/app/site/common/common-routing.module.ts
Normal file
26
client/src/app/site/common/common-routing.module.ts
Normal file
@ -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 {}
|
26
client/src/app/site/common/common.config.ts
Normal file
26
client/src/app/site/common/common.config.ts
Normal file
@ -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'
|
||||
}
|
||||
]
|
||||
};
|
13
client/src/app/site/common/common.module.spec.ts
Normal file
13
client/src/app/site/common/common.module.spec.ts
Normal file
@ -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();
|
||||
});
|
||||
});
|
14
client/src/app/site/common/common.module.ts
Normal file
14
client/src/app/site/common/common.module.ts
Normal file
@ -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 {}
|
@ -7,8 +7,6 @@
|
||||
<h4> {{welcomeTitle | translate}} </h4>
|
||||
<span> {{welcomeText | translate}} </span>
|
||||
|
||||
<button mat-button (click)="DataStoreTest()">DataStoreTest</button>
|
||||
<br/>
|
||||
<button mat-button (click)="TranslateTest()">Translate in console</button>
|
||||
<br/>
|
||||
<button mat-button (click)="giveDataStore()">print the dataStore</button>
|
@ -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>(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 => user.id === 1));
|
||||
}
|
||||
|
||||
/**
|
||||
* function to print datastore
|
||||
*/
|
16
client/src/app/site/mediafiles/mediafile.config.ts
Normal file
16
client/src/app/site/mediafiles/mediafile.config.ts
Normal file
@ -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'
|
||||
}
|
||||
]
|
||||
};
|
28
client/src/app/site/motions/motions.config.ts
Normal file
28
client/src/app/site/motions/motions.config.ts
Normal file
@ -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'
|
||||
}
|
||||
]
|
||||
};
|
16
client/src/app/site/settings/settings.config.ts
Normal file
16
client/src/app/site/settings/settings.config.ts
Normal file
@ -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'
|
||||
}
|
||||
]
|
||||
};
|
@ -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',
|
||||
|
@ -45,35 +45,13 @@
|
||||
|
||||
<!-- navigation -->
|
||||
<mat-nav-list class='main-nav'>
|
||||
<a [@navItemAnim] *osPerms="'core.can_see_frontpage'" mat-list-item routerLink='/' routerLinkActive='active' [routerLinkActiveOptions]="{exact: true}"
|
||||
(click)='toggleSideNav()'>
|
||||
<fa-icon icon='home'></fa-icon>
|
||||
<span translate>Home</span>
|
||||
</a>
|
||||
<a [@navItemAnim] *osPerms="'agenda.can_see'" mat-list-item routerLink='/agenda' routerLinkActive='active' (click)='toggleSideNav()'>
|
||||
<fa-icon icon='calendar'></fa-icon>
|
||||
<span translate>Agenda</span>
|
||||
</a>
|
||||
<a [@navItemAnim] *osPerms="'motions.can_see'" mat-list-item routerLink='/motions' routerLinkActive='active' (click)='toggleSideNav()'>
|
||||
<fa-icon icon='file-alt'></fa-icon>
|
||||
<span translate>Motions</span>
|
||||
</a>
|
||||
<a [@navItemAnim] *osPerms="'assignments.can_see'" mat-list-item routerLink='/assignments' routerLinkActive='active' (click)='vp.isMobile ? sideNav.toggle() : null'>
|
||||
<fa-icon icon='chart-pie'></fa-icon>
|
||||
<span translate>Assignments</span>
|
||||
</a>
|
||||
<a [@navItemAnim] *osPerms="'users.can_see_name'" mat-list-item routerLink='/users' routerLinkActive='active' (click)='toggleSideNav()'>
|
||||
<fa-icon icon='user'></fa-icon>
|
||||
<span translate>Participants</span>
|
||||
</a>
|
||||
<a [@navItemAnim] *osPerms="'mediafiles.can_see'" mat-list-item routerLink='/mediafiles' routerLinkActive='active' (click)='toggleSideNav()'>
|
||||
<fa-icon icon='paperclip'></fa-icon>
|
||||
<span translate>Files</span>
|
||||
</a>
|
||||
<a [@navItemAnim] *osPerms="'core.can_manage_config'" mat-list-item routerLink='/settings' routerLinkActive='active' (click)='toggleSideNav()'>
|
||||
<fa-icon icon='cog'></fa-icon>
|
||||
<span translate>Settings</span>
|
||||
<span *ngFor="let entry of mainMenuService.entries">
|
||||
<a [@navItemAnim] *osPerms="entry.permission" mat-list-item (click)='toggleSideNav()'
|
||||
[routerLink]='entry.route' routerLinkActive='active' [routerLinkActiveOptions]="{exact: true}">
|
||||
<fa-icon [icon]='entry.icon'></fa-icon>
|
||||
{{ entry.displayName | translate}}
|
||||
</a>
|
||||
</span>
|
||||
<mat-divider></mat-divider>
|
||||
<a [@navItemAnim] *osPerms="'core.can_see_projector'" mat-list-item routerLink='/projector' routerLinkActive='active' (click)='toggleSideNav()'>
|
||||
<fa-icon icon='video'></fa-icon>
|
||||
|
@ -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.
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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 {}
|
||||
|
22
client/src/app/site/users/users.config.ts
Normal file
22
client/src/app/site/users/users.config.ts
Normal file
@ -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'
|
||||
}
|
||||
]
|
||||
};
|
1
client/src/plugins.ts
Normal file
1
client/src/plugins.ts
Normal file
@ -0,0 +1 @@
|
||||
export const plugins: string[] = [];
|
Loading…
Reference in New Issue
Block a user