ViewModelStore, register repositories, improve view model relations
This commit is contained in:
parent
812168b00c
commit
d0991936d6
@ -1,20 +1,23 @@
|
||||
import { Type } from '@angular/core';
|
||||
|
||||
import { ModelConstructor, BaseModel } from '../shared/models/base/base-model';
|
||||
import { MainMenuEntry } from './core-services/main-menu.service';
|
||||
import { Searchable } from '../shared/models/base/searchable';
|
||||
import { Type } from '@angular/core';
|
||||
import { Searchable } from '../site/base/searchable';
|
||||
import { BaseRepository } from './repositories/base-repository';
|
||||
import { BaseViewModel, ViewModelConstructor } from 'app/site/base/base-view-model';
|
||||
|
||||
interface BaseModelEntry {
|
||||
collectionString: string;
|
||||
repository: Type<BaseRepository<any, any>>;
|
||||
}
|
||||
|
||||
export interface ModelEntry extends BaseModelEntry {
|
||||
model: ModelConstructor<BaseModel>;
|
||||
}
|
||||
|
||||
export interface ModelEntry extends BaseModelEntry {
|
||||
viewModel: ViewModelConstructor<BaseViewModel>;
|
||||
}
|
||||
|
||||
export interface SearchableModelEntry extends BaseModelEntry {
|
||||
model: new (...args: any[]) => BaseModel & Searchable;
|
||||
viewModel: new (...args: any[]) => BaseViewModel & Searchable;
|
||||
searchOrder: number;
|
||||
}
|
||||
|
||||
|
@ -14,9 +14,11 @@ import { TagAppConfig } from '../../site/tags/tag.config';
|
||||
import { MainMenuService } from './main-menu.service';
|
||||
import { HistoryAppConfig } from 'app/site/history/history.config';
|
||||
import { SearchService } from '../ui-services/search.service';
|
||||
import { isSearchable } from '../../shared/models/base/searchable';
|
||||
import { isSearchable } from '../../site/base/searchable';
|
||||
import { ProjectorAppConfig } from 'app/site/projector/projector.config';
|
||||
import { BaseRepository } from 'app/core/repositories/base-repository';
|
||||
import { OnAfterAppsLoaded } from '../onAfterAppsLoaded';
|
||||
import { ServicesToLoadOnAppsLoaded } from '../core.module';
|
||||
|
||||
/**
|
||||
* A list of all app configurations of all delivered apps.
|
||||
@ -63,16 +65,21 @@ export class AppLoadService {
|
||||
const plugin = await import('../../../../../plugins/' + pluginName + '/' + pluginName);
|
||||
plugin.main();
|
||||
}*/
|
||||
const repositories: OnAfterAppsLoaded[] = [];
|
||||
appConfigs.forEach((config: AppConfig) => {
|
||||
if (config.models) {
|
||||
config.models.forEach(entry => {
|
||||
let repository: BaseRepository<any, any> = null;
|
||||
if (entry.repository) {
|
||||
repository = this.injector.get(entry.repository);
|
||||
}
|
||||
this.modelMapper.registerCollectionElement(entry.collectionString, entry.model, repository);
|
||||
repository = this.injector.get(entry.repository);
|
||||
repositories.push(repository);
|
||||
this.modelMapper.registerCollectionElement(
|
||||
entry.collectionString,
|
||||
entry.model,
|
||||
entry.viewModel,
|
||||
repository
|
||||
);
|
||||
if (this.isSearchableModelEntry(entry)) {
|
||||
this.searchService.registerModel(entry.collectionString, entry.model, entry.searchOrder);
|
||||
this.searchService.registerModel(entry.collectionString, entry.viewModel, entry.searchOrder);
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -80,6 +87,16 @@ export class AppLoadService {
|
||||
this.mainMenuService.registerEntries(config.mainMenuEntries);
|
||||
}
|
||||
});
|
||||
|
||||
// Collect all services to notify for the OnAfterAppsLoadedHook
|
||||
const onAfterAppsLoadedItems = ServicesToLoadOnAppsLoaded.map(service => {
|
||||
return this.injector.get(service);
|
||||
}).concat(repositories);
|
||||
|
||||
// Notify them.
|
||||
onAfterAppsLoadedItems.forEach(repo => {
|
||||
repo.onAfterAppsLoaded();
|
||||
});
|
||||
}
|
||||
|
||||
private isSearchableModelEntry(entry: ModelEntry | SearchableModelEntry): entry is SearchableModelEntry {
|
||||
@ -87,7 +104,7 @@ export class AppLoadService {
|
||||
// We need to double check, because Typescipt cannot check contructors. If typescript could differentiate
|
||||
// between (ModelConstructor<BaseModel>) and (new (...args: any[]) => (BaseModel & Searchable)), we would not have
|
||||
// to check if the result of the contructor (the model instance) is really a searchable.
|
||||
if (!isSearchable(new entry.model())) {
|
||||
if (!isSearchable(new entry.viewModel())) {
|
||||
throw Error(
|
||||
`Wrong configuration for ${
|
||||
entry.collectionString
|
||||
|
@ -120,7 +120,6 @@ export class AutoupdateService extends OpenSlidesComponent {
|
||||
await this.DS.add(this.mapObjectsToBaseModels(collection, autoupdate.changed[collection]));
|
||||
}
|
||||
|
||||
console.log('new max change id', autoupdate.to_change_id);
|
||||
await this.DS.flushToStorage(autoupdate.to_change_id);
|
||||
} else {
|
||||
// autoupdate fully in the future. we are missing something!
|
||||
@ -135,7 +134,7 @@ export class AutoupdateService extends OpenSlidesComponent {
|
||||
* @returns A list of basemodels constructed from the given models.
|
||||
*/
|
||||
private mapObjectsToBaseModels(collection: string, models: object[]): BaseModel[] {
|
||||
const targetClass = this.modelMapper.getModelConstructor(collection);
|
||||
const targetClass = this.modelMapper.getModelConstructorFromCollectionString(collection);
|
||||
if (!targetClass) {
|
||||
throw new Error(`Unregistered resource ${collection}`);
|
||||
}
|
||||
|
@ -2,64 +2,134 @@ import { Injectable } from '@angular/core';
|
||||
|
||||
import { ModelConstructor, BaseModel } from '../../shared/models/base/base-model';
|
||||
import { BaseRepository } from 'app/core/repositories/base-repository';
|
||||
import { ViewModelConstructor, BaseViewModel } from 'app/site/base/base-view-model';
|
||||
|
||||
/**
|
||||
* Registeres the mapping of collection strings <--> actual types. Every Model should register itself here.
|
||||
* Holds a mapping entry with the matching collection string,
|
||||
* model constructor, view model constructor and the repository
|
||||
*/
|
||||
type MappingEntry = [
|
||||
string,
|
||||
ModelConstructor<BaseModel>,
|
||||
ViewModelConstructor<BaseViewModel>,
|
||||
BaseRepository<BaseViewModel, BaseModel>
|
||||
];
|
||||
|
||||
/**
|
||||
* Registeres the mapping between collection strings, models constructors, view
|
||||
* model constructors and repositories.
|
||||
* All models ned to be registered!
|
||||
*/
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class CollectionStringMapperService {
|
||||
/**
|
||||
* Mapps collection strings to model constructors. Accessed by {@method registerCollectionElement} and
|
||||
* {@method getCollectionStringType}.
|
||||
* Maps collection strings to mapping entries
|
||||
*/
|
||||
private collectionStringsTypeMapping: {
|
||||
[collectionString: string]: [ModelConstructor<BaseModel>, BaseRepository<any, any>];
|
||||
private collectionStringMapping: {
|
||||
[collectionString: string]: MappingEntry;
|
||||
} = {};
|
||||
|
||||
/**
|
||||
* Constructor to create the NotifyService. Registers itself to the WebsocketService.
|
||||
* @param websocketService
|
||||
* Maps models to mapping entries
|
||||
*/
|
||||
private modelMapping: {
|
||||
[modelName: string]: MappingEntry;
|
||||
} = {};
|
||||
|
||||
/**
|
||||
* Maps view models to mapping entries
|
||||
*/
|
||||
private viewModelMapping: {
|
||||
[viewModelname: string]: MappingEntry;
|
||||
} = {};
|
||||
|
||||
/**
|
||||
* Maps repositories to mapping entries
|
||||
*/
|
||||
private repositoryMapping: {
|
||||
[repositoryName: string]: MappingEntry;
|
||||
} = {};
|
||||
|
||||
public constructor() {}
|
||||
|
||||
/**
|
||||
* Registers the type to the collection string
|
||||
* Registers the combination of a collection string, model, view model and repository
|
||||
* @param collectionString
|
||||
* @param model
|
||||
*/
|
||||
public registerCollectionElement(
|
||||
public registerCollectionElement<V extends BaseViewModel, M extends BaseModel>(
|
||||
collectionString: string,
|
||||
model: ModelConstructor<BaseModel>,
|
||||
repository: BaseRepository<any, any>
|
||||
model: ModelConstructor<M>,
|
||||
viewModel: ViewModelConstructor<V>,
|
||||
repository: BaseRepository<V, M>
|
||||
): void {
|
||||
this.collectionStringsTypeMapping[collectionString] = [model, repository];
|
||||
const entry: MappingEntry = [collectionString, model, viewModel, repository];
|
||||
this.collectionStringMapping[collectionString] = entry;
|
||||
this.modelMapping[model.name] = entry;
|
||||
this.viewModelMapping[viewModel.name] = entry;
|
||||
this.repositoryMapping[repository.name] = entry;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 this.collectionStringsTypeMapping[collectionString][0];
|
||||
// The following accessors are for giving one of EntryType by given a different object
|
||||
// of EntryType.
|
||||
|
||||
public getCollectionStringFromModelConstructor<M extends BaseModel>(ctor: ModelConstructor<M>): string {
|
||||
return this.modelMapping[ctor.name][0];
|
||||
}
|
||||
public getCollectionStringFromViewModelConstructor<V extends BaseViewModel>(ctor: ViewModelConstructor<V>): string {
|
||||
return this.viewModelMapping[ctor.name][0];
|
||||
}
|
||||
public getCollectionStringFromRepository<M extends BaseModel, V extends BaseViewModel>(
|
||||
repository: BaseRepository<V, M>
|
||||
): string {
|
||||
return this.repositoryMapping[repository.name][0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the repository of the requested collection or undefined, if it is not registered.
|
||||
* @param collectionString the requested collection
|
||||
*/
|
||||
public getRepository(collectionString: string): BaseRepository<any, any> {
|
||||
return this.collectionStringsTypeMapping[collectionString][1];
|
||||
public getModelConstructorFromCollectionString<M extends BaseModel>(collectionString: string): ModelConstructor<M> {
|
||||
return this.collectionStringMapping[collectionString][1] as ModelConstructor<M>;
|
||||
}
|
||||
public getModelConstructorFromViewModelConstructor<V extends BaseViewModel, M extends BaseModel>(
|
||||
ctor: ViewModelConstructor<V>
|
||||
): ModelConstructor<M> {
|
||||
return this.viewModelMapping[ctor.name][1] as ModelConstructor<M>;
|
||||
}
|
||||
public getModelConstructorFromRepository<V extends BaseViewModel, M extends BaseModel>(
|
||||
repository: BaseRepository<V, M>
|
||||
): ModelConstructor<M> {
|
||||
return this.repositoryMapping[repository.name][1] as ModelConstructor<M>;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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(this.collectionStringsTypeMapping).find((collectionString: string) => {
|
||||
return ctor === this.collectionStringsTypeMapping[collectionString][0];
|
||||
});
|
||||
public getViewModelConstructorFromCollectionString<M extends BaseViewModel>(
|
||||
collectionString: string
|
||||
): ViewModelConstructor<M> {
|
||||
return this.collectionStringMapping[collectionString][2] as ViewModelConstructor<M>;
|
||||
}
|
||||
public getViewModelConstructorFromModelConstructor<V extends BaseViewModel, M extends BaseModel>(
|
||||
ctor: ModelConstructor<M>
|
||||
): ViewModelConstructor<V> {
|
||||
return this.modelMapping[ctor.name][2] as ViewModelConstructor<V>;
|
||||
}
|
||||
public getViewModelConstructorFromRepository<V extends BaseViewModel, M extends BaseModel>(
|
||||
repository: BaseRepository<V, M>
|
||||
): ViewModelConstructor<V> {
|
||||
return this.repositoryMapping[repository.name][2] as ViewModelConstructor<V>;
|
||||
}
|
||||
|
||||
public getRepositoryFromCollectionString<V extends BaseViewModel, M extends BaseModel>(
|
||||
collectionString: string
|
||||
): BaseRepository<V, M> {
|
||||
return this.collectionStringMapping[collectionString][3] as BaseRepository<V, M>;
|
||||
}
|
||||
public getRepositoryFromModelConstructor<V extends BaseViewModel, M extends BaseModel>(
|
||||
ctor: ModelConstructor<M>
|
||||
): BaseRepository<V, M> {
|
||||
return this.modelMapping[ctor.name][3] as BaseRepository<V, M>;
|
||||
}
|
||||
public getRepositoryFromViewModelConstructor<V extends BaseViewModel, M extends BaseModel>(
|
||||
ctor: ViewModelConstructor<V>
|
||||
): BaseRepository<V, M> {
|
||||
return this.viewModelMapping[ctor.name][3] as BaseRepository<V, M>;
|
||||
}
|
||||
}
|
||||
|
@ -72,6 +72,18 @@ export class DataStoreService {
|
||||
*/
|
||||
private readonly changedSubject: Subject<BaseModel> = new Subject<BaseModel>();
|
||||
|
||||
/**
|
||||
* This is subject notify all subscribers _before_ the `secondaryModelChangeSubject`.
|
||||
* It's the same subject as the changedSubject.
|
||||
*/
|
||||
public readonly primaryModelChangeSubject = new Subject<BaseModel>();
|
||||
|
||||
/**
|
||||
* This is subject notify all subscribers _after_ the `primaryModelChangeSubject`.
|
||||
* It's the same subject as the changedSubject.
|
||||
*/
|
||||
public readonly secondaryModelChangeSubject = new Subject<BaseModel>();
|
||||
|
||||
/**
|
||||
* Observe the datastore for changes.
|
||||
*
|
||||
@ -127,7 +139,12 @@ export class DataStoreService {
|
||||
* @param storageService use StorageService to preserve the DataStore.
|
||||
* @param modelMapper
|
||||
*/
|
||||
public constructor(private storageService: StorageService, private modelMapper: CollectionStringMapperService) {}
|
||||
public constructor(private storageService: StorageService, private modelMapper: CollectionStringMapperService) {
|
||||
this.changeObservable.subscribe(model => {
|
||||
this.primaryModelChangeSubject.next(model);
|
||||
this.secondaryModelChangeSubject.next(model);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the DataStore from cache and instantiate all models out of the serialized version.
|
||||
@ -170,7 +187,7 @@ export class DataStoreService {
|
||||
const storage: ModelStorage = {};
|
||||
Object.keys(serializedStore).forEach(collectionString => {
|
||||
storage[collectionString] = {} as ModelCollection;
|
||||
const target = this.modelMapper.getModelConstructor(collectionString);
|
||||
const target = this.modelMapper.getModelConstructorFromCollectionString(collectionString);
|
||||
if (target) {
|
||||
Object.keys(serializedStore[collectionString]).forEach(id => {
|
||||
const data = JSON.parse(serializedStore[collectionString][id]);
|
||||
@ -201,7 +218,7 @@ export class DataStoreService {
|
||||
if (typeof collectionType === 'string') {
|
||||
return collectionType;
|
||||
} else {
|
||||
return this.modelMapper.getCollectionString(collectionType);
|
||||
return this.modelMapper.getCollectionStringFromModelConstructor(collectionType);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -9,6 +9,10 @@ import { User } from '../../shared/models/users/user';
|
||||
import { environment } from 'environments/environment';
|
||||
import { DataStoreService } from './data-store.service';
|
||||
import { OfflineService } from './offline.service';
|
||||
import { ViewUser } from 'app/site/users/models/view-user';
|
||||
import { CollectionStringMapperService } from './collectionStringMapper.service';
|
||||
import { OnAfterAppsLoaded } from '../onAfterAppsLoaded';
|
||||
import { UserRepositoryService } from '../repositories/users/user-repository.service';
|
||||
|
||||
/**
|
||||
* Permissions on the client are just strings. This makes clear, that
|
||||
@ -36,12 +40,19 @@ export interface WhoAmIResponse {
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class OperatorService extends OpenSlidesComponent {
|
||||
export class OperatorService extends OpenSlidesComponent implements OnAfterAppsLoaded {
|
||||
/**
|
||||
* The operator.
|
||||
*/
|
||||
private _user: User;
|
||||
|
||||
/**
|
||||
* The operator as a view user. We need a separation here, because
|
||||
* we need to acces the operators permissions, before we get data
|
||||
* from the server to build the view user.
|
||||
*/
|
||||
private _viewUser: ViewUser;
|
||||
|
||||
/**
|
||||
* Get the user that corresponds to operator.
|
||||
*/
|
||||
@ -49,14 +60,20 @@ export class OperatorService extends OpenSlidesComponent {
|
||||
return this._user;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the user that corresponds to operator.
|
||||
*/
|
||||
public get viewUser(): ViewUser {
|
||||
return this._viewUser;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the current operator.
|
||||
*
|
||||
* The permissions are updated and the new user published.
|
||||
*/
|
||||
public set user(user: User) {
|
||||
this._user = user;
|
||||
this.updatePermissions();
|
||||
this.updateUser(user);
|
||||
}
|
||||
|
||||
public get isAnonymous(): boolean {
|
||||
@ -78,6 +95,16 @@ export class OperatorService extends OpenSlidesComponent {
|
||||
*/
|
||||
private operatorSubject: BehaviorSubject<User> = new BehaviorSubject<User>(null);
|
||||
|
||||
/**
|
||||
* Subject for the operator as a view user.
|
||||
*/
|
||||
private viewOperatorSubject: BehaviorSubject<ViewUser> = new BehaviorSubject<ViewUser>(null);
|
||||
|
||||
/**
|
||||
* The user repository. Will be filled by the `onAfterAppsLoaded`.
|
||||
*/
|
||||
private userRepository: UserRepositoryService;
|
||||
|
||||
/**
|
||||
* Sets up an observer for watching changes in the DS. If the operator user or groups are changed,
|
||||
* the operator's permissions are updated.
|
||||
@ -86,7 +113,12 @@ export class OperatorService extends OpenSlidesComponent {
|
||||
* @param DS
|
||||
* @param offlineService
|
||||
*/
|
||||
public constructor(private http: HttpClient, private DS: DataStoreService, private offlineService: OfflineService) {
|
||||
public constructor(
|
||||
private http: HttpClient,
|
||||
private DS: DataStoreService,
|
||||
private offlineService: OfflineService,
|
||||
private collectionStringMapperService: CollectionStringMapperService
|
||||
) {
|
||||
super();
|
||||
|
||||
this.DS.changeObservable.subscribe(newModel => {
|
||||
@ -96,8 +128,7 @@ export class OperatorService extends OpenSlidesComponent {
|
||||
}
|
||||
|
||||
if (newModel instanceof User && this._user.id === newModel.id) {
|
||||
this._user = newModel;
|
||||
this.updatePermissions();
|
||||
this.updateUser(newModel);
|
||||
}
|
||||
} else if (newModel instanceof Group && newModel.id === 1) {
|
||||
// Group 1 (default) for anonymous changed
|
||||
@ -106,6 +137,33 @@ export class OperatorService extends OpenSlidesComponent {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the repo to get a view user.
|
||||
*/
|
||||
public onAfterAppsLoaded(): void {
|
||||
this.userRepository = this.collectionStringMapperService.getRepositoryFromModelConstructor(
|
||||
User
|
||||
) as UserRepositoryService;
|
||||
if (this.user) {
|
||||
this._viewUser = this.userRepository.getViewModel(this.user.id);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the user and update the permissions.
|
||||
*
|
||||
* @param user The user to set.
|
||||
*/
|
||||
private updateUser(user: User | null): void {
|
||||
this._user = user;
|
||||
if (user && this.userRepository) {
|
||||
this._viewUser = this.userRepository.getViewModel(user.id);
|
||||
} else {
|
||||
this._viewUser = null;
|
||||
}
|
||||
this.updatePermissions();
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls `/apps/users/whoami` to find out the real operator.
|
||||
* @returns The response of the WhoAmI request.
|
||||
@ -131,10 +189,14 @@ export class OperatorService extends OpenSlidesComponent {
|
||||
* Services an components can use it to get informed when something changes in
|
||||
* the operator
|
||||
*/
|
||||
public getObservable(): Observable<User> {
|
||||
public getUserObservable(): Observable<User> {
|
||||
return this.operatorSubject.asObservable();
|
||||
}
|
||||
|
||||
public getViewUserObservable(): Observable<ViewUser> {
|
||||
return this.viewOperatorSubject.asObservable();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks, if the operator has at least one of the given permissions.
|
||||
* @param checkPerms The permissions to check, if at least one matches.
|
||||
@ -193,5 +255,6 @@ export class OperatorService extends OpenSlidesComponent {
|
||||
}
|
||||
// publish changes in the operator.
|
||||
this.operatorSubject.next(this.user);
|
||||
this.viewOperatorSubject.next(this.viewUser);
|
||||
}
|
||||
}
|
||||
|
@ -17,6 +17,8 @@ import {
|
||||
import { HttpService } from './http.service';
|
||||
import { SlideManager } from 'app/slides/services/slide-manager.service';
|
||||
import { BaseModel } from 'app/shared/models/base/base-model';
|
||||
import { BaseViewModel } from 'app/site/base/base-view-model';
|
||||
import { ViewModelStoreService } from './view-model-store.service';
|
||||
|
||||
/**
|
||||
* This service cares about Projectables being projected and manage all projection-related
|
||||
@ -34,7 +36,12 @@ export class ProjectorService extends OpenSlidesComponent {
|
||||
* @param DS
|
||||
* @param dataSend
|
||||
*/
|
||||
public constructor(private DS: DataStoreService, private http: HttpService, private slideManager: SlideManager) {
|
||||
public constructor(
|
||||
private DS: DataStoreService,
|
||||
private http: HttpService,
|
||||
private slideManager: SlideManager,
|
||||
private viewModelStore: ViewModelStoreService
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
@ -222,13 +229,12 @@ export class ProjectorService extends OpenSlidesComponent {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a model associated with the identifiable projector element. Throws an error,
|
||||
* if the element is not mappable.
|
||||
* Asserts, that the given element is mappable to a model or view model.
|
||||
* Throws an error, if this assertion fails.
|
||||
*
|
||||
* @param element The projector element
|
||||
* @returns the model from the projector element
|
||||
* @param element The element to check
|
||||
*/
|
||||
public getModelFromProjectorElement<T extends BaseModel>(element: IdentifiableProjectorElement): T {
|
||||
private assertElementIsMappable(element: IdentifiableProjectorElement): void {
|
||||
if (!this.slideManager.canSlideBeMappedToModel(element.name)) {
|
||||
throw new Error('This projector element cannot be mapped to a model');
|
||||
}
|
||||
@ -236,9 +242,32 @@ export class ProjectorService extends OpenSlidesComponent {
|
||||
if (!identifiers.includes('name') || !identifiers.includes('id')) {
|
||||
throw new Error('To map this element to a model, a name and id is needed.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a model associated with the identifiable projector element. Throws an error,
|
||||
* if the element is not mappable.
|
||||
*
|
||||
* @param element The projector element
|
||||
* @returns the model from the projector element
|
||||
*/
|
||||
public getModelFromProjectorElement<T extends BaseModel>(element: IdentifiableProjectorElement): T {
|
||||
this.assertElementIsMappable(element);
|
||||
return this.DS.get<T>(element.name, element.id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a view model associated with the identifiable projector element. Throws an error,
|
||||
* if the element is not mappable.
|
||||
*
|
||||
* @param element The projector element
|
||||
* @returns the view model from the projector element
|
||||
*/
|
||||
public getViewModelFromProjectorElement<T extends BaseViewModel>(element: IdentifiableProjectorElement): T {
|
||||
this.assertElementIsMappable(element);
|
||||
return this.viewModelStore.get<T>(element.name, element.id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Projects the next slide in the queue. Moves all currently projected
|
||||
* non-stable slides to the history.
|
||||
|
@ -68,7 +68,7 @@ export class TimeTravelService {
|
||||
[collectionString, id] = historyObject.element_id.split(':');
|
||||
|
||||
if (historyObject.full_data) {
|
||||
const targetClass = this.modelMapperService.getModelConstructor(collectionString);
|
||||
const targetClass = this.modelMapperService.getModelConstructorFromCollectionString(collectionString);
|
||||
await this.DS.add([new targetClass(historyObject.full_data)]);
|
||||
} else {
|
||||
await this.DS.remove(collectionString, [+id]);
|
||||
|
@ -0,0 +1,15 @@
|
||||
import { TestBed, inject } from '@angular/core/testing';
|
||||
import { ViewModelStoreService } from './view-model-store.service';
|
||||
import { E2EImportsModule } from '../../../e2e-imports.module';
|
||||
|
||||
describe('ViewModelStoreService', () => {
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [E2EImportsModule],
|
||||
providers: [ViewModelStoreService]
|
||||
});
|
||||
});
|
||||
it('should be created', inject([ViewModelStoreService], (service: ViewModelStoreService) => {
|
||||
expect(service).toBeTruthy();
|
||||
}));
|
||||
});
|
@ -0,0 +1,90 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { CollectionStringMapperService } from './collectionStringMapper.service';
|
||||
import { BaseViewModel, ViewModelConstructor } from 'app/site/base/base-view-model';
|
||||
import { BaseRepository } from '../repositories/base-repository';
|
||||
|
||||
/**
|
||||
* This service takes care of handling view models.
|
||||
*/
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class ViewModelStoreService {
|
||||
/**
|
||||
* @param mapperService
|
||||
*/
|
||||
public constructor(private mapperService: CollectionStringMapperService) {}
|
||||
|
||||
/**
|
||||
* gets the repository from a collection string or a view model constructor.
|
||||
*
|
||||
* @param collectionType The collection string or constructor.
|
||||
*/
|
||||
private getRepository<T extends BaseViewModel>(
|
||||
collectionType: ViewModelConstructor<T> | string
|
||||
): BaseRepository<T, any> {
|
||||
if (typeof collectionType === 'string') {
|
||||
return this.mapperService.getRepositoryFromCollectionString(collectionType) as BaseRepository<T, any>;
|
||||
} else {
|
||||
return this.mapperService.getRepositoryFromViewModelConstructor(collectionType as ViewModelConstructor<T>);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the view model identified by the collectionString and id
|
||||
*
|
||||
* @param collectionString The collection of the view model
|
||||
* @param id The id of the view model
|
||||
*/
|
||||
public get<T extends BaseViewModel>(collectionType: ViewModelConstructor<T> | string, id: number): T {
|
||||
return this.getRepository(collectionType).getViewModel(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all view models for the given ids.
|
||||
*
|
||||
* @param collectionType The collection of the view model
|
||||
* @param ids All ids to match
|
||||
*/
|
||||
public getMany<T extends BaseViewModel>(collectionType: ViewModelConstructor<T> | string, ids: number[]): T[] {
|
||||
const repository = this.getRepository<T>(collectionType);
|
||||
|
||||
return ids
|
||||
.map(id => {
|
||||
return repository.getViewModel(id);
|
||||
})
|
||||
.filter(model => !!model); // remove non valid models.
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all view models from a collection
|
||||
*
|
||||
* @param collectionString The collection
|
||||
* @returns all models from the collection
|
||||
*/
|
||||
public getAll<T extends BaseViewModel>(collectionType: ViewModelConstructor<T> | string): T[] {
|
||||
return this.getRepository(collectionType).getViewModelList();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all view modles from a collection, that satisfy the callback
|
||||
*
|
||||
* @param collectionString The collection
|
||||
* @param callback The function to check
|
||||
* @returns all matched view models of the collection
|
||||
*/
|
||||
public filter<T extends BaseViewModel>(collectionString: string, callback: (model: T) => boolean): T[] {
|
||||
return this.getAll<T>(collectionString).filter(callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds one view model from the collection, that satifies the callback
|
||||
*
|
||||
* @param collectionString The collection
|
||||
* @param callback THe callback to satisfy
|
||||
* @returns a found view model or null, if nothing was found.
|
||||
*/
|
||||
public find<T extends BaseViewModel>(collectionString: string, callback: (model: T) => boolean): T {
|
||||
return this.getAll<T>(collectionString).find(callback);
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
import { NgModule, Optional, SkipSelf } from '@angular/core';
|
||||
import { NgModule, Optional, SkipSelf, Type } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { Title } from '@angular/platform-browser';
|
||||
|
||||
@ -6,6 +6,10 @@ import { Title } from '@angular/platform-browser';
|
||||
import { PromptDialogComponent } from '../shared/components/prompt-dialog/prompt-dialog.component';
|
||||
import { ChoiceDialogComponent } from '../shared/components/choice-dialog/choice-dialog.component';
|
||||
import { ProjectionDialogComponent } from 'app/shared/components/projection-dialog/projection-dialog.component';
|
||||
import { OperatorService } from './core-services/operator.service';
|
||||
import { OnAfterAppsLoaded } from './onAfterAppsLoaded';
|
||||
|
||||
export const ServicesToLoadOnAppsLoaded: Type<OnAfterAppsLoaded>[] = [OperatorService];
|
||||
|
||||
/**
|
||||
* Global Core Module.
|
||||
|
9
client/src/app/core/onAfterAppsLoaded.ts
Normal file
9
client/src/app/core/onAfterAppsLoaded.ts
Normal file
@ -0,0 +1,9 @@
|
||||
/**
|
||||
* A lifecyclehook to be called, after all apps are loaded.
|
||||
*/
|
||||
export interface OnAfterAppsLoaded {
|
||||
/**
|
||||
* The hook to call
|
||||
*/
|
||||
onAfterAppsLoaded(): void;
|
||||
}
|
@ -1,17 +1,17 @@
|
||||
import { TestBed, inject } from '@angular/core/testing';
|
||||
|
||||
import { AgendaRepositoryService } from './agenda-repository.service';
|
||||
import { ItemRepositoryService } from './item-repository.service';
|
||||
import { E2EImportsModule } from 'e2e-imports.module';
|
||||
|
||||
describe('AgendaRepositoryService', () => {
|
||||
describe('ItemRepositoryService', () => {
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [E2EImportsModule],
|
||||
providers: [AgendaRepositoryService]
|
||||
providers: [ItemRepositoryService]
|
||||
});
|
||||
});
|
||||
|
||||
it('should be created', inject([AgendaRepositoryService], (service: AgendaRepositoryService) => {
|
||||
it('should be created', inject([ItemRepositoryService], (service: ItemRepositoryService) => {
|
||||
expect(service).toBeTruthy();
|
||||
}));
|
||||
});
|
@ -3,8 +3,6 @@ import { tap, map } from 'rxjs/operators';
|
||||
import { Observable } from 'rxjs';
|
||||
|
||||
import { BaseRepository } from '../base-repository';
|
||||
import { AgendaBaseModel } from 'app/shared/models/base/agenda-base-model';
|
||||
import { BaseModel } from 'app/shared/models/base/base-model';
|
||||
import { CollectionStringMapperService } from '../../core-services/collectionStringMapper.service';
|
||||
import { ConfigService } from 'app/core/ui-services/config.service';
|
||||
import { DataSendService } from 'app/core/core-services/data-send.service';
|
||||
@ -14,10 +12,13 @@ import { Identifiable } from 'app/shared/models/base/identifiable';
|
||||
import { Item } from 'app/shared/models/agenda/item';
|
||||
import { OSTreeSortEvent } from 'app/shared/components/sorting-tree/sorting-tree.component';
|
||||
import { Speaker } from 'app/shared/models/agenda/speaker';
|
||||
import { User } from 'app/shared/models/users/user';
|
||||
import { ViewItem } from 'app/site/agenda/models/view-item';
|
||||
import { ViewSpeaker } from 'app/site/agenda/models/view-speaker';
|
||||
import { TreeService } from 'app/core/ui-services/tree.service';
|
||||
import { BaseAgendaViewModel } from 'app/site/base/base-agenda-view-model';
|
||||
import { ViewModelStoreService } from 'app/core/core-services/view-model-store.service';
|
||||
import { BaseViewModel } from 'app/site/base/base-view-model';
|
||||
import { ViewUser } from 'app/site/users/models/view-user';
|
||||
|
||||
/**
|
||||
* Repository service for users
|
||||
@ -27,7 +28,7 @@ import { TreeService } from 'app/core/ui-services/tree.service';
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class AgendaRepositoryService extends BaseRepository<ViewItem, Item> {
|
||||
export class ItemRepositoryService extends BaseRepository<ViewItem, Item> {
|
||||
/**
|
||||
* Contructor for agenda repository.
|
||||
*
|
||||
@ -39,38 +40,39 @@ export class AgendaRepositoryService extends BaseRepository<ViewItem, Item> {
|
||||
* @param treeService sort the data according to weight and parents
|
||||
*/
|
||||
public constructor(
|
||||
protected DS: DataStoreService,
|
||||
private httpService: HttpService,
|
||||
DS: DataStoreService,
|
||||
mapperService: CollectionStringMapperService,
|
||||
viewModelStoreService: ViewModelStoreService,
|
||||
private httpService: HttpService,
|
||||
private config: ConfigService,
|
||||
private dataSend: DataSendService,
|
||||
private treeService: TreeService
|
||||
) {
|
||||
super(DS, mapperService, Item);
|
||||
super(DS, mapperService, viewModelStoreService, Item);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the corresponding content object to a given {@link Item} as an {@link AgendaBaseModel}
|
||||
* Returns the corresponding content object to a given {@link Item} as an {@link AgendaBaseViewModel}
|
||||
* Used dynamically because of heavy race conditions
|
||||
*
|
||||
* @param agendaItem the target agenda Item
|
||||
* @returns the content object of the given item. Might be null if it was not found.
|
||||
*/
|
||||
public getContentObject(agendaItem: Item): AgendaBaseModel {
|
||||
const contentObject = this.DS.get<BaseModel>(
|
||||
public getContentObject(agendaItem: Item): BaseAgendaViewModel {
|
||||
const contentObject = this.viewModelStoreService.get<BaseViewModel>(
|
||||
agendaItem.content_object.collection,
|
||||
agendaItem.content_object.id
|
||||
);
|
||||
if (!contentObject) {
|
||||
return null;
|
||||
}
|
||||
if (contentObject instanceof AgendaBaseModel) {
|
||||
return contentObject as AgendaBaseModel;
|
||||
if (contentObject instanceof BaseAgendaViewModel) {
|
||||
return contentObject as BaseAgendaViewModel;
|
||||
} else {
|
||||
throw new Error(
|
||||
`The content object (${agendaItem.content_object.collection}, ${
|
||||
agendaItem.content_object.id
|
||||
}) of item ${agendaItem.id} is not a AgendaBaseModel.`
|
||||
}) of item ${agendaItem.id} is not a AgendaBaseViewModel.`
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -86,7 +88,7 @@ export class AgendaRepositoryService extends BaseRepository<ViewItem, Item> {
|
||||
const speakers = item.speakers;
|
||||
if (speakers && speakers.length > 0) {
|
||||
speakers.forEach((speaker: Speaker) => {
|
||||
const user = this.DS.get(User, speaker.user_id);
|
||||
const user = this.viewModelStoreService.get(ViewUser, speaker.user_id);
|
||||
viewSpeakers.push(new ViewSpeaker(speaker, user));
|
||||
});
|
||||
}
|
@ -10,6 +10,9 @@ import { ViewTopic } from 'app/site/agenda/models/view-topic';
|
||||
import { Identifiable } from 'app/shared/models/base/identifiable';
|
||||
import { CollectionStringMapperService } from 'app/core/core-services/collectionStringMapper.service';
|
||||
import { CreateTopic } from 'app/site/agenda/models/create-topic';
|
||||
import { ViewModelStoreService } from 'app/core/core-services/view-model-store.service';
|
||||
import { ViewMediafile } from 'app/site/mediafiles/models/view-mediafile';
|
||||
import { ViewItem } from 'app/site/agenda/models/view-item';
|
||||
|
||||
/**
|
||||
* Repository for topics
|
||||
@ -28,9 +31,10 @@ export class TopicRepositoryService extends BaseRepository<ViewTopic, Topic> {
|
||||
public constructor(
|
||||
DS: DataStoreService,
|
||||
mapperService: CollectionStringMapperService,
|
||||
viewModelStoreService: ViewModelStoreService,
|
||||
private dataSend: DataSendService
|
||||
) {
|
||||
super(DS, mapperService, Topic, [Mediafile, Item]);
|
||||
super(DS, mapperService, viewModelStoreService, Topic, [Mediafile, Item]);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -40,22 +44,11 @@ export class TopicRepositoryService extends BaseRepository<ViewTopic, Topic> {
|
||||
* @returns a new view topic
|
||||
*/
|
||||
public createViewModel(topic: Topic): ViewTopic {
|
||||
const attachments = this.DS.getMany(Mediafile, topic.attachments_id);
|
||||
const item = this.getAgendaItem(topic);
|
||||
const attachments = this.viewModelStoreService.getMany(ViewMediafile, topic.attachments_id);
|
||||
const item = this.viewModelStoreService.get(ViewItem, topic.agenda_item_id);
|
||||
return new ViewTopic(topic, attachments, item);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the corresponding agendaItem to the topic.
|
||||
* Used to deal with race conditions
|
||||
*
|
||||
* @param topic the topic for the agenda item
|
||||
* @returns an agenda item that fits for the topic
|
||||
*/
|
||||
public getAgendaItem(topic: Topic): Item {
|
||||
return this.DS.get(Item, topic.agenda_item_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Save a new topic
|
||||
*
|
||||
|
@ -8,6 +8,10 @@ import { BaseRepository } from '../base-repository';
|
||||
import { DataStoreService } from '../../core-services/data-store.service';
|
||||
import { Identifiable } from 'app/shared/models/base/identifiable';
|
||||
import { CollectionStringMapperService } from '../../core-services/collectionStringMapper.service';
|
||||
import { ViewModelStoreService } from 'app/core/core-services/view-model-store.service';
|
||||
import { ViewItem } from 'app/site/agenda/models/view-item';
|
||||
import { ViewUser } from 'app/site/users/models/view-user';
|
||||
import { ViewTag } from 'app/site/tags/models/view-tag';
|
||||
|
||||
/**
|
||||
* Repository Service for Assignments.
|
||||
@ -24,8 +28,12 @@ export class AssignmentRepositoryService extends BaseRepository<ViewAssignment,
|
||||
* @param DS The DataStore
|
||||
* @param mapperService Maps collection strings to classes
|
||||
*/
|
||||
public constructor(DS: DataStoreService, mapperService: CollectionStringMapperService) {
|
||||
super(DS, mapperService, Assignment, [User, Item, Tag]);
|
||||
public constructor(
|
||||
DS: DataStoreService,
|
||||
mapperService: CollectionStringMapperService,
|
||||
viewModelStoreService: ViewModelStoreService
|
||||
) {
|
||||
super(DS, mapperService, viewModelStoreService, Assignment, [User, Item, Tag]);
|
||||
}
|
||||
|
||||
public async update(assignment: Partial<Assignment>, viewAssignment: ViewAssignment): Promise<void> {
|
||||
@ -41,9 +49,9 @@ export class AssignmentRepositoryService extends BaseRepository<ViewAssignment,
|
||||
}
|
||||
|
||||
public createViewModel(assignment: Assignment): ViewAssignment {
|
||||
const relatedUser = this.DS.getMany(User, assignment.candidateIds);
|
||||
const agendaItem = this.DS.get(Item, assignment.agenda_item_id);
|
||||
const tags = this.DS.getMany(Tag, assignment.tags_id);
|
||||
const relatedUser = this.viewModelStoreService.getMany(ViewUser, assignment.candidateIds);
|
||||
const agendaItem = this.viewModelStoreService.get(ViewItem, assignment.agenda_item_id);
|
||||
const tags = this.viewModelStoreService.getMany(ViewTag, assignment.tags_id);
|
||||
|
||||
return new ViewAssignment(assignment, relatedUser, agendaItem, tags);
|
||||
}
|
||||
|
@ -7,8 +7,11 @@ import { CollectionStringMapperService } from '../core-services/collectionString
|
||||
import { DataStoreService } from '../core-services/data-store.service';
|
||||
import { Identifiable } from '../../shared/models/base/identifiable';
|
||||
import { auditTime } from 'rxjs/operators';
|
||||
import { ViewModelStoreService } from '../core-services/view-model-store.service';
|
||||
import { OnAfterAppsLoaded } from '../onAfterAppsLoaded';
|
||||
|
||||
export abstract class BaseRepository<V extends BaseViewModel, M extends BaseModel> extends OpenSlidesComponent {
|
||||
export abstract class BaseRepository<V extends BaseViewModel, M extends BaseModel> extends OpenSlidesComponent
|
||||
implements OnAfterAppsLoaded {
|
||||
/**
|
||||
* Stores all the viewModel in an object
|
||||
*/
|
||||
@ -29,26 +32,33 @@ export abstract class BaseRepository<V extends BaseViewModel, M extends BaseMode
|
||||
*/
|
||||
protected readonly generalViewModelSubject: Subject<V> = new Subject<V>();
|
||||
|
||||
private _name: string;
|
||||
|
||||
public get name(): string {
|
||||
return this._name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Construction routine for the base repository
|
||||
*
|
||||
* @param DS: The DataStore
|
||||
* @param collectionStringModelMapperService Mapping strings to their corresponding classes
|
||||
* @param collectionStringMapperService Mapping strings to their corresponding classes
|
||||
* @param baseModelCtor The model constructor of which this repository is about.
|
||||
* @param depsModelCtors A list of constructors that are used in the view model.
|
||||
* If one of those changes, the view models will be updated.
|
||||
*/
|
||||
public constructor(
|
||||
protected DS: DataStoreService,
|
||||
protected collectionStringModelMapperService: CollectionStringMapperService,
|
||||
protected collectionStringMapperService: CollectionStringMapperService,
|
||||
protected viewModelStoreService: ViewModelStoreService,
|
||||
protected baseModelCtor: ModelConstructor<M>,
|
||||
protected depsModelCtors?: ModelConstructor<BaseModel>[]
|
||||
) {
|
||||
super();
|
||||
this.setup();
|
||||
this._name = baseModelCtor.name;
|
||||
}
|
||||
|
||||
protected setup(): void {
|
||||
public onAfterAppsLoaded(): void {
|
||||
// Populate the local viewModelStore with ViewModel Objects.
|
||||
this.DS.getAll(this.baseModelCtor).forEach((model: M) => {
|
||||
this.viewModelStore[model.id] = this.createViewModel(model);
|
||||
@ -60,28 +70,40 @@ export abstract class BaseRepository<V extends BaseViewModel, M extends BaseMode
|
||||
});
|
||||
|
||||
// Could be raise in error if the root injector is not known
|
||||
this.DS.changeObservable.subscribe(model => {
|
||||
this.DS.primaryModelChangeSubject.subscribe(model => {
|
||||
if (model instanceof this.baseModelCtor) {
|
||||
// Add new and updated motions to the viewModelStore
|
||||
this.viewModelStore[model.id] = this.createViewModel(model as M);
|
||||
this.updateAllObservables(model.id);
|
||||
} else if (this.depsModelCtors) {
|
||||
}
|
||||
});
|
||||
|
||||
if (this.depsModelCtors) {
|
||||
this.DS.secondaryModelChangeSubject.subscribe(model => {
|
||||
const dependencyChanged: boolean = this.depsModelCtors.some(ctor => {
|
||||
return model instanceof ctor;
|
||||
});
|
||||
if (dependencyChanged) {
|
||||
const viewModel = this.viewModelStoreService.get(model.collectionString, model.id);
|
||||
|
||||
// if an domain object we need was added or changed, update viewModelStore
|
||||
this.getViewModelList().forEach(viewModel => {
|
||||
viewModel.updateValues(model);
|
||||
this.getViewModelList().forEach(ownViewModel => {
|
||||
ownViewModel.updateDependencies(viewModel);
|
||||
});
|
||||
this.updateAllObservables(model.id);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Watch the Observables for deleting
|
||||
// TODO: What happens, if some related object was deleted?
|
||||
// My quess: This must trigger an autoupdate also for this model, because some IDs changed, so the
|
||||
// affected models will be newly created by the primaryModelChangeSubject.
|
||||
this.DS.deletedObservable.subscribe(model => {
|
||||
if (model.collection === this.collectionStringModelMapperService.getCollectionString(this.baseModelCtor)) {
|
||||
if (
|
||||
model.collection ===
|
||||
this.collectionStringMapperService.getCollectionStringFromModelConstructor(this.baseModelCtor)
|
||||
) {
|
||||
delete this.viewModelStore[model.id];
|
||||
this.updateAllObservables(model.id);
|
||||
}
|
||||
@ -167,8 +189,8 @@ export abstract class BaseRepository<V extends BaseViewModel, M extends BaseMode
|
||||
protected updateViewModelObservable(id: number): void {
|
||||
if (this.viewModelSubjects[id]) {
|
||||
this.viewModelSubjects[id].next(this.viewModelStore[id]);
|
||||
this.generalViewModelSubject.next(this.viewModelStore[id]);
|
||||
}
|
||||
this.generalViewModelSubject.next(this.viewModelStore[id]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -5,13 +5,18 @@ import { Identifiable } from 'app/shared/models/base/identifiable';
|
||||
import { CollectionStringMapperService } from '../../core-services/collectionStringMapper.service';
|
||||
import { ChatMessage } from 'app/shared/models/core/chat-message';
|
||||
import { ViewChatMessage } from 'app/site/common/models/view-chatmessage';
|
||||
import { ViewModelStoreService } from 'app/core/core-services/view-model-store.service';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class ChatMessageRepositoryService extends BaseRepository<ViewChatMessage, ChatMessage> {
|
||||
public constructor(DS: DataStoreService, mapperService: CollectionStringMapperService) {
|
||||
super(DS, mapperService, ChatMessage);
|
||||
public constructor(
|
||||
DS: DataStoreService,
|
||||
mapperService: CollectionStringMapperService,
|
||||
viewModelStoreService: ViewModelStoreService
|
||||
) {
|
||||
super(DS, mapperService, viewModelStoreService, ChatMessage);
|
||||
}
|
||||
|
||||
protected createViewModel(message: ChatMessage): ViewChatMessage {
|
||||
|
@ -1,14 +1,16 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
|
||||
import { BaseRepository } from 'app/core/repositories/base-repository';
|
||||
import { ViewConfig } from '../models/view-config';
|
||||
import { Config } from 'app/shared/models/core/config';
|
||||
import { Observable, BehaviorSubject } from 'rxjs';
|
||||
|
||||
import { BaseRepository } from 'app/core/repositories/base-repository';
|
||||
import { Config } from 'app/shared/models/core/config';
|
||||
import { DataStoreService } from 'app/core/core-services/data-store.service';
|
||||
import { ConstantsService } from 'app/core/ui-services/constants.service';
|
||||
import { HttpService } from 'app/core/core-services/http.service';
|
||||
import { Identifiable } from 'app/shared/models/base/identifiable';
|
||||
import { CollectionStringMapperService } from 'app/core/core-services/collectionStringMapper.service';
|
||||
import { ViewModelStoreService } from 'app/core/core-services/view-model-store.service';
|
||||
import { ViewConfig } from 'app/site/config/models/view-config';
|
||||
|
||||
/**
|
||||
* Holds a single config item.
|
||||
@ -95,10 +97,11 @@ export class ConfigRepositoryService extends BaseRepository<ViewConfig, Config>
|
||||
public constructor(
|
||||
DS: DataStoreService,
|
||||
mapperService: CollectionStringMapperService,
|
||||
viewModelStoreService: ViewModelStoreService,
|
||||
private constantsService: ConstantsService,
|
||||
private http: HttpService
|
||||
) {
|
||||
super(DS, mapperService, Config);
|
||||
super(DS, mapperService, viewModelStoreService, Config);
|
||||
|
||||
this.constantsService.get('OpenSlidesConfigVariables').subscribe(constant => {
|
||||
this.createConfigStructure(constant);
|
||||
@ -111,7 +114,7 @@ export class ConfigRepositoryService extends BaseRepository<ViewConfig, Config>
|
||||
* Overwritten setup. Does only care about the custom list observable and inserts changed configs into the
|
||||
* config group structure.
|
||||
*/
|
||||
protected setup(): void {
|
||||
public onAfterAppsLoaded(): void {
|
||||
if (!this.configListSubject) {
|
||||
this.configListSubject = new BehaviorSubject<ConfigGroup[]>(null);
|
||||
}
|
@ -9,7 +9,8 @@ import { Identifiable } from 'app/shared/models/base/identifiable';
|
||||
import { HttpService } from 'app/core/core-services/http.service';
|
||||
import { ViewHistory } from 'app/site/history/models/view-history';
|
||||
import { TimeTravelService } from 'app/core/core-services/time-travel.service';
|
||||
import { BaseModel } from 'app/shared/models/base/base-model';
|
||||
import { ViewModelStoreService } from 'app/core/core-services/view-model-store.service';
|
||||
import { ViewUser } from 'app/site/users/models/view-user';
|
||||
|
||||
/**
|
||||
* Repository for the history.
|
||||
@ -31,10 +32,11 @@ export class HistoryRepositoryService extends BaseRepository<ViewHistory, Histor
|
||||
public constructor(
|
||||
DS: DataStoreService,
|
||||
mapperService: CollectionStringMapperService,
|
||||
viewModelStoreService: ViewModelStoreService,
|
||||
private httpService: HttpService,
|
||||
private timeTravel: TimeTravelService
|
||||
) {
|
||||
super(DS, mapperService, History, [User]);
|
||||
super(DS, mapperService, viewModelStoreService, History, [User]);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -70,12 +72,11 @@ export class HistoryRepositoryService extends BaseRepository<ViewHistory, Histor
|
||||
* @returns the ListTitle or null if the model was deleted already
|
||||
*/
|
||||
public getOldModelInfo(collectionString: string, id: number): string {
|
||||
const oldModel: BaseModel = this.DS.get(collectionString, id);
|
||||
if (oldModel) {
|
||||
return oldModel.getListTitle();
|
||||
} else {
|
||||
return null;
|
||||
const model = this.viewModelStoreService.get(collectionString, id);
|
||||
if (model) {
|
||||
return model.getListTitle();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -85,7 +86,7 @@ export class HistoryRepositoryService extends BaseRepository<ViewHistory, Histor
|
||||
* @return a new ViewHistory object
|
||||
*/
|
||||
public createViewModel(history: History): ViewHistory {
|
||||
const user = this.DS.get(User, history.user_id);
|
||||
const user = this.viewModelStoreService.get(ViewUser, history.user_id);
|
||||
return new ViewHistory(history, user);
|
||||
}
|
||||
|
||||
|
@ -10,6 +10,8 @@ import { CollectionStringMapperService } from '../../core-services/collectionStr
|
||||
import { DataSendService } from 'app/core/core-services/data-send.service';
|
||||
import { HttpService } from 'app/core/core-services/http.service';
|
||||
import { HttpHeaders } from '@angular/common/http';
|
||||
import { ViewModelStoreService } from 'app/core/core-services/view-model-store.service';
|
||||
import { ViewUser } from 'app/site/users/models/view-user';
|
||||
|
||||
/**
|
||||
* Repository for MediaFiles
|
||||
@ -28,10 +30,11 @@ export class MediafileRepositoryService extends BaseRepository<ViewMediafile, Me
|
||||
public constructor(
|
||||
DS: DataStoreService,
|
||||
mapperService: CollectionStringMapperService,
|
||||
viewModelStoreService: ViewModelStoreService,
|
||||
private dataSend: DataSendService,
|
||||
private httpService: HttpService
|
||||
) {
|
||||
super(DS, mapperService, Mediafile, [User]);
|
||||
super(DS, mapperService, viewModelStoreService, Mediafile, [User]);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -88,7 +91,7 @@ export class MediafileRepositoryService extends BaseRepository<ViewMediafile, Me
|
||||
* @returns a new mediafile ViewModel
|
||||
*/
|
||||
public createViewModel(file: Mediafile): ViewMediafile {
|
||||
const uploader = this.DS.get(User, file.uploader_id);
|
||||
const uploader = this.viewModelStoreService.get(ViewUser, file.uploader_id);
|
||||
return new ViewMediafile(file, uploader);
|
||||
}
|
||||
}
|
||||
|
@ -9,6 +9,7 @@ import { Motion } from 'app/shared/models/motions/motion';
|
||||
import { HttpService } from '../../core-services/http.service';
|
||||
import { Identifiable } from 'app/shared/models/base/identifiable';
|
||||
import { CollectionStringMapperService } from '../../core-services/collectionStringMapper.service';
|
||||
import { ViewModelStoreService } from 'app/core/core-services/view-model-store.service';
|
||||
|
||||
/**
|
||||
* Repository Services for Categories
|
||||
@ -37,10 +38,11 @@ export class CategoryRepositoryService extends BaseRepository<ViewCategory, Cate
|
||||
public constructor(
|
||||
protected DS: DataStoreService,
|
||||
mapperService: CollectionStringMapperService,
|
||||
viewModelStoreService: ViewModelStoreService,
|
||||
private dataSend: DataSendService,
|
||||
private httpService: HttpService
|
||||
) {
|
||||
super(DS, mapperService, Category);
|
||||
super(DS, mapperService, viewModelStoreService, Category);
|
||||
}
|
||||
|
||||
protected createViewModel(category: Category): ViewCategory {
|
||||
|
@ -3,16 +3,17 @@ import { Injectable } from '@angular/core';
|
||||
import { Observable } from 'rxjs';
|
||||
import { map } from 'rxjs/operators';
|
||||
|
||||
import { DataSendService } from '../../core-services/data-send.service';
|
||||
import { DataSendService } from 'app/core/core-services/data-send.service';
|
||||
import { User } from 'app/shared/models/users/user';
|
||||
import { Category } from 'app/shared/models/motions/category';
|
||||
import { Workflow } from 'app/shared/models/motions/workflow';
|
||||
import { BaseRepository } from '../base-repository';
|
||||
import { DataStoreService } from '../../core-services/data-store.service';
|
||||
import { MotionChangeReco } from 'app/shared/models/motions/motion-change-reco';
|
||||
import { ViewChangeReco } from 'app/site/motions/models/view-change-reco';
|
||||
import { DataStoreService } from 'app/core/core-services/data-store.service';
|
||||
import { MotionChangeRecommendation } from 'app/shared/models/motions/motion-change-reco';
|
||||
import { ViewMotionChangeRecommendation } from 'app/site/motions/models/view-change-recommendation';
|
||||
import { Identifiable } from 'app/shared/models/base/identifiable';
|
||||
import { CollectionStringMapperService } from '../../core-services/collectionStringMapper.service';
|
||||
import { CollectionStringMapperService } from 'app/core/core-services/collectionStringMapper.service';
|
||||
import { ViewModelStoreService } from 'app/core/core-services/view-model-store.service';
|
||||
|
||||
/**
|
||||
* Repository Services for change recommendations
|
||||
@ -27,7 +28,10 @@ import { CollectionStringMapperService } from '../../core-services/collectionStr
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class ChangeRecommendationRepositoryService extends BaseRepository<ViewChangeReco, MotionChangeReco> {
|
||||
export class ChangeRecommendationRepositoryService extends BaseRepository<
|
||||
ViewMotionChangeRecommendation,
|
||||
MotionChangeRecommendation
|
||||
> {
|
||||
/**
|
||||
* Creates a MotionRepository
|
||||
*
|
||||
@ -41,18 +45,19 @@ export class ChangeRecommendationRepositoryService extends BaseRepository<ViewCh
|
||||
public constructor(
|
||||
DS: DataStoreService,
|
||||
mapperService: CollectionStringMapperService,
|
||||
viewModelStoreService: ViewModelStoreService,
|
||||
private dataSend: DataSendService
|
||||
) {
|
||||
super(DS, mapperService, MotionChangeReco, [Category, User, Workflow]);
|
||||
super(DS, mapperService, viewModelStoreService, MotionChangeRecommendation, [Category, User, Workflow]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a change recommendation
|
||||
* Creates a (real) change recommendation and delegates it to the {@link DataSendService}
|
||||
*
|
||||
* @param {MotionChangeReco} changeReco
|
||||
* @param {MotionChangeRecommendation} changeReco
|
||||
*/
|
||||
public async create(changeReco: MotionChangeReco): Promise<Identifiable> {
|
||||
public async create(changeReco: MotionChangeRecommendation): Promise<Identifiable> {
|
||||
return await this.dataSend.createModel(changeReco);
|
||||
}
|
||||
|
||||
@ -61,17 +66,17 @@ export class ChangeRecommendationRepositoryService extends BaseRepository<ViewCh
|
||||
* @param view
|
||||
* @returns The id of the created change recommendation
|
||||
*/
|
||||
public async createByViewModel(view: ViewChangeReco): Promise<Identifiable> {
|
||||
public async createByViewModel(view: ViewMotionChangeRecommendation): Promise<Identifiable> {
|
||||
return await this.dataSend.createModel(view.changeRecommendation);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates this view wrapper based on an actual Change Recommendation model
|
||||
*
|
||||
* @param {MotionChangeReco} model
|
||||
* @param {MotionChangeRecommendation} model
|
||||
*/
|
||||
protected createViewModel(model: MotionChangeReco): ViewChangeReco {
|
||||
return new ViewChangeReco(model);
|
||||
protected createViewModel(model: MotionChangeRecommendation): ViewMotionChangeRecommendation {
|
||||
return new ViewMotionChangeRecommendation(model);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -79,9 +84,9 @@ export class ChangeRecommendationRepositoryService extends BaseRepository<ViewCh
|
||||
*
|
||||
* Extract the change recommendation out of the viewModel and delegate
|
||||
* to {@link DataSendService}
|
||||
* @param {ViewChangeReco} viewModel
|
||||
* @param {ViewMotionChangeRecommendation} viewModel
|
||||
*/
|
||||
public async delete(viewModel: ViewChangeReco): Promise<void> {
|
||||
public async delete(viewModel: ViewMotionChangeRecommendation): Promise<void> {
|
||||
await this.dataSend.deleteModel(viewModel.changeRecommendation);
|
||||
}
|
||||
|
||||
@ -91,10 +96,13 @@ export class ChangeRecommendationRepositoryService extends BaseRepository<ViewCh
|
||||
* Updates a (real) change recommendation with patched data and delegate it
|
||||
* to the {@link DataSendService}
|
||||
*
|
||||
* @param {Partial<MotionChangeReco>} update the form data containing the update values
|
||||
* @param {ViewChangeReco} viewModel The View Change Recommendation. If not present, a new motion will be created
|
||||
* @param {Partial<MotionChangeRecommendation>} update the form data containing the update values
|
||||
* @param {ViewMotionChangeRecommendation} viewModel The View Change Recommendation. If not present, a new motion will be created
|
||||
*/
|
||||
public async update(update: Partial<MotionChangeReco>, viewModel: ViewChangeReco): Promise<void> {
|
||||
public async update(
|
||||
update: Partial<MotionChangeRecommendation>,
|
||||
viewModel: ViewMotionChangeRecommendation
|
||||
): Promise<void> {
|
||||
const changeReco = viewModel.changeRecommendation;
|
||||
changeReco.patchValues(update);
|
||||
await this.dataSend.partialUpdateModel(changeReco);
|
||||
@ -103,9 +111,9 @@ export class ChangeRecommendationRepositoryService extends BaseRepository<ViewCh
|
||||
/**
|
||||
* return the Observable of all change recommendations belonging to the given motion
|
||||
*/
|
||||
public getChangeRecosOfMotionObservable(motion_id: number): Observable<ViewChangeReco[]> {
|
||||
public getChangeRecosOfMotionObservable(motion_id: number): Observable<ViewMotionChangeRecommendation[]> {
|
||||
return this.viewModelListSubject.asObservable().pipe(
|
||||
map((recos: ViewChangeReco[]) => {
|
||||
map((recos: ViewMotionChangeRecommendation[]) => {
|
||||
return recos.filter(reco => reco.motion_id === motion_id);
|
||||
})
|
||||
);
|
||||
@ -117,16 +125,16 @@ export class ChangeRecommendationRepositoryService extends BaseRepository<ViewCh
|
||||
* @param motionId the id of the target motion
|
||||
* @returns the array of change recommendations to the motions.
|
||||
*/
|
||||
public getChangeRecoOfMotion(motion_id: number): ViewChangeReco[] {
|
||||
public getChangeRecoOfMotion(motion_id: number): ViewMotionChangeRecommendation[] {
|
||||
return this.getViewModelList().filter(reco => reco.motion_id === motion_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a change recommendation to accepted.
|
||||
*
|
||||
* @param {ViewChangeReco} change
|
||||
* @param {ViewMotionChangeRecommendation} change
|
||||
*/
|
||||
public async setAccepted(change: ViewChangeReco): Promise<void> {
|
||||
public async setAccepted(change: ViewMotionChangeRecommendation): Promise<void> {
|
||||
const changeReco = change.changeRecommendation;
|
||||
changeReco.patchValues({
|
||||
rejected: false
|
||||
@ -137,9 +145,9 @@ export class ChangeRecommendationRepositoryService extends BaseRepository<ViewCh
|
||||
/**
|
||||
* Sets a change recommendation to rejected.
|
||||
*
|
||||
* @param {ViewChangeReco} change
|
||||
* @param {ViewMotionChangeRecommendation} change
|
||||
*/
|
||||
public async setRejected(change: ViewChangeReco): Promise<void> {
|
||||
public async setRejected(change: ViewMotionChangeRecommendation): Promise<void> {
|
||||
const changeReco = change.changeRecommendation;
|
||||
changeReco.patchValues({
|
||||
rejected: true
|
||||
@ -150,10 +158,10 @@ export class ChangeRecommendationRepositoryService extends BaseRepository<ViewCh
|
||||
/**
|
||||
* Sets if a change recommendation is internal (for the administrators) or not.
|
||||
*
|
||||
* @param {ViewChangeReco} change
|
||||
* @param {ViewMotionChangeRecommendation} change
|
||||
* @param {boolean} internal
|
||||
*/
|
||||
public async setInternal(change: ViewChangeReco, internal: boolean): Promise<void> {
|
||||
public async setInternal(change: ViewMotionChangeRecommendation, internal: boolean): Promise<void> {
|
||||
const changeReco = change.changeRecommendation;
|
||||
changeReco.patchValues({
|
||||
internal: internal
|
||||
|
@ -14,6 +14,9 @@ import { MotionBlock } from 'app/shared/models/motions/motion-block';
|
||||
import { MotionRepositoryService } from './motion-repository.service';
|
||||
import { ViewMotion } from 'app/site/motions/models/view-motion';
|
||||
import { ViewMotionBlock } from 'app/site/motions/models/view-motion-block';
|
||||
import { ViewModelStoreService } from 'app/core/core-services/view-model-store.service';
|
||||
import { Item } from 'app/shared/models/agenda/item';
|
||||
import { ViewItem } from 'app/site/agenda/models/view-item';
|
||||
|
||||
/**
|
||||
* Repository service for motion blocks
|
||||
@ -34,11 +37,12 @@ export class MotionBlockRepositoryService extends BaseRepository<ViewMotionBlock
|
||||
public constructor(
|
||||
DS: DataStoreService,
|
||||
mapperService: CollectionStringMapperService,
|
||||
viewModelStoreService: ViewModelStoreService,
|
||||
private dataSend: DataSendService,
|
||||
private motionRepo: MotionRepositoryService,
|
||||
private httpService: HttpService
|
||||
) {
|
||||
super(DS, mapperService, MotionBlock);
|
||||
super(DS, mapperService, viewModelStoreService, MotionBlock, [Item]);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -80,7 +84,8 @@ export class MotionBlockRepositoryService extends BaseRepository<ViewMotionBlock
|
||||
* @returns a new ViewMotionBlock
|
||||
*/
|
||||
protected createViewModel(block: MotionBlock): ViewMotionBlock {
|
||||
return new ViewMotionBlock(block);
|
||||
const item = this.viewModelStoreService.get(ViewItem, block.agenda_item_id);
|
||||
return new ViewMotionBlock(block, item);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -9,6 +9,8 @@ import { Group } from 'app/shared/models/users/group';
|
||||
import { Identifiable } from 'app/shared/models/base/identifiable';
|
||||
import { CollectionStringMapperService } from '../../core-services/collectionStringMapper.service';
|
||||
import { HttpService } from 'app/core/core-services/http.service';
|
||||
import { ViewModelStoreService } from 'app/core/core-services/view-model-store.service';
|
||||
import { ViewGroup } from 'app/site/users/models/view-group';
|
||||
|
||||
/**
|
||||
* Repository Services for Categories
|
||||
@ -38,12 +40,13 @@ export class MotionCommentSectionRepositoryService extends BaseRepository<
|
||||
* @param http Service to handle direct http-communication
|
||||
*/
|
||||
public constructor(
|
||||
DS: DataStoreService,
|
||||
mapperService: CollectionStringMapperService,
|
||||
protected DS: DataStoreService,
|
||||
viewModelStoreService: ViewModelStoreService,
|
||||
private dataSend: DataSendService,
|
||||
private http: HttpService
|
||||
) {
|
||||
super(DS, mapperService, MotionCommentSection, [Group]);
|
||||
super(DS, mapperService, viewModelStoreService, MotionCommentSection, [Group]);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -53,9 +56,9 @@ export class MotionCommentSectionRepositoryService extends BaseRepository<
|
||||
* @returns the View Model representation of the MotionCommentSection
|
||||
*/
|
||||
protected createViewModel(section: MotionCommentSection): ViewMotionCommentSection {
|
||||
const read_groups = this.DS.getMany(Group, section.read_groups_id);
|
||||
const write_groups = this.DS.getMany(Group, section.write_groups_id);
|
||||
return new ViewMotionCommentSection(section, read_groups, write_groups);
|
||||
const readGroups = this.viewModelStoreService.getMany(ViewGroup, section.read_groups_id);
|
||||
const writeGroups = this.viewModelStoreService.getMany(ViewGroup, section.write_groups_id);
|
||||
return new ViewMotionCommentSection(section, readGroups, writeGroups);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -19,19 +19,27 @@ import { LinenumberingService } from '../../ui-services/linenumbering.service';
|
||||
import { Mediafile } from 'app/shared/models/mediafiles/mediafile';
|
||||
import { Motion } from 'app/shared/models/motions/motion';
|
||||
import { MotionBlock } from 'app/shared/models/motions/motion-block';
|
||||
import { MotionChangeReco } from 'app/shared/models/motions/motion-change-reco';
|
||||
import { MotionChangeRecommendation } from 'app/shared/models/motions/motion-change-reco';
|
||||
import { MotionPoll } from 'app/shared/models/motions/motion-poll';
|
||||
import { OSTreeSortEvent } from 'app/shared/components/sorting-tree/sorting-tree.component';
|
||||
import { PersonalNoteService } from '../../ui-services/personal-note.service';
|
||||
import { TreeService } from 'app/core/ui-services/tree.service';
|
||||
import { User } from '../../../shared/models/users/user';
|
||||
import { ViewChangeReco } from '../../../site/motions/models/view-change-reco';
|
||||
import { ViewMotionAmendedParagraph } from '../../../site/motions/models/view-motion-amended-paragraph';
|
||||
import { ViewUnifiedChange } from '../../../site/motions/models/view-unified-change';
|
||||
import { ViewStatuteParagraph } from '../../../site/motions/models/view-statute-paragraph';
|
||||
import { Workflow } from '../../../shared/models/motions/workflow';
|
||||
import { WorkflowState } from '../../../shared/models/motions/workflow-state';
|
||||
import { User } from 'app/shared/models/users/user';
|
||||
import { ViewMotionChangeRecommendation } from 'app/site/motions/models/view-change-recommendation';
|
||||
import { ViewMotionAmendedParagraph } from 'app/site/motions/models/view-motion-amended-paragraph';
|
||||
import { ViewUnifiedChange } from 'app/site/motions/models/view-unified-change';
|
||||
import { ViewStatuteParagraph } from 'app/site/motions/models/view-statute-paragraph';
|
||||
import { Workflow } from 'app/shared/models/motions/workflow';
|
||||
import { WorkflowState } from 'app/shared/models/motions/workflow-state';
|
||||
import { Tag } from 'app/shared/models/core/tag';
|
||||
import { ViewModelStoreService } from 'app/core/core-services/view-model-store.service';
|
||||
import { ViewCategory } from 'app/site/motions/models/view-category';
|
||||
import { ViewUser } from 'app/site/users/models/view-user';
|
||||
import { ViewWorkflow } from 'app/site/motions/models/view-workflow';
|
||||
import { ViewItem } from 'app/site/agenda/models/view-item';
|
||||
import { ViewMotionBlock } from 'app/site/motions/models/view-motion-block';
|
||||
import { ViewMediafile } from 'app/site/mediafiles/models/view-mediafile';
|
||||
import { ViewTag } from 'app/site/tags/models/view-tag';
|
||||
|
||||
/**
|
||||
* Repository Services for motions (and potentially categories)
|
||||
@ -64,6 +72,7 @@ export class MotionRepositoryService extends BaseRepository<ViewMotion, Motion>
|
||||
public constructor(
|
||||
DS: DataStoreService,
|
||||
mapperService: CollectionStringMapperService,
|
||||
viewModelStoreService: ViewModelStoreService,
|
||||
private dataSend: DataSendService,
|
||||
private httpService: HttpService,
|
||||
private readonly lineNumbering: LinenumberingService,
|
||||
@ -72,7 +81,15 @@ export class MotionRepositoryService extends BaseRepository<ViewMotion, Motion>
|
||||
private personalNoteService: PersonalNoteService,
|
||||
private translate: TranslateService
|
||||
) {
|
||||
super(DS, mapperService, Motion, [Category, User, Workflow, Item, MotionBlock, Mediafile, Tag]);
|
||||
super(DS, mapperService, viewModelStoreService, Motion, [
|
||||
Category,
|
||||
User,
|
||||
Workflow,
|
||||
Item,
|
||||
MotionBlock,
|
||||
Mediafile,
|
||||
Tag
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -84,15 +101,15 @@ export class MotionRepositoryService extends BaseRepository<ViewMotion, Motion>
|
||||
* @param motion blank motion domain object
|
||||
*/
|
||||
protected createViewModel(motion: Motion): ViewMotion {
|
||||
const category = this.DS.get(Category, motion.category_id);
|
||||
const submitters = this.DS.getMany(User, motion.submitterIds);
|
||||
const supporters = this.DS.getMany(User, motion.supporters_id);
|
||||
const workflow = this.DS.get(Workflow, motion.workflow_id);
|
||||
const item = this.DS.get(Item, motion.agenda_item_id);
|
||||
const block = this.DS.get(MotionBlock, motion.motion_block_id);
|
||||
const attachments = this.DS.getMany(Mediafile, motion.attachments_id);
|
||||
const tags = this.DS.getMany(Tag, motion.tags_id);
|
||||
const parent = this.DS.get(Motion, motion.parent_id);
|
||||
const category = this.viewModelStoreService.get(ViewCategory, motion.category_id);
|
||||
const submitters = this.viewModelStoreService.getMany(ViewUser, motion.submitterIds);
|
||||
const supporters = this.viewModelStoreService.getMany(ViewUser, motion.supporters_id);
|
||||
const workflow = this.viewModelStoreService.get(ViewWorkflow, motion.workflow_id);
|
||||
const item = this.viewModelStoreService.get(ViewItem, motion.agenda_item_id);
|
||||
const block = this.viewModelStoreService.get(ViewMotionBlock, motion.motion_block_id);
|
||||
const attachments = this.viewModelStoreService.getMany(ViewMediafile, motion.attachments_id);
|
||||
const tags = this.viewModelStoreService.getMany(ViewTag, motion.tags_id);
|
||||
const parent = this.viewModelStoreService.get(ViewMotion, motion.parent_id);
|
||||
let state: WorkflowState = null;
|
||||
if (workflow) {
|
||||
state = workflow.getStateById(motion.state_id);
|
||||
@ -260,7 +277,7 @@ export class MotionRepositoryService extends BaseRepository<ViewMotion, Motion>
|
||||
* @param viewMotion The motion to change the submitters from
|
||||
* @param submitters The submitters to set
|
||||
*/
|
||||
public async setSubmitters(viewMotion: ViewMotion, submitters: User[]): Promise<void> {
|
||||
public async setSubmitters(viewMotion: ViewMotion, submitters: ViewUser[]): Promise<void> {
|
||||
const requestData = {
|
||||
motions: [
|
||||
{
|
||||
@ -525,8 +542,8 @@ export class MotionRepositoryService extends BaseRepository<ViewMotion, Motion>
|
||||
motionId: number,
|
||||
lineRange: LineRange,
|
||||
lineLength: number
|
||||
): ViewChangeReco {
|
||||
const changeReco = new MotionChangeReco();
|
||||
): ViewMotionChangeRecommendation {
|
||||
const changeReco = new MotionChangeRecommendation();
|
||||
changeReco.line_from = lineRange.from;
|
||||
changeReco.line_to = lineRange.to;
|
||||
changeReco.type = ModificationType.TYPE_REPLACEMENT;
|
||||
@ -534,7 +551,7 @@ export class MotionRepositoryService extends BaseRepository<ViewMotion, Motion>
|
||||
changeReco.rejected = false;
|
||||
changeReco.motion_id = motionId;
|
||||
|
||||
return new ViewChangeReco(changeReco);
|
||||
return new ViewMotionChangeRecommendation(changeReco);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -7,6 +7,7 @@ import { ViewStatuteParagraph } from 'app/site/motions/models/view-statute-parag
|
||||
import { StatuteParagraph } from 'app/shared/models/motions/statute-paragraph';
|
||||
import { Identifiable } from 'app/shared/models/base/identifiable';
|
||||
import { CollectionStringMapperService } from '../../core-services/collectionStringMapper.service';
|
||||
import { ViewModelStoreService } from 'app/core/core-services/view-model-store.service';
|
||||
|
||||
/**
|
||||
* Repository Services for statute paragraphs
|
||||
@ -31,9 +32,10 @@ export class StatuteParagraphRepositoryService extends BaseRepository<ViewStatut
|
||||
public constructor(
|
||||
DS: DataStoreService,
|
||||
mapperService: CollectionStringMapperService,
|
||||
viewModelStoreService: ViewModelStoreService,
|
||||
private dataSend: DataSendService
|
||||
) {
|
||||
super(DS, mapperService, StatuteParagraph);
|
||||
super(DS, mapperService, viewModelStoreService, StatuteParagraph);
|
||||
}
|
||||
|
||||
protected createViewModel(statuteParagraph: StatuteParagraph): ViewStatuteParagraph {
|
||||
|
@ -10,6 +10,7 @@ import { CollectionStringMapperService } from '../../core-services/collectionStr
|
||||
import { WorkflowState } from 'app/shared/models/motions/workflow-state';
|
||||
import { ViewMotion } from 'app/site/motions/models/view-motion';
|
||||
import { HttpService } from 'app/core/core-services/http.service';
|
||||
import { ViewModelStoreService } from 'app/core/core-services/view-model-store.service';
|
||||
|
||||
/**
|
||||
* Repository Services for Categories
|
||||
@ -40,12 +41,13 @@ export class WorkflowRepositoryService extends BaseRepository<ViewWorkflow, Work
|
||||
* @param httpService HttpService
|
||||
*/
|
||||
public constructor(
|
||||
protected DS: DataStoreService,
|
||||
DS: DataStoreService,
|
||||
mapperService: CollectionStringMapperService,
|
||||
private dataSend: DataSendService,
|
||||
private httpService: HttpService
|
||||
private httpService: HttpService,
|
||||
viewModelStoreService: ViewModelStoreService,
|
||||
private dataSend: DataSendService
|
||||
) {
|
||||
super(DS, mapperService, Workflow);
|
||||
super(DS, mapperService, viewModelStoreService, Workflow);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -6,6 +6,7 @@ import { Identifiable } from 'app/shared/models/base/identifiable';
|
||||
import { CollectionStringMapperService } from '../../core-services/collectionStringMapper.service';
|
||||
import { ViewCountdown } from 'app/site/projector/models/view-countdown';
|
||||
import { Countdown } from 'app/shared/models/core/countdown';
|
||||
import { ViewModelStoreService } from 'app/core/core-services/view-model-store.service';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
@ -14,9 +15,10 @@ export class CountdownRepositoryService extends BaseRepository<ViewCountdown, Co
|
||||
public constructor(
|
||||
DS: DataStoreService,
|
||||
mapperService: CollectionStringMapperService,
|
||||
viewModelStoreService: ViewModelStoreService,
|
||||
private dataSend: DataSendService
|
||||
) {
|
||||
super(DS, mapperService, Countdown);
|
||||
super(DS, mapperService, viewModelStoreService, Countdown);
|
||||
}
|
||||
|
||||
protected createViewModel(countdown: Countdown): ViewCountdown {
|
||||
|
@ -8,6 +8,7 @@ import { Identifiable } from 'app/shared/models/base/identifiable';
|
||||
import { ViewProjector } from 'app/site/projector/models/view-projector';
|
||||
import { Projector } from 'app/shared/models/core/projector';
|
||||
import { HttpService } from 'app/core/core-services/http.service';
|
||||
import { ViewModelStoreService } from 'app/core/core-services/view-model-store.service';
|
||||
|
||||
/**
|
||||
* Directions for scale and scroll requests.
|
||||
@ -36,10 +37,11 @@ export class ProjectorRepositoryService extends BaseRepository<ViewProjector, Pr
|
||||
public constructor(
|
||||
DS: DataStoreService,
|
||||
mapperService: CollectionStringMapperService,
|
||||
viewModelStoreService: ViewModelStoreService,
|
||||
private dataSend: DataSendService,
|
||||
private http: HttpService
|
||||
) {
|
||||
super(DS, mapperService, Projector);
|
||||
super(DS, mapperService, viewModelStoreService, Projector);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -5,13 +5,18 @@ import { Identifiable } from 'app/shared/models/base/identifiable';
|
||||
import { CollectionStringMapperService } from '../../core-services/collectionStringMapper.service';
|
||||
import { ProjectorMessage } from 'app/shared/models/core/projector-message';
|
||||
import { ViewProjectorMessage } from 'app/site/projector/models/view-projectormessage';
|
||||
import { ViewModelStoreService } from 'app/core/core-services/view-model-store.service';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class ProjectorMessageRepositoryService extends BaseRepository<ViewProjectorMessage, ProjectorMessage> {
|
||||
public constructor(DS: DataStoreService, mapperService: CollectionStringMapperService) {
|
||||
super(DS, mapperService, ProjectorMessage);
|
||||
public constructor(
|
||||
DS: DataStoreService,
|
||||
mapperService: CollectionStringMapperService,
|
||||
viewModelStoreService: ViewModelStoreService
|
||||
) {
|
||||
super(DS, mapperService, viewModelStoreService, ProjectorMessage);
|
||||
}
|
||||
|
||||
protected createViewModel(message: ProjectorMessage): ViewProjectorMessage {
|
||||
|
@ -7,6 +7,7 @@ import { DataStoreService } from '../../core-services/data-store.service';
|
||||
import { BaseRepository } from '../base-repository';
|
||||
import { Identifiable } from 'app/shared/models/base/identifiable';
|
||||
import { CollectionStringMapperService } from '../../core-services/collectionStringMapper.service';
|
||||
import { ViewModelStoreService } from 'app/core/core-services/view-model-store.service';
|
||||
|
||||
/**
|
||||
* Repository Services for Tags
|
||||
@ -34,9 +35,10 @@ export class TagRepositoryService extends BaseRepository<ViewTag, Tag> {
|
||||
public constructor(
|
||||
protected DS: DataStoreService,
|
||||
mapperService: CollectionStringMapperService,
|
||||
viewModelStoreService: ViewModelStoreService,
|
||||
private dataSend: DataSendService
|
||||
) {
|
||||
super(DS, mapperService, Tag);
|
||||
super(DS, mapperService, viewModelStoreService, Tag);
|
||||
}
|
||||
|
||||
protected createViewModel(tag: Tag): ViewTag {
|
||||
|
@ -8,6 +8,7 @@ import { DataStoreService } from '../../core-services/data-store.service';
|
||||
import { Group } from 'app/shared/models/users/group';
|
||||
import { Identifiable } from 'app/shared/models/base/identifiable';
|
||||
import { ViewGroup } from 'app/site/users/models/view-group';
|
||||
import { ViewModelStoreService } from 'app/core/core-services/view-model-store.service';
|
||||
|
||||
/**
|
||||
* Shape of a permission
|
||||
@ -49,10 +50,11 @@ export class GroupRepositoryService extends BaseRepository<ViewGroup, Group> {
|
||||
public constructor(
|
||||
DS: DataStoreService,
|
||||
mapperService: CollectionStringMapperService,
|
||||
viewModelStoreService: ViewModelStoreService,
|
||||
private dataSend: DataSendService,
|
||||
private constants: ConstantsService
|
||||
) {
|
||||
super(DS, mapperService, Group);
|
||||
super(DS, mapperService, viewModelStoreService, Group);
|
||||
this.sortPermsPerApp();
|
||||
}
|
||||
|
||||
|
@ -5,34 +5,40 @@ import { BaseRepository } from '../base-repository';
|
||||
import { CollectionStringMapperService } from '../../core-services/collectionStringMapper.service';
|
||||
import { PersonalNote } from 'app/shared/models/users/personal-note';
|
||||
import { Identifiable } from 'app/shared/models/base/identifiable';
|
||||
import { ViewPersonalNote } from 'app/site/users/models/view-personal-note';
|
||||
import { ViewModelStoreService } from 'app/core/core-services/view-model-store.service';
|
||||
|
||||
/**
|
||||
*/
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class PersonalNoteRepositoryService extends BaseRepository<any, PersonalNote> {
|
||||
export class PersonalNoteRepositoryService extends BaseRepository<ViewPersonalNote, PersonalNote> {
|
||||
/**
|
||||
* @param DS The DataStore
|
||||
* @param mapperService Maps collection strings to classes
|
||||
*/
|
||||
public constructor(protected DS: DataStoreService, mapperService: CollectionStringMapperService) {
|
||||
super(DS, mapperService, PersonalNote);
|
||||
public constructor(
|
||||
DS: DataStoreService,
|
||||
mapperService: CollectionStringMapperService,
|
||||
viewModelStoreService: ViewModelStoreService
|
||||
) {
|
||||
super(DS, mapperService, viewModelStoreService, PersonalNote);
|
||||
}
|
||||
|
||||
protected createViewModel(personalNote: PersonalNote): any {
|
||||
return {};
|
||||
protected createViewModel(personalNote: PersonalNote): ViewPersonalNote {
|
||||
return new ViewPersonalNote();
|
||||
}
|
||||
|
||||
public async create(personalNote: PersonalNote): Promise<Identifiable> {
|
||||
throw new Error('TODO');
|
||||
}
|
||||
|
||||
public async update(personalNote: Partial<PersonalNote>, viewPersonalNote: any): Promise<void> {
|
||||
public async update(personalNote: Partial<PersonalNote>, viewPersonalNote: ViewPersonalNote): Promise<void> {
|
||||
throw new Error('TODO');
|
||||
}
|
||||
|
||||
public async delete(viewPersonalNote: any): Promise<void> {
|
||||
public async delete(viewPersonalNote: ViewPersonalNote): Promise<void> {
|
||||
throw new Error('TODO');
|
||||
}
|
||||
}
|
||||
|
@ -12,6 +12,8 @@ import { ConfigService } from 'app/core/ui-services/config.service';
|
||||
import { HttpService } from 'app/core/core-services/http.service';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { environment } from '../../../../environments/environment';
|
||||
import { ViewModelStoreService } from 'app/core/core-services/view-model-store.service';
|
||||
import { ViewGroup } from 'app/site/users/models/view-group';
|
||||
|
||||
/**
|
||||
* type for determining the user name from a string during import.
|
||||
@ -38,12 +40,13 @@ export class UserRepositoryService extends BaseRepository<ViewUser, User> {
|
||||
public constructor(
|
||||
DS: DataStoreService,
|
||||
mapperService: CollectionStringMapperService,
|
||||
viewModelStoreService: ViewModelStoreService,
|
||||
private dataSend: DataSendService,
|
||||
private translate: TranslateService,
|
||||
private httpService: HttpService,
|
||||
private configService: ConfigService
|
||||
) {
|
||||
super(DS, mapperService, User, [Group]);
|
||||
super(DS, mapperService, viewModelStoreService, User, [Group]);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -104,7 +107,7 @@ export class UserRepositoryService extends BaseRepository<ViewUser, User> {
|
||||
}
|
||||
|
||||
public createViewModel(user: User): ViewUser {
|
||||
const groups = this.DS.getMany(Group, user.groups_id);
|
||||
const groups = this.viewModelStoreService.getMany(ViewGroup, user.groups_id);
|
||||
return new ViewUser(user, groups);
|
||||
}
|
||||
|
||||
@ -218,20 +221,9 @@ export class UserRepositoryService extends BaseRepository<ViewUser, User> {
|
||||
* @returns all users matching that name
|
||||
*/
|
||||
public getUsersByName(name: string): ViewUser[] {
|
||||
const results: ViewUser[] = [];
|
||||
const users = this.DS.getAll(User).filter(user => {
|
||||
if (user.full_name === name || user.short_name === name) {
|
||||
return true;
|
||||
}
|
||||
if (user.number === name) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
return this.getViewModelList().filter(user => {
|
||||
return user.full_name === name || user.short_name === name || user.number === name;
|
||||
});
|
||||
users.forEach(user => {
|
||||
results.push(this.createViewModel(user));
|
||||
});
|
||||
return results;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -241,7 +233,7 @@ export class UserRepositoryService extends BaseRepository<ViewUser, User> {
|
||||
* @returns all users matching that number
|
||||
*/
|
||||
public getUsersByNumber(number: string): ViewUser[] {
|
||||
return this.getViewModelList().filter(user => user.participant_number === number);
|
||||
return this.getViewModelList().filter(user => user.number === number);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -61,7 +61,7 @@ export class CountUsersService extends OpenSlidesComponent {
|
||||
});
|
||||
|
||||
// Look for the current user.
|
||||
operator.getObservable().subscribe(user => (this.currentUserId = user ? user.id : null));
|
||||
operator.getUserObservable().subscribe(user => (this.currentUserId = user ? user.id : null));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -41,7 +41,7 @@ export class PersonalNoteService {
|
||||
* Watches for changes in the personal note model.
|
||||
*/
|
||||
public constructor(private operator: OperatorService, private DS: DataStoreService, private http: HttpService) {
|
||||
operator.getObservable().subscribe(() => this.updatePersonalNoteObject());
|
||||
operator.getUserObservable().subscribe(() => this.updatePersonalNoteObject());
|
||||
this.DS.changeObservable.subscribe(model => {
|
||||
if (model instanceof PersonalNote) {
|
||||
this.updatePersonalNoteObject();
|
||||
|
@ -1,7 +1,6 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { BaseModel } from '../../shared/models/base/base-model';
|
||||
import { DataStoreService } from '../core-services/data-store.service';
|
||||
import { Searchable } from '../../shared/models/base/searchable';
|
||||
import { Searchable } from '../../site/base/searchable';
|
||||
import { BaseViewModel } from 'app/site/base/base-view-model';
|
||||
|
||||
/**
|
||||
* The representation every searchable model should use to represent their data.
|
||||
@ -46,7 +45,7 @@ export interface SearchResult {
|
||||
/**
|
||||
* All matched models.
|
||||
*/
|
||||
models: (BaseModel & Searchable)[];
|
||||
models: (BaseViewModel & Searchable)[];
|
||||
}
|
||||
|
||||
/**
|
||||
@ -61,7 +60,6 @@ export class SearchService {
|
||||
*/
|
||||
private searchModels: {
|
||||
collectionString: string;
|
||||
ctor: new (...args: any[]) => Searchable & BaseModel;
|
||||
verboseNameSingular: string;
|
||||
verboseNamePlural: string;
|
||||
displayOrder: number;
|
||||
@ -70,7 +68,7 @@ export class SearchService {
|
||||
/**
|
||||
* @param DS The DataStore to search in.
|
||||
*/
|
||||
public constructor(private DS: DataStoreService) {}
|
||||
public constructor() {}
|
||||
|
||||
/**
|
||||
* Registers a model by the given attributes.
|
||||
@ -81,13 +79,12 @@ export class SearchService {
|
||||
*/
|
||||
public registerModel(
|
||||
collectionString: string,
|
||||
ctor: new (...args: any[]) => Searchable & BaseModel,
|
||||
ctor: new (...args: any[]) => Searchable & BaseViewModel,
|
||||
displayOrder: number
|
||||
): void {
|
||||
const instance = new ctor();
|
||||
this.searchModels.push({
|
||||
collectionString: collectionString,
|
||||
ctor: ctor,
|
||||
verboseNameSingular: instance.getVerboseName(),
|
||||
verboseNamePlural: instance.getVerboseName(true),
|
||||
displayOrder: displayOrder
|
||||
@ -115,10 +112,10 @@ export class SearchService {
|
||||
*/
|
||||
public search(query: string, inCollectionStrings: string[]): SearchResult[] {
|
||||
query = query.toLowerCase();
|
||||
return this.searchModels
|
||||
/*return this.searchModels
|
||||
.filter(s => inCollectionStrings.includes(s.collectionString))
|
||||
.map(searchModel => {
|
||||
const results = this.DS.filter(searchModel.ctor, model =>
|
||||
const results = this.viewModelStore.filter(searchModel.collectionString, model =>
|
||||
model.formatForSearch().some(text => text.toLowerCase().includes(query))
|
||||
);
|
||||
return {
|
||||
@ -126,6 +123,8 @@ export class SearchService {
|
||||
verboseName: results.length === 1 ? searchModel.verboseNameSingular : searchModel.verboseNamePlural,
|
||||
models: results
|
||||
};
|
||||
});
|
||||
});*/
|
||||
throw new Error('Todo');
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
|
||||
import { OpenSlidesComponent } from 'app/openslides.component';
|
||||
import { Displayable } from 'app/shared/models/base/displayable';
|
||||
import { Displayable } from 'app/site/base/displayable';
|
||||
import { Identifiable } from 'app/shared/models/base/identifiable';
|
||||
|
||||
/**
|
||||
|
@ -88,7 +88,7 @@ export class FullscreenProjectorComponent implements OnInit {
|
||||
this.isLoading = false;
|
||||
});
|
||||
|
||||
this.operator.getObservable().subscribe(() => {
|
||||
this.operator.getUserObservable().subscribe(() => {
|
||||
this.canSeeProjector = this.operator.hasPerms('projector.can_see');
|
||||
});
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { Component, Inject } from '@angular/core';
|
||||
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material';
|
||||
|
||||
import { Displayable } from 'app/shared/models/base/displayable';
|
||||
import { Displayable } from 'app/site/base/displayable';
|
||||
import { Identifiable } from 'app/shared/models/base/identifiable';
|
||||
|
||||
/**
|
||||
|
@ -373,7 +373,7 @@ export class C4DialogComponent implements OnInit, OnDestroy {
|
||||
* Returns the operators name.
|
||||
*/
|
||||
public getPlayerName(): string {
|
||||
return this.op.user.short_name;
|
||||
return this.op.viewUser.short_name;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -221,9 +221,9 @@ export class ProjectorComponent extends BaseComponent implements OnDestroy {
|
||||
this.projectorDataService.projectorClosed(from);
|
||||
}
|
||||
|
||||
this.dataSubscription = this.projectorDataService
|
||||
.getProjectorObservable(to)
|
||||
.subscribe(data => (this.slides = data || []));
|
||||
this.dataSubscription = this.projectorDataService.getProjectorObservable(to).subscribe(data => {
|
||||
this.slides = data || [];
|
||||
});
|
||||
this.projectorSubscription = this.projectorRepository.getViewModelObservable(to).subscribe(projector => {
|
||||
if (projector) {
|
||||
this.scroll = projector.scroll || 0;
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { Displayable } from '../models/base/displayable';
|
||||
import { Displayable } from '../../site/base/displayable';
|
||||
import { Identifiable } from '../models/base/identifiable';
|
||||
|
||||
/**
|
||||
|
@ -2,7 +2,7 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { E2EImportsModule } from '../../../../e2e-imports.module';
|
||||
import { SortingTreeComponent } from './sorting-tree.component';
|
||||
import { Component, ViewChild } from '@angular/core';
|
||||
import { Displayable } from 'app/shared/models/base/displayable';
|
||||
import { Displayable } from 'app/site/base/displayable';
|
||||
import { Identifiable } from 'app/shared/models/base/identifiable';
|
||||
import { BehaviorSubject } from 'rxjs';
|
||||
|
||||
|
@ -6,7 +6,7 @@ import { auditTime } from 'rxjs/operators';
|
||||
import { Subscription, Observable } from 'rxjs';
|
||||
|
||||
import { Identifiable } from 'app/shared/models/base/identifiable';
|
||||
import { Displayable } from 'app/shared/models/base/displayable';
|
||||
import { Displayable } from 'app/site/base/displayable';
|
||||
import { OSTreeNode, TreeService } from 'app/core/ui-services/tree.service';
|
||||
|
||||
/**
|
||||
|
@ -68,7 +68,7 @@ export class PermsDirective extends OpenSlidesComponent implements OnInit, OnDes
|
||||
|
||||
public ngOnInit(): void {
|
||||
// observe groups of operator, so the directive can actively react to changes
|
||||
this.operatorSubscription = this.operator.getObservable().subscribe(() => {
|
||||
this.operatorSubscription = this.operator.getUserObservable().subscribe(() => {
|
||||
this.updateView();
|
||||
});
|
||||
}
|
||||
|
@ -41,7 +41,7 @@ export class Item extends BaseModel<Item> {
|
||||
public parent_id: number;
|
||||
|
||||
public constructor(input?: any) {
|
||||
super('agenda/item', 'Item', input);
|
||||
super('agenda/item', input);
|
||||
}
|
||||
|
||||
public deserialize(input: any): void {
|
||||
@ -84,16 +84,4 @@ export class Item extends BaseModel<Item> {
|
||||
const type = itemVisibilityChoices.find(choice => choice.key === this.type);
|
||||
return type ? type.csvName : '';
|
||||
}
|
||||
|
||||
public getTitle(): string {
|
||||
return this.title;
|
||||
}
|
||||
|
||||
public getListTitle(): string {
|
||||
return this.title_with_type;
|
||||
}
|
||||
|
||||
public getProjectorTitle(): string {
|
||||
return this.getListTitle();
|
||||
}
|
||||
}
|
||||
|
@ -59,12 +59,4 @@ export class Speaker extends Deserializer {
|
||||
return SpeakerState.FINISHED;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Getting the title of a speaker does not make much sense.
|
||||
* Usually it would refer to the title of a user.
|
||||
*/
|
||||
public getTitle(): string {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,6 @@
|
||||
import { AssignmentUser } from './assignment-user';
|
||||
import { Poll } from './poll';
|
||||
import { AgendaBaseModel } from '../base/agenda-base-model';
|
||||
import { SearchRepresentation } from 'app/core/ui-services/search.service';
|
||||
import { BaseModel } from '../base/base-model';
|
||||
|
||||
export const assignmentPhase = [
|
||||
{ key: 0, name: 'Searching for candidates' },
|
||||
@ -13,7 +12,7 @@ export const assignmentPhase = [
|
||||
* Representation of an assignment.
|
||||
* @ignore
|
||||
*/
|
||||
export class Assignment extends AgendaBaseModel {
|
||||
export class Assignment extends BaseModel<Assignment> {
|
||||
public id: number;
|
||||
public title: string;
|
||||
public description: string;
|
||||
@ -26,7 +25,7 @@ export class Assignment extends AgendaBaseModel {
|
||||
public tags_id: number[];
|
||||
|
||||
public constructor(input?: any) {
|
||||
super('assignments/assignment', 'Election', input);
|
||||
super('assignments/assignment', input);
|
||||
}
|
||||
|
||||
public get candidateIds(): number[] {
|
||||
@ -54,16 +53,4 @@ export class Assignment extends AgendaBaseModel {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public getTitle(): string {
|
||||
return this.title;
|
||||
}
|
||||
|
||||
public formatForSearch(): SearchRepresentation {
|
||||
return [this.title, this.description];
|
||||
}
|
||||
|
||||
public getDetailStateURL(): string {
|
||||
return 'TODO';
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { OpenSlidesComponent } from 'app/openslides.component';
|
||||
import { Deserializable } from './deserializable';
|
||||
import { Displayable } from './displayable';
|
||||
import { Identifiable } from './identifiable';
|
||||
import { Collection } from './collection';
|
||||
|
||||
export type ModelConstructor<T extends BaseModel<T>> = new (...args: any[]) => T;
|
||||
|
||||
@ -10,7 +10,7 @@ export type ModelConstructor<T extends BaseModel<T>> = new (...args: any[]) => T
|
||||
* When inherit from this class, give the subclass as the type. E.g. `class Motion extends BaseModel<Motion>`
|
||||
*/
|
||||
export abstract class BaseModel<T = object> extends OpenSlidesComponent
|
||||
implements Deserializable, Displayable, Identifiable {
|
||||
implements Deserializable, Identifiable, Collection {
|
||||
/**
|
||||
* force children of BaseModel to have a collectionString.
|
||||
*
|
||||
@ -27,11 +27,6 @@ export abstract class BaseModel<T = object> extends OpenSlidesComponent
|
||||
return this._collectionString;
|
||||
}
|
||||
|
||||
/**
|
||||
* Children should also have a verbose name for generic display purposes
|
||||
*/
|
||||
protected _verboseName: string;
|
||||
|
||||
/**
|
||||
* force children of BaseModel to have an id
|
||||
*/
|
||||
@ -44,10 +39,9 @@ export abstract class BaseModel<T = object> extends OpenSlidesComponent
|
||||
* @param verboseName
|
||||
* @param input
|
||||
*/
|
||||
protected constructor(collectionString: string, verboseName: string, input?: any) {
|
||||
protected constructor(collectionString: string, input?: any) {
|
||||
super();
|
||||
this._collectionString = collectionString;
|
||||
this._verboseName = verboseName;
|
||||
|
||||
if (input) {
|
||||
this.changeNullValuesToUndef(input);
|
||||
@ -74,32 +68,6 @@ export abstract class BaseModel<T = object> extends OpenSlidesComponent
|
||||
Object.assign(this, update);
|
||||
}
|
||||
|
||||
public abstract getTitle(): string;
|
||||
|
||||
public getListTitle(): string {
|
||||
return this.getTitle();
|
||||
}
|
||||
|
||||
public toString(): string {
|
||||
return this.getTitle();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the verbose name. Makes it plural by adding a 's'.
|
||||
*
|
||||
* @param plural If the name should be plural
|
||||
* @returns the verbose name of the model
|
||||
*/
|
||||
public getVerboseName(plural: boolean = false): string {
|
||||
if (plural) {
|
||||
return this._verboseName + 's'; // I love english. This works for all our models (participantS, electionS,
|
||||
// topicS, motionS, (media)fileS, motion blockS, commentS, personal noteS, projectorS, messageS, countdownS, ...)
|
||||
// Just categorIES need to overwrite this...
|
||||
} else {
|
||||
return this._verboseName;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Most simple and most commonly used deserialize function.
|
||||
* Inherited to children, can be overwritten for special use cases
|
||||
|
6
client/src/app/shared/models/base/collection.ts
Normal file
6
client/src/app/shared/models/base/collection.ts
Normal file
@ -0,0 +1,6 @@
|
||||
/**
|
||||
* Every implementing object should have a collection string.
|
||||
*/
|
||||
export interface Collection {
|
||||
readonly collectionString: string;
|
||||
}
|
@ -5,16 +5,13 @@ import { BaseModel } from '../base/base-model';
|
||||
* @ignore
|
||||
*/
|
||||
export class ChatMessage extends BaseModel<ChatMessage> {
|
||||
public static COLLECTIONSTRING = 'core/chat-message';
|
||||
public id: number;
|
||||
public message: string;
|
||||
public timestamp: string; // TODO: Type for timestamp
|
||||
public user_id: number;
|
||||
|
||||
public constructor(input?: any) {
|
||||
super('core/chat-message', 'Chatmessage', input);
|
||||
}
|
||||
|
||||
public getTitle(): string {
|
||||
return 'Chatmessage';
|
||||
super(ChatMessage.COLLECTIONSTRING, input);
|
||||
}
|
||||
}
|
||||
|
@ -5,15 +5,12 @@ import { BaseModel } from '../base/base-model';
|
||||
* @ignore
|
||||
*/
|
||||
export class Config extends BaseModel {
|
||||
public static COLLECTIONSTRING = 'core/config';
|
||||
public id: number;
|
||||
public key: string;
|
||||
public value: Object;
|
||||
|
||||
public constructor(input?: any) {
|
||||
super('core/config', 'Config', input);
|
||||
}
|
||||
|
||||
public getTitle(): string {
|
||||
return this.key;
|
||||
super(Config.COLLECTIONSTRING, input);
|
||||
}
|
||||
}
|
||||
|
@ -14,10 +14,6 @@ export class Countdown extends BaseModel<Countdown> {
|
||||
public running: boolean;
|
||||
|
||||
public constructor(input?: any) {
|
||||
super(Countdown.COLLECTIONSTRING, 'Countdown', input);
|
||||
}
|
||||
|
||||
public getTitle(): string {
|
||||
return this.description;
|
||||
super(Countdown.COLLECTIONSTRING, input);
|
||||
}
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ import { BaseModel } from '../base/base-model';
|
||||
* @ignore
|
||||
*/
|
||||
export class History extends BaseModel {
|
||||
public static COLLECTIONSTRING = 'core/history';
|
||||
public id: number;
|
||||
public element_id: string;
|
||||
public now: string;
|
||||
@ -29,10 +30,6 @@ export class History extends BaseModel {
|
||||
}
|
||||
|
||||
public constructor(input?: any) {
|
||||
super('core/history', 'History', input);
|
||||
}
|
||||
|
||||
public getTitle(): string {
|
||||
return this.element_id;
|
||||
super(History.COLLECTIONSTRING, input);
|
||||
}
|
||||
}
|
||||
|
@ -11,10 +11,6 @@ export class ProjectorMessage extends BaseModel<ProjectorMessage> {
|
||||
public message: string;
|
||||
|
||||
public constructor(input?: any) {
|
||||
super(ProjectorMessage.COLLECTIONSTRING, 'Message', input);
|
||||
}
|
||||
|
||||
public getTitle(): string {
|
||||
return 'Projectormessage';
|
||||
super(ProjectorMessage.COLLECTIONSTRING, input);
|
||||
}
|
||||
}
|
||||
|
@ -44,9 +44,14 @@ export interface ProjectionDefault {
|
||||
|
||||
/**
|
||||
* Representation of a projector. Has the nested property "projectiondefaults"
|
||||
*
|
||||
* TODO: Move all function to the viewprojector.
|
||||
*
|
||||
* @ignore
|
||||
*/
|
||||
export class Projector extends BaseModel<Projector> {
|
||||
public static COLLECTIONSTRING = 'core/projector';
|
||||
|
||||
public id: number;
|
||||
public elements: ProjectorElements;
|
||||
public elements_preview: ProjectorElements;
|
||||
@ -60,7 +65,7 @@ export class Projector extends BaseModel<Projector> {
|
||||
public projectiondefaults: ProjectionDefault[];
|
||||
|
||||
public constructor(input?: any) {
|
||||
super('core/projector', 'Projector', input);
|
||||
super(Projector.COLLECTIONSTRING, input);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -139,8 +144,4 @@ export class Projector extends BaseModel<Projector> {
|
||||
[[], []] as [T[], T[]]
|
||||
);
|
||||
}
|
||||
|
||||
public getTitle(): string {
|
||||
return this.name;
|
||||
}
|
||||
}
|
||||
|
@ -1,27 +1,16 @@
|
||||
import { BaseModel } from '../base/base-model';
|
||||
import { Searchable } from '../base/searchable';
|
||||
|
||||
/**
|
||||
* Representation of a tag.
|
||||
* @ignore
|
||||
*/
|
||||
export class Tag extends BaseModel<Tag> implements Searchable {
|
||||
export class Tag extends BaseModel<Tag> {
|
||||
public static COLLECTIONSTRING = 'core/tag';
|
||||
|
||||
public id: number;
|
||||
public name: string;
|
||||
|
||||
public constructor(input?: any) {
|
||||
super('core/tag', 'Tag', input);
|
||||
}
|
||||
|
||||
public getTitle(): string {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
public formatForSearch(): string[] {
|
||||
return [this.name];
|
||||
}
|
||||
|
||||
public getDetailStateURL(): string {
|
||||
return '/tags';
|
||||
super(Tag.COLLECTIONSTRING, input);
|
||||
}
|
||||
}
|
||||
|
@ -1,12 +1,11 @@
|
||||
import { File } from './file';
|
||||
import { Searchable } from '../base/searchable';
|
||||
import { BaseModel } from '../base/base-model';
|
||||
|
||||
/**
|
||||
* Representation of MediaFile. Has the nested property "File"
|
||||
* @ignore
|
||||
*/
|
||||
export class Mediafile extends BaseModel<Mediafile> implements Searchable {
|
||||
export class Mediafile extends BaseModel<Mediafile> {
|
||||
public id: number;
|
||||
public title: string;
|
||||
public mediafile: File;
|
||||
@ -17,7 +16,7 @@ export class Mediafile extends BaseModel<Mediafile> implements Searchable {
|
||||
public timestamp: string;
|
||||
|
||||
public constructor(input?: any) {
|
||||
super('mediafiles/mediafile', 'Mediafile', input);
|
||||
super('mediafiles/mediafile', input);
|
||||
}
|
||||
|
||||
public deserialize(input: any): void {
|
||||
@ -30,19 +29,7 @@ export class Mediafile extends BaseModel<Mediafile> implements Searchable {
|
||||
*
|
||||
* @returns the download URL for the specific file as string
|
||||
*/
|
||||
public getDownloadUrl(): string {
|
||||
public get downloadUrl(): string {
|
||||
return `${this.media_url_prefix}${this.mediafile.name}`;
|
||||
}
|
||||
|
||||
public getTitle(): string {
|
||||
return this.title;
|
||||
}
|
||||
|
||||
public formatForSearch(): string[] {
|
||||
return [this.title, this.mediafile.name];
|
||||
}
|
||||
|
||||
public getDetailStateURL(): string {
|
||||
return this.getDownloadUrl();
|
||||
}
|
||||
}
|
||||
|
@ -1,52 +1,17 @@
|
||||
import { BaseModel } from '../base/base-model';
|
||||
import { Searchable } from '../base/searchable';
|
||||
import { SearchRepresentation } from 'app/core/ui-services/search.service';
|
||||
|
||||
/**
|
||||
* Representation of a motion category. Has the nested property "File"
|
||||
* @ignore
|
||||
*/
|
||||
export class Category extends BaseModel<Category> implements Searchable {
|
||||
export class Category extends BaseModel<Category> {
|
||||
public static COLLECTIONSTRING = 'motions/category';
|
||||
|
||||
public id: number;
|
||||
public name: string;
|
||||
public prefix: string;
|
||||
|
||||
public constructor(input?: any) {
|
||||
super('motions/category', 'Category', input);
|
||||
}
|
||||
|
||||
public getTitle(): string {
|
||||
return this.prefix ? this.prefix + ' - ' + this.name : this.name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the verbose name of this model.
|
||||
*
|
||||
* @override
|
||||
* @param plural If the name should be plural
|
||||
* @param The verbose name
|
||||
*/
|
||||
public getVerboseName(plural: boolean = false): string {
|
||||
if (plural) {
|
||||
return 'Categories';
|
||||
} else {
|
||||
return this._verboseName;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats the category for search
|
||||
*
|
||||
* @override
|
||||
*/
|
||||
public formatForSearch(): SearchRepresentation {
|
||||
return [this.getTitle()];
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO: add an id as url parameter, so the category auto-opens.
|
||||
*/
|
||||
public getDetailStateURL(): string {
|
||||
return '/motions/category';
|
||||
super(Category.COLLECTIONSTRING, input);
|
||||
}
|
||||
}
|
||||
|
@ -1,38 +1,17 @@
|
||||
import { AgendaBaseModel } from '../base/agenda-base-model';
|
||||
import { SearchRepresentation } from 'app/core/ui-services/search.service';
|
||||
import { BaseModel } from '../base/base-model';
|
||||
|
||||
/**
|
||||
* Representation of a motion block.
|
||||
* @ignore
|
||||
*/
|
||||
export class MotionBlock extends AgendaBaseModel {
|
||||
export class MotionBlock extends BaseModel {
|
||||
public static COLLECTIONSTRING = 'motions/motion-block';
|
||||
|
||||
public id: number;
|
||||
public title: string;
|
||||
public agenda_item_id: number;
|
||||
|
||||
public constructor(input?: any) {
|
||||
super('motions/motion-block', 'Motion block', input);
|
||||
}
|
||||
|
||||
public getTitle(): string {
|
||||
return this.title;
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats the category for search
|
||||
*
|
||||
* @override
|
||||
*/
|
||||
public formatForSearch(): SearchRepresentation {
|
||||
return [this.title];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the URL to the motion block
|
||||
*
|
||||
* @returns the URL as string
|
||||
*/
|
||||
public getDetailStateURL(): string {
|
||||
return `/motions/blocks/${this.id}`;
|
||||
super(MotionBlock.COLLECTIONSTRING, input);
|
||||
}
|
||||
}
|
||||
|
@ -4,7 +4,9 @@ import { BaseModel } from '../base/base-model';
|
||||
* Representation of a motion change recommendation.
|
||||
* @ignore
|
||||
*/
|
||||
export class MotionChangeReco extends BaseModel<MotionChangeReco> {
|
||||
export class MotionChangeRecommendation extends BaseModel<MotionChangeRecommendation> {
|
||||
public static COLLECTIONSTRING = 'motions/motion-change-recommendation';
|
||||
|
||||
public id: number;
|
||||
public motion_id: number;
|
||||
public rejected: boolean;
|
||||
@ -17,10 +19,6 @@ export class MotionChangeReco extends BaseModel<MotionChangeReco> {
|
||||
public creation_time: string;
|
||||
|
||||
public constructor(input?: any) {
|
||||
super('motions/motion-change-recommendation', 'Change recommendation', input);
|
||||
}
|
||||
|
||||
public getTitle(): string {
|
||||
return 'Changerecommendation';
|
||||
super(MotionChangeRecommendation.COLLECTIONSTRING, input);
|
||||
}
|
||||
}
|
||||
|
@ -1,20 +1,18 @@
|
||||
import { BaseModel } from '../base/base-model';
|
||||
|
||||
/**
|
||||
* Representation of a motion category. Has the nested property "File"
|
||||
* Representation of a comment section.
|
||||
* @ignore
|
||||
*/
|
||||
export class MotionCommentSection extends BaseModel<MotionCommentSection> {
|
||||
public static COLLECTIONSTRING = 'motions/motion-comment-section';
|
||||
|
||||
public id: number;
|
||||
public name: string;
|
||||
public read_groups_id: number[];
|
||||
public write_groups_id: number[];
|
||||
|
||||
public constructor(input?: any) {
|
||||
super('motions/motion-comment-section', 'Comment section', input);
|
||||
}
|
||||
|
||||
public getTitle(): string {
|
||||
return this.name;
|
||||
super(MotionCommentSection.COLLECTIONSTRING, input);
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,7 @@
|
||||
import { MotionSubmitter } from './motion-submitter';
|
||||
import { MotionComment } from './motion-comment';
|
||||
import { AgendaBaseModel } from '../base/agenda-base-model';
|
||||
import { SearchRepresentation } from 'app/core/ui-services/search.service';
|
||||
import { MotionPoll } from './motion-poll';
|
||||
import { BaseModel } from '../base/base-model';
|
||||
|
||||
/**
|
||||
* Representation of Motion.
|
||||
@ -11,7 +10,7 @@ import { MotionPoll } from './motion-poll';
|
||||
*
|
||||
* @ignore
|
||||
*/
|
||||
export class Motion extends AgendaBaseModel {
|
||||
export class Motion extends BaseModel {
|
||||
public static COLLECTIONSTRING = 'motions/motion';
|
||||
|
||||
public id: number;
|
||||
@ -45,7 +44,7 @@ export class Motion extends AgendaBaseModel {
|
||||
public last_modified: string;
|
||||
|
||||
public constructor(input?: any) {
|
||||
super(Motion.COLLECTIONSTRING, 'Motion', input);
|
||||
super(Motion.COLLECTIONSTRING, input);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -59,49 +58,6 @@ export class Motion extends AgendaBaseModel {
|
||||
.map((submitter: MotionSubmitter) => submitter.user_id);
|
||||
}
|
||||
|
||||
public getTitle(): string {
|
||||
if (this.identifier) {
|
||||
return this.identifier + ': ' + this.title;
|
||||
} else {
|
||||
return this.title;
|
||||
}
|
||||
}
|
||||
|
||||
public getAgendaTitle(): string {
|
||||
// if the identifier is set, the title will be 'Motion <identifier>'.
|
||||
if (this.identifier) {
|
||||
return 'Motion ' + this.identifier;
|
||||
} else {
|
||||
return this.getTitle();
|
||||
}
|
||||
}
|
||||
|
||||
public getAgendaTitleWithType(): string {
|
||||
// Append the verbose name only, if not the special format 'Motion <identifier>' is used.
|
||||
if (this.identifier) {
|
||||
return 'Motion ' + this.identifier;
|
||||
} else {
|
||||
return this.getTitle() + ' (' + this.getVerboseName() + ')';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats the category for search
|
||||
*
|
||||
* @override
|
||||
*/
|
||||
public formatForSearch(): SearchRepresentation {
|
||||
let searchValues = [this.title, this.text, this.reason];
|
||||
if (this.amendment_paragraphs) {
|
||||
searchValues = searchValues.concat(this.amendment_paragraphs.filter(x => !!x));
|
||||
}
|
||||
return searchValues;
|
||||
}
|
||||
|
||||
public getDetailStateURL(): string {
|
||||
return `/motions/${this.id}`;
|
||||
}
|
||||
|
||||
public deserialize(input: any): void {
|
||||
Object.assign(this, input);
|
||||
|
||||
|
@ -1,38 +1,18 @@
|
||||
import { BaseModel } from '../base/base-model';
|
||||
import { Searchable } from '../base/searchable';
|
||||
import { SearchRepresentation } from 'app/core/ui-services/search.service';
|
||||
|
||||
/**
|
||||
* Representation of a statute paragraph.
|
||||
* @ignore
|
||||
*/
|
||||
export class StatuteParagraph extends BaseModel<StatuteParagraph> implements Searchable {
|
||||
export class StatuteParagraph extends BaseModel<StatuteParagraph> {
|
||||
public static COLLECTIONSTRING = 'motions/statute-paragraph';
|
||||
|
||||
public id: number;
|
||||
public title: string;
|
||||
public text: string;
|
||||
public weight: number;
|
||||
|
||||
public constructor(input?: any) {
|
||||
super('motions/statute-paragraph', 'Statute paragraph', input);
|
||||
}
|
||||
|
||||
public getTitle(): string {
|
||||
return this.title;
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats the category for search
|
||||
*
|
||||
* @override
|
||||
*/
|
||||
public formatForSearch(): SearchRepresentation {
|
||||
return [this.title, this.text];
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO: add an id as url parameter, so the statute paragraph auto-opens.
|
||||
*/
|
||||
public getDetailStateURL(): string {
|
||||
return '/motions/statute-paragraphs';
|
||||
super(StatuteParagraph.COLLECTIONSTRING, input);
|
||||
}
|
||||
}
|
||||
|
@ -6,17 +6,15 @@ import { WorkflowState } from './workflow-state';
|
||||
* @ignore
|
||||
*/
|
||||
export class Workflow extends BaseModel<Workflow> {
|
||||
public static COLLECTIONSTRING = 'motions/workflow';
|
||||
|
||||
public id: number;
|
||||
public name: string;
|
||||
public states: WorkflowState[];
|
||||
public first_state_id: number;
|
||||
|
||||
public get firstState(): WorkflowState {
|
||||
return this.getStateById(this.first_state_id);
|
||||
}
|
||||
|
||||
public constructor(input?: any) {
|
||||
super('motions/workflow', 'Workflow', input);
|
||||
super(Workflow.COLLECTIONSTRING, input);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -38,10 +36,6 @@ export class Workflow extends BaseModel<Workflow> {
|
||||
});
|
||||
}
|
||||
|
||||
public getStateById(id: number): WorkflowState {
|
||||
return this.states.find(state => state.id === id);
|
||||
}
|
||||
|
||||
public deserialize(input: any): void {
|
||||
Object.assign(this, input);
|
||||
if (input.states instanceof Array) {
|
||||
@ -51,8 +45,4 @@ export class Workflow extends BaseModel<Workflow> {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public getTitle(): string {
|
||||
return this.name;
|
||||
}
|
||||
}
|
||||
|
@ -1,11 +1,12 @@
|
||||
import { AgendaBaseModel } from '../base/agenda-base-model';
|
||||
import { SearchRepresentation } from 'app/core/ui-services/search.service';
|
||||
import { BaseModel } from '../base/base-model';
|
||||
|
||||
/**
|
||||
* Representation of a topic.
|
||||
* @ignore
|
||||
*/
|
||||
export class Topic extends AgendaBaseModel {
|
||||
export class Topic extends BaseModel<Topic> {
|
||||
public static COLLECTIONSTRING = 'topics/topic';
|
||||
|
||||
public id: number;
|
||||
public title: string;
|
||||
public text: string;
|
||||
@ -13,36 +14,6 @@ export class Topic extends AgendaBaseModel {
|
||||
public agenda_item_id: number;
|
||||
|
||||
public constructor(input?: any) {
|
||||
super('topics/topic', 'Topic', input);
|
||||
}
|
||||
|
||||
public getTitle(): string {
|
||||
return this.title;
|
||||
}
|
||||
|
||||
public getAgendaTitleWithType(): string {
|
||||
// Do not append ' (Topic)' to the title.
|
||||
return this.getAgendaTitle();
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats the category for search
|
||||
*
|
||||
* @override
|
||||
*/
|
||||
public formatForSearch(): SearchRepresentation {
|
||||
return [this.title, this.text];
|
||||
}
|
||||
|
||||
public getDetailStateURL(): string {
|
||||
return `/agenda/topics/${this.id}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the text to be inserted in csv exports
|
||||
* @override
|
||||
*/
|
||||
public getCSVExportText(): string {
|
||||
return this.text;
|
||||
super(Topic.COLLECTIONSTRING, input);
|
||||
}
|
||||
}
|
||||
|
@ -5,12 +5,13 @@ import { BaseModel } from '../base/base-model';
|
||||
* @ignore
|
||||
*/
|
||||
export class Group extends BaseModel<Group> {
|
||||
public static COLLECTIONSTRING = 'users/group';
|
||||
public id: number;
|
||||
public name: string;
|
||||
public permissions: string[];
|
||||
|
||||
public constructor(input?: any) {
|
||||
super('users/group', 'Group', input);
|
||||
super(Group.COLLECTIONSTRING, input);
|
||||
if (!input) {
|
||||
// permissions are required for new groups
|
||||
this.permissions = [];
|
||||
|
@ -49,12 +49,14 @@ export interface PersonalNoteObject {
|
||||
* @ignore
|
||||
*/
|
||||
export class PersonalNote extends BaseModel<PersonalNote> implements PersonalNoteObject {
|
||||
public static COLLECTIONSTRING = 'users/personal-note';
|
||||
|
||||
public id: number;
|
||||
public user_id: number;
|
||||
public notes: PersonalNotesFormat;
|
||||
|
||||
public constructor(input: any) {
|
||||
super('users/personal-note', 'Personal note', input);
|
||||
super(PersonalNote.COLLECTIONSTRING, input);
|
||||
}
|
||||
|
||||
public getTitle(): string {
|
||||
|
@ -1,7 +1,4 @@
|
||||
import { Searchable } from '../base/searchable';
|
||||
import { SearchRepresentation } from 'app/core/ui-services/search.service';
|
||||
import { BaseModel } from '../base/base-model';
|
||||
import { DetailNavigable } from '../base/detail-navigable';
|
||||
|
||||
/**
|
||||
* Iterable pre selection of genders (sexes)
|
||||
@ -12,7 +9,7 @@ export const genders = ['Female', 'Male', 'Diverse'];
|
||||
* Representation of a user in contrast to the operator.
|
||||
* @ignore
|
||||
*/
|
||||
export class User extends BaseModel<User> implements Searchable, DetailNavigable {
|
||||
export class User extends BaseModel<User> {
|
||||
public static COLLECTIONSTRING = 'users/user';
|
||||
|
||||
public id: number;
|
||||
@ -34,89 +31,10 @@ export class User extends BaseModel<User> implements Searchable, DetailNavigable
|
||||
public default_password: string;
|
||||
|
||||
public constructor(input?: any) {
|
||||
super(User.COLLECTIONSTRING, 'Participant', input);
|
||||
}
|
||||
|
||||
public get full_name(): string {
|
||||
let name = this.short_name;
|
||||
const additions: string[] = [];
|
||||
|
||||
// addition: add number and structure level
|
||||
const structure_level = this.structure_level ? this.structure_level.trim() : '';
|
||||
if (structure_level) {
|
||||
additions.push(structure_level);
|
||||
}
|
||||
|
||||
const number = this.number ? this.number.trim() : null;
|
||||
if (number) {
|
||||
// TODO Translate
|
||||
additions.push('No. ' + number);
|
||||
}
|
||||
|
||||
if (additions.length > 0) {
|
||||
name += ' (' + additions.join(' · ') + ')';
|
||||
}
|
||||
return name.trim();
|
||||
super(User.COLLECTIONSTRING, input);
|
||||
}
|
||||
|
||||
public containsGroupId(id: number): boolean {
|
||||
return this.groups_id.some(groupId => groupId === id);
|
||||
}
|
||||
|
||||
// TODO read config values for "users_sort_by"
|
||||
|
||||
/**
|
||||
* Getter for the short name (Title, given name, surname)
|
||||
*
|
||||
* @returns a non-empty string
|
||||
*/
|
||||
public get short_name(): string {
|
||||
const title = this.title ? this.title.trim() : '';
|
||||
const firstName = this.first_name ? this.first_name.trim() : '';
|
||||
const lastName = this.last_name ? this.last_name.trim() : '';
|
||||
|
||||
// TODO need DS adjustment first first
|
||||
// if (this.DS.getConfig('users_sort_by').value === 'last_name') {
|
||||
// if (lastName && firstName) {
|
||||
// shortName += `${lastName}, ${firstName}`;
|
||||
// } else {
|
||||
// shortName += lastName || firstName;
|
||||
// }
|
||||
// }
|
||||
|
||||
let shortName = `${firstName} ${lastName}`;
|
||||
|
||||
if (shortName.length <= 1) {
|
||||
// We have at least one space from the concatination of
|
||||
// first- and lastname.
|
||||
shortName = this.username;
|
||||
}
|
||||
|
||||
if (title) {
|
||||
shortName = `${title} ${shortName}`;
|
||||
}
|
||||
|
||||
return shortName;
|
||||
}
|
||||
|
||||
public getTitle(): string {
|
||||
return this.full_name;
|
||||
}
|
||||
|
||||
public getListViewTitle(): string {
|
||||
return this.short_name;
|
||||
}
|
||||
|
||||
public getDetailStateURL(): string {
|
||||
return `/users/${this.id}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats the category for search
|
||||
*
|
||||
* @override
|
||||
*/
|
||||
public formatForSearch(): SearchRepresentation {
|
||||
return [this.title, this.first_name, this.last_name, this.structure_level, this.number];
|
||||
}
|
||||
}
|
||||
|
@ -1,14 +1,22 @@
|
||||
import { AppConfig } from '../../core/app-config';
|
||||
import { Item } from '../../shared/models/agenda/item';
|
||||
import { Topic } from '../../shared/models/topics/topic';
|
||||
import { AgendaRepositoryService } from 'app/core/repositories/agenda/agenda-repository.service';
|
||||
import { ItemRepositoryService } from 'app/core/repositories/agenda/item-repository.service';
|
||||
import { TopicRepositoryService } from 'app/core/repositories/agenda/topic-repository.service';
|
||||
import { ViewTopic } from './models/view-topic';
|
||||
import { ViewItem } from './models/view-item';
|
||||
|
||||
export const AgendaAppConfig: AppConfig = {
|
||||
name: 'agenda',
|
||||
models: [
|
||||
{ collectionString: 'agenda/item', model: Item, repository: AgendaRepositoryService },
|
||||
{ collectionString: 'topics/topic', model: Topic, searchOrder: 1, repository: TopicRepositoryService }
|
||||
{ collectionString: 'agenda/item', model: Item, viewModel: ViewItem, repository: ItemRepositoryService },
|
||||
{
|
||||
collectionString: 'topics/topic',
|
||||
model: Topic,
|
||||
viewModel: ViewTopic,
|
||||
searchOrder: 1,
|
||||
repository: TopicRepositoryService
|
||||
}
|
||||
],
|
||||
mainMenuEntries: [
|
||||
{
|
||||
|
@ -5,7 +5,7 @@ import { Title } from '@angular/platform-browser';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
|
||||
import { AgendaFilterListService } from '../../services/agenda-filter-list.service';
|
||||
import { AgendaRepositoryService } from 'app/core/repositories/agenda/agenda-repository.service';
|
||||
import { ItemRepositoryService } from 'app/core/repositories/agenda/item-repository.service';
|
||||
import { ListViewBaseComponent } from 'app/site/base/list-view-base';
|
||||
import { PromptService } from 'app/core/ui-services/prompt.service';
|
||||
import { ViewItem } from '../../models/view-item';
|
||||
@ -63,7 +63,7 @@ export class AgendaListComponent extends ListViewBaseComponent<ViewItem> impleme
|
||||
matSnackBar: MatSnackBar,
|
||||
private route: ActivatedRoute,
|
||||
private router: Router,
|
||||
private repo: AgendaRepositoryService,
|
||||
private repo: ItemRepositoryService,
|
||||
private promptService: PromptService,
|
||||
private dialog: MatDialog,
|
||||
private config: ConfigService,
|
||||
|
@ -5,7 +5,7 @@ import { MatSnackBar } from '@angular/material';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { Observable } from 'rxjs';
|
||||
|
||||
import { AgendaRepositoryService } from 'app/core/repositories/agenda/agenda-repository.service';
|
||||
import { ItemRepositoryService } from 'app/core/repositories/agenda/item-repository.service';
|
||||
import { BaseViewComponent } from '../../../base/base-view';
|
||||
import { OSTreeSortEvent } from 'app/shared/components/sorting-tree/sorting-tree.component';
|
||||
import { ViewItem } from '../../models/view-item';
|
||||
@ -39,7 +39,7 @@ export class AgendaSortComponent extends BaseViewComponent {
|
||||
title: Title,
|
||||
translate: TranslateService,
|
||||
matSnackBar: MatSnackBar,
|
||||
private agendaRepo: AgendaRepositoryService
|
||||
private agendaRepo: ItemRepositoryService
|
||||
) {
|
||||
super(title, translate, matSnackBar);
|
||||
this.itemsObservable = this.agendaRepo.getViewModelListObservable();
|
||||
|
@ -1,25 +1,25 @@
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { BehaviorSubject, Subscription } from 'rxjs';
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { FormGroup, FormControl } from '@angular/forms';
|
||||
import { MatSnackBar, MatSelectChange } from '@angular/material';
|
||||
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { BehaviorSubject, Subscription } from 'rxjs';
|
||||
|
||||
import { AgendaRepositoryService } from 'app/core/repositories/agenda/agenda-repository.service';
|
||||
import { ItemRepositoryService } from 'app/core/repositories/agenda/item-repository.service';
|
||||
import { BaseViewComponent } from 'app/site/base/base-view';
|
||||
import { CurrentListOfSpeakersSlideService } from 'app/site/projector/services/current-list-of-of-speakers-slide.service';
|
||||
import { DataStoreService } from 'app/core/core-services/data-store.service';
|
||||
import { DurationService } from 'app/core/ui-services/duration.service';
|
||||
import { OperatorService } from 'app/core/core-services/operator.service';
|
||||
import { ProjectorRepositoryService } from 'app/core/repositories/projector/projector-repository.service';
|
||||
import { PromptService } from 'app/core/ui-services/prompt.service';
|
||||
import { SpeakerState } from 'app/shared/models/agenda/speaker';
|
||||
import { Title } from '@angular/platform-browser';
|
||||
import { User } from 'app/shared/models/users/user';
|
||||
import { ViewItem } from '../../models/view-item';
|
||||
import { ViewSpeaker } from '../../models/view-speaker';
|
||||
import { ViewProjector } from 'app/site/projector/models/view-projector';
|
||||
import { ViewUser } from 'app/site/users/models/view-user';
|
||||
import { UserRepositoryService } from 'app/core/repositories/users/user-repository.service';
|
||||
import { DurationService } from 'app/core/ui-services/duration.service';
|
||||
|
||||
/**
|
||||
* The list of speakers for agenda items.
|
||||
@ -68,7 +68,7 @@ export class SpeakerListComponent extends BaseViewComponent implements OnInit {
|
||||
/**
|
||||
* Hold the users
|
||||
*/
|
||||
public users: BehaviorSubject<User[]>;
|
||||
public users: BehaviorSubject<ViewUser[]>;
|
||||
|
||||
/**
|
||||
* Required for the user search selector
|
||||
@ -113,12 +113,12 @@ export class SpeakerListComponent extends BaseViewComponent implements OnInit {
|
||||
snackBar: MatSnackBar,
|
||||
projectorRepo: ProjectorRepositoryService,
|
||||
private route: ActivatedRoute,
|
||||
private DS: DataStoreService,
|
||||
private itemRepo: AgendaRepositoryService,
|
||||
private itemRepo: ItemRepositoryService,
|
||||
private op: OperatorService,
|
||||
private promptService: PromptService,
|
||||
private currentListOfSpeakersService: CurrentListOfSpeakersSlideService,
|
||||
private durationService: DurationService
|
||||
private durationService: DurationService,
|
||||
private userRepository: UserRepositoryService
|
||||
) {
|
||||
super(title, translate, snackBar);
|
||||
this.isCurrentListOfSpeakers();
|
||||
@ -143,12 +143,8 @@ export class SpeakerListComponent extends BaseViewComponent implements OnInit {
|
||||
*/
|
||||
public ngOnInit(): void {
|
||||
// load and observe users
|
||||
this.users = new BehaviorSubject(this.DS.getAll(User));
|
||||
this.DS.changeObservable.subscribe(model => {
|
||||
if (model instanceof User) {
|
||||
this.users.next(this.DS.getAll(User));
|
||||
}
|
||||
});
|
||||
this.users = new BehaviorSubject(this.userRepository.getViewModelList());
|
||||
this.userRepository.getViewModelListObservable().subscribe(users => this.users.next(users));
|
||||
|
||||
// detect changes in the form
|
||||
this.addSpeakerForm.valueChanges.subscribe(formResult => {
|
||||
@ -190,10 +186,10 @@ export class SpeakerListComponent extends BaseViewComponent implements OnInit {
|
||||
}
|
||||
|
||||
this.projectorSubscription = this.currentListOfSpeakersService
|
||||
.getAgendaItemIdObservable(projector)
|
||||
.subscribe(agendaId => {
|
||||
if (agendaId) {
|
||||
this.setSpeakerList(agendaId);
|
||||
.getAgendaItemObservable(projector)
|
||||
.subscribe(item => {
|
||||
if (item) {
|
||||
this.setSpeakerList(item.id);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -24,90 +24,89 @@
|
||||
</div>
|
||||
</os-head-bar>
|
||||
|
||||
<mat-card *ngIf="topic || editTopic"
|
||||
[ngClass]="editTopic ? 'os-form-card' : 'os-card'"
|
||||
class="on-transition-fade">
|
||||
<div *ngIf="!editTopic">
|
||||
<h1>{{ topic.title }}</h1>
|
||||
</div>
|
||||
<div>
|
||||
<span *ngIf="!editTopic">
|
||||
<!-- Render topic text as HTML -->
|
||||
<div [innerHTML]="topic.text"></div>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<mat-card *ngIf="topic || editTopic"
|
||||
[ngClass]="editTopic ? 'os-form-card' : 'os-card'"
|
||||
class="on-transition-fade">
|
||||
<div *ngIf="!editTopic">
|
||||
<h1>{{ topic.title }}</h1>
|
||||
</div>
|
||||
<div *ngIf="topic.hasAttachments() && !editTopic">
|
||||
<h3>
|
||||
<span translate>Attachments</span>:
|
||||
<mat-list dense>
|
||||
<mat-list-item *ngFor="let file of topic.attachments">
|
||||
<a [routerLink]="file.downloadUrl" target="_blank">{{ file.title }}</a>
|
||||
</mat-list-item>
|
||||
</mat-list>
|
||||
<!-- TODO: Mediafiles and attachments are not fully implemented -->
|
||||
</h3>
|
||||
</div>
|
||||
|
||||
<form *ngIf="editTopic" [formGroup]="topicForm" (keydown)="onKeyDown($event)" (ngSubmit)="saveTopic()">
|
||||
<div>
|
||||
<span *ngIf="!editTopic">
|
||||
<!-- Render topic text as HTML -->
|
||||
<div [innerHTML]="topic.text"></div>
|
||||
</span>
|
||||
<mat-form-field>
|
||||
<input
|
||||
type="text"
|
||||
matInput
|
||||
osAutofocus
|
||||
required
|
||||
formControlName="title"
|
||||
placeholder="{{ 'Title' | translate }}"
|
||||
/>
|
||||
<mat-error *ngIf="topicForm.invalid" translate>A name is required</mat-error>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
|
||||
<div *ngIf="topic.hasAttachments() && !editTopic">
|
||||
<h3>
|
||||
<span translate>Attachments</span>:
|
||||
<mat-list dense>
|
||||
<mat-list-item *ngFor="let file of topic.attachments">
|
||||
<a [routerLink]="file.getDownloadUrl()" target="_blank">{{ file.title }}</a>
|
||||
</mat-list-item>
|
||||
</mat-list>
|
||||
<!-- TODO: Mediafiles and attachments are not fully implemented -->
|
||||
</h3>
|
||||
<!-- The editor -->
|
||||
<div class="spacer-bottom-20">
|
||||
<h4 translate>Text</h4>
|
||||
<editor formControlName="text" [init]="tinyMceSettings"></editor>
|
||||
</div>
|
||||
|
||||
<form *ngIf="editTopic" [formGroup]="topicForm" (keydown)="onKeyDown($event)" (ngSubmit)="saveTopic()">
|
||||
<!-- Attachments -->
|
||||
<os-search-value-selector
|
||||
ngDefaultControl
|
||||
[form]="topicForm"
|
||||
[formControl]="topicForm.get('attachments_id')"
|
||||
[multiple]="true"
|
||||
listname="{{ 'Attachments' | translate }}"
|
||||
[InputListValues]="mediafilesObserver"
|
||||
></os-search-value-selector>
|
||||
|
||||
<div *ngIf="newTopic">
|
||||
<!-- Visibility -->
|
||||
<div>
|
||||
<mat-form-field>
|
||||
<input
|
||||
type="text"
|
||||
matInput
|
||||
osAutofocus
|
||||
required
|
||||
formControlName="title"
|
||||
placeholder="{{ 'Title' | translate }}"
|
||||
/>
|
||||
<mat-error *ngIf="topicForm.invalid" translate>A name is required</mat-error>
|
||||
<mat-select formControlName="agenda_type" placeholder="{{ 'Agenda visibility' | translate }}">
|
||||
<mat-option *ngFor="let type of itemVisibility" [value]="type.key">
|
||||
<span>{{ type.name | translate }}</span>
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
|
||||
<!-- The editor -->
|
||||
<div class="spacer-bottom-20">
|
||||
<h4 translate>Text</h4>
|
||||
<editor formControlName="text" [init]="tinyMceSettings"></editor>
|
||||
<!-- Parent item -->
|
||||
<div>
|
||||
<os-search-value-selector
|
||||
ngDefaultControl
|
||||
[form]="topicForm"
|
||||
[formControl]="topicForm.get('agenda_parent_id')"
|
||||
[multiple]="false"
|
||||
[includeNone]="true"
|
||||
listname="{{ 'Parent item' | translate }}"
|
||||
[InputListValues]="itemObserver"
|
||||
></os-search-value-selector>
|
||||
</div>
|
||||
|
||||
<!-- Attachments -->
|
||||
<os-search-value-selector
|
||||
ngDefaultControl
|
||||
[form]="topicForm"
|
||||
[formControl]="topicForm.get('attachments_id')"
|
||||
[multiple]="true"
|
||||
listname="{{ 'Attachments' | translate }}"
|
||||
[InputListValues]="mediafilesObserver"
|
||||
></os-search-value-selector>
|
||||
|
||||
<div *ngIf="newTopic">
|
||||
<!-- Visibility -->
|
||||
<div>
|
||||
<mat-form-field>
|
||||
<mat-select formControlName="agenda_type" placeholder="{{ 'Agenda visibility' | translate }}">
|
||||
<mat-option *ngFor="let type of itemVisibility" [value]="type.key">
|
||||
<span>{{ type.name | translate }}</span>
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
|
||||
<!-- Parent item -->
|
||||
<div>
|
||||
<os-search-value-selector
|
||||
ngDefaultControl
|
||||
[form]="topicForm"
|
||||
[formControl]="topicForm.get('agenda_parent_id')"
|
||||
[multiple]="false"
|
||||
[includeNone]="true"
|
||||
listname="{{ 'Parent item' | translate }}"
|
||||
[InputListValues]="agendaItemObserver"
|
||||
></os-search-value-selector>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</mat-card>
|
||||
</div>
|
||||
</form>
|
||||
</mat-card>
|
||||
|
||||
<mat-menu #topicExtraMenu="matMenu">
|
||||
<button mat-menu-item *ngIf="topic" [routerLink]="getSpeakerLink()">
|
||||
|
@ -12,10 +12,13 @@ import { TopicRepositoryService } from 'app/core/repositories/agenda/topic-repos
|
||||
import { ViewTopic } from '../../models/view-topic';
|
||||
import { OperatorService } from 'app/core/core-services/operator.service';
|
||||
import { BehaviorSubject } from 'rxjs';
|
||||
import { DataStoreService } from 'app/core/core-services/data-store.service';
|
||||
import { Mediafile } from 'app/shared/models/mediafiles/mediafile';
|
||||
import { Item, itemVisibilityChoices } from 'app/shared/models/agenda/item';
|
||||
import { itemVisibilityChoices } from 'app/shared/models/agenda/item';
|
||||
import { CreateTopic } from '../../models/create-topic';
|
||||
import { Topic } from 'app/shared/models/topics/topic';
|
||||
import { ViewMediafile } from 'app/site/mediafiles/models/view-mediafile';
|
||||
import { ViewItem } from '../../models/view-item';
|
||||
import { MediafileRepositoryService } from 'app/core/repositories/mediafiles/mediafile-repository.service';
|
||||
import { ItemRepositoryService } from 'app/core/repositories/agenda/item-repository.service';
|
||||
|
||||
/**
|
||||
* Detail page for topics.
|
||||
@ -49,12 +52,12 @@ export class TopicDetailComponent extends BaseViewComponent {
|
||||
/**
|
||||
* Subject for mediafiles
|
||||
*/
|
||||
public mediafilesObserver: BehaviorSubject<Mediafile[]>;
|
||||
public mediafilesObserver: BehaviorSubject<ViewMediafile[]>;
|
||||
|
||||
/**
|
||||
* Subject for agenda items
|
||||
*/
|
||||
public agendaItemObserver: BehaviorSubject<Item[]>;
|
||||
public itemObserver: BehaviorSubject<ViewItem[]>;
|
||||
|
||||
/**
|
||||
* Determine visibility states for the agenda that will be created implicitly
|
||||
@ -85,22 +88,20 @@ export class TopicDetailComponent extends BaseViewComponent {
|
||||
private repo: TopicRepositoryService,
|
||||
private promptService: PromptService,
|
||||
private operator: OperatorService,
|
||||
private DS: DataStoreService
|
||||
private mediafileRepo: MediafileRepositoryService,
|
||||
private itemRepo: ItemRepositoryService
|
||||
) {
|
||||
super(title, translate, matSnackBar);
|
||||
this.getTopicByUrl();
|
||||
this.createForm();
|
||||
|
||||
this.mediafilesObserver = new BehaviorSubject(this.DS.getAll(Mediafile));
|
||||
this.agendaItemObserver = new BehaviorSubject(this.DS.getAll(Item));
|
||||
this.mediafilesObserver = new BehaviorSubject(this.mediafileRepo.getViewModelList());
|
||||
this.itemObserver = new BehaviorSubject(this.itemRepo.getViewModelList());
|
||||
|
||||
this.DS.changeObservable.subscribe(newModel => {
|
||||
if (newModel instanceof Item) {
|
||||
this.agendaItemObserver.next(DS.getAll(Item));
|
||||
} else if (newModel instanceof Mediafile) {
|
||||
this.mediafilesObserver.next(DS.getAll(Mediafile));
|
||||
}
|
||||
});
|
||||
this.mediafileRepo
|
||||
.getViewModelListObservable()
|
||||
.subscribe(mediafiles => this.mediafilesObserver.next(mediafiles));
|
||||
this.itemRepo.getViewModelListObservable().subscribe(items => this.itemObserver.next(items));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -170,7 +171,7 @@ export class TopicDetailComponent extends BaseViewComponent {
|
||||
// creates a new topic
|
||||
this.newTopic = true;
|
||||
this.editTopic = true;
|
||||
this.topic = new ViewTopic();
|
||||
this.topic = new ViewTopic(new Topic());
|
||||
} else {
|
||||
// load existing topic
|
||||
this.route.params.subscribe(params => {
|
||||
@ -205,7 +206,7 @@ export class TopicDetailComponent extends BaseViewComponent {
|
||||
*/
|
||||
public getSpeakerLink(): string {
|
||||
if (!this.newTopic && this.topic) {
|
||||
const item = this.repo.getAgendaItem(this.topic.topic);
|
||||
const item = this.topic.getAgendaItem();
|
||||
if (item) {
|
||||
return `/agenda/${item.id}/speakers`;
|
||||
}
|
||||
|
@ -1,11 +1,11 @@
|
||||
import { BaseViewModel } from '../../base/base-view-model';
|
||||
import { Item } from 'app/shared/models/agenda/item';
|
||||
import { AgendaBaseModel } from 'app/shared/models/base/agenda-base-model';
|
||||
import { Speaker } from 'app/shared/models/agenda/speaker';
|
||||
import { BaseAgendaViewModel } from 'app/site/base/base-agenda-view-model';
|
||||
|
||||
export class ViewItem extends BaseViewModel {
|
||||
private _item: Item;
|
||||
private _contentObject: AgendaBaseModel;
|
||||
private _contentObject: BaseAgendaViewModel;
|
||||
|
||||
/**
|
||||
* virtual weight defined by the order in the agenda tree, representing a shortcut to sorting by
|
||||
@ -24,7 +24,7 @@ export class ViewItem extends BaseViewModel {
|
||||
return this._item;
|
||||
}
|
||||
|
||||
public get contentObject(): AgendaBaseModel {
|
||||
public get contentObject(): BaseAgendaViewModel {
|
||||
return this._contentObject;
|
||||
}
|
||||
|
||||
@ -92,8 +92,8 @@ export class ViewItem extends BaseViewModel {
|
||||
return this.item ? this.item.parent_id : null;
|
||||
}
|
||||
|
||||
public constructor(item: Item, contentObject: AgendaBaseModel) {
|
||||
super();
|
||||
public constructor(item: Item, contentObject: BaseAgendaViewModel) {
|
||||
super('Item');
|
||||
this._item = item;
|
||||
this._contentObject = contentObject;
|
||||
}
|
||||
@ -113,7 +113,7 @@ export class ViewItem extends BaseViewModel {
|
||||
* @returns the agenda list title as string
|
||||
*/
|
||||
public getListTitle(): string {
|
||||
const contentObject: AgendaBaseModel = this.contentObject;
|
||||
const contentObject: BaseAgendaViewModel = this.contentObject;
|
||||
const numberPrefix = this.itemNumber ? `${this.itemNumber} · ` : '';
|
||||
|
||||
if (contentObject) {
|
||||
@ -123,9 +123,5 @@ export class ViewItem extends BaseViewModel {
|
||||
}
|
||||
}
|
||||
|
||||
public updateValues(update: Item): void {
|
||||
if (this.id === update.id) {
|
||||
this._item = update;
|
||||
}
|
||||
}
|
||||
public updateDependencies(update: BaseViewModel): void {}
|
||||
}
|
||||
|
@ -1,63 +1,62 @@
|
||||
import { BaseViewModel } from 'app/site/base/base-view-model';
|
||||
import { Speaker, SpeakerState } from 'app/shared/models/agenda/speaker';
|
||||
import { User } from 'app/shared/models/users/user';
|
||||
import { Selectable } from 'app/shared/components/selectable';
|
||||
import { ViewUser } from 'app/site/users/models/view-user';
|
||||
|
||||
/**
|
||||
* Provides "safe" access to a speaker with all it's components
|
||||
*/
|
||||
export class ViewSpeaker extends BaseViewModel implements Selectable {
|
||||
export class ViewSpeaker extends BaseViewModel {
|
||||
private _speaker: Speaker;
|
||||
private _user: User;
|
||||
private _user: ViewUser | null;
|
||||
|
||||
public get speaker(): Speaker {
|
||||
return this._speaker;
|
||||
}
|
||||
|
||||
public get user(): User {
|
||||
public get user(): ViewUser {
|
||||
return this._user;
|
||||
}
|
||||
|
||||
public get id(): number {
|
||||
return this.speaker ? this.speaker.id : null;
|
||||
return this.speaker.id;
|
||||
}
|
||||
|
||||
public get weight(): number {
|
||||
return this.speaker ? this.speaker.weight : null;
|
||||
return this.speaker.weight;
|
||||
}
|
||||
|
||||
public get marked(): boolean {
|
||||
return this.speaker ? this.speaker.marked : null;
|
||||
return this.speaker.marked;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns an ISO datetime string or null
|
||||
*/
|
||||
public get begin_time(): string {
|
||||
return this.speaker ? this.speaker.begin_time : null;
|
||||
return this.speaker.begin_time;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns an ISO datetime string or null
|
||||
*/
|
||||
public get end_time(): string {
|
||||
return this.speaker ? this.speaker.end_time : null;
|
||||
return this.speaker.end_time;
|
||||
}
|
||||
|
||||
public get state(): SpeakerState {
|
||||
return this.speaker ? this.speaker.state : null;
|
||||
return this.speaker.state;
|
||||
}
|
||||
|
||||
public get name(): string {
|
||||
return this.user.full_name;
|
||||
return this.user ? this.user.full_name : '';
|
||||
}
|
||||
|
||||
public get gender(): string {
|
||||
return this.user.gender || '';
|
||||
return this.user ? this.user.gender : '';
|
||||
}
|
||||
|
||||
public constructor(speaker?: Speaker, user?: User) {
|
||||
super();
|
||||
public constructor(speaker: Speaker, user?: ViewUser) {
|
||||
super('Speaker');
|
||||
this._speaker = speaker;
|
||||
this._user = user;
|
||||
}
|
||||
@ -70,9 +69,5 @@ export class ViewSpeaker extends BaseViewModel implements Selectable {
|
||||
* Speaker is not a base model,
|
||||
* @param update the incoming update
|
||||
*/
|
||||
public updateValues(update: Speaker): void {
|
||||
if (this.id === update.id) {
|
||||
this._speaker = update;
|
||||
}
|
||||
}
|
||||
public updateDependencies(update: BaseViewModel): void {}
|
||||
}
|
||||
|
@ -1,71 +1,112 @@
|
||||
import { BaseViewModel } from '../../base/base-view-model';
|
||||
import { Topic } from 'app/shared/models/topics/topic';
|
||||
import { Mediafile } from 'app/shared/models/mediafiles/mediafile';
|
||||
import { Item } from 'app/shared/models/agenda/item';
|
||||
import { BaseModel } from 'app/shared/models/base/base-model';
|
||||
import { BaseAgendaViewModel } from 'app/site/base/base-agenda-view-model';
|
||||
import { SearchRepresentation } from 'app/core/ui-services/search.service';
|
||||
import { ProjectorElementBuildDeskriptor } from 'app/site/base/projectable';
|
||||
import { ViewMediafile } from 'app/site/mediafiles/models/view-mediafile';
|
||||
import { ViewItem } from './view-item';
|
||||
import { BaseViewModel } from 'app/site/base/base-view-model';
|
||||
|
||||
/**
|
||||
* Provides "safe" access to topic with all it's components
|
||||
* @ignore
|
||||
*/
|
||||
export class ViewTopic extends BaseViewModel {
|
||||
export class ViewTopic extends BaseAgendaViewModel {
|
||||
protected _topic: Topic;
|
||||
private _attachments: Mediafile[];
|
||||
private _agenda_item: Item;
|
||||
private _attachments: ViewMediafile[];
|
||||
private _agendaItem: ViewItem;
|
||||
|
||||
public get topic(): Topic {
|
||||
return this._topic;
|
||||
}
|
||||
|
||||
public get attachments(): Mediafile[] {
|
||||
public get attachments(): ViewMediafile[] {
|
||||
return this._attachments;
|
||||
}
|
||||
|
||||
public get agenda_item(): Item {
|
||||
return this._agenda_item;
|
||||
public get agendaItem(): ViewItem {
|
||||
return this._agendaItem;
|
||||
}
|
||||
|
||||
public get id(): number {
|
||||
return this.topic ? this.topic.id : null;
|
||||
return this.topic.id;
|
||||
}
|
||||
|
||||
public get agenda_item_id(): number {
|
||||
return this.topic ? this.topic.agenda_item_id : null;
|
||||
return this.topic.agenda_item_id;
|
||||
}
|
||||
|
||||
public get attachments_id(): number[] {
|
||||
return this.topic ? this.topic.attachments_id : null;
|
||||
return this.topic.attachments_id;
|
||||
}
|
||||
|
||||
public get title(): string {
|
||||
return this.topic ? this.topic.title : null;
|
||||
return this.topic.title;
|
||||
}
|
||||
|
||||
public get text(): string {
|
||||
return this.topic ? this.topic.text : null;
|
||||
return this.topic.text;
|
||||
}
|
||||
|
||||
public constructor(topic?: Topic, attachments?: Mediafile[], item?: Item) {
|
||||
super();
|
||||
public constructor(topic: Topic, attachments?: ViewMediafile[], item?: ViewItem) {
|
||||
super('Topic');
|
||||
this._topic = topic;
|
||||
this._attachments = attachments;
|
||||
this._agenda_item = item;
|
||||
this._agendaItem = item;
|
||||
}
|
||||
|
||||
public getTitle(): string {
|
||||
return this.title;
|
||||
}
|
||||
|
||||
public getAgendaItem(): ViewItem {
|
||||
return this.agendaItem;
|
||||
}
|
||||
|
||||
public getAgendaTitleWithType(): string {
|
||||
// Do not append ' (Topic)' to the title.
|
||||
return this.getAgendaTitle();
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats the category for search
|
||||
*
|
||||
* @override
|
||||
*/
|
||||
public formatForSearch(): SearchRepresentation {
|
||||
return [this.title, this.text];
|
||||
}
|
||||
|
||||
public getDetailStateURL(): string {
|
||||
return `/agenda/topics/${this.id}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the text to be inserted in csv exports
|
||||
* @override
|
||||
*/
|
||||
public getCSVExportText(): string {
|
||||
return this.text;
|
||||
}
|
||||
|
||||
public getSlide(): ProjectorElementBuildDeskriptor {
|
||||
throw new Error('TODO');
|
||||
}
|
||||
|
||||
public hasAttachments(): boolean {
|
||||
return this.attachments && this.attachments.length > 0;
|
||||
}
|
||||
|
||||
public updateValues(update: BaseModel): void {
|
||||
if (update instanceof Mediafile) {
|
||||
if (this.topic && this.attachments_id && this.attachments_id.includes(update.id)) {
|
||||
const attachmentIndex = this.attachments.findIndex(mediafile => mediafile.id === update.id);
|
||||
this.attachments[attachmentIndex] = update as Mediafile;
|
||||
public updateDependencies(update: BaseViewModel): void {
|
||||
if (update instanceof ViewMediafile && this.attachments_id.includes(update.id)) {
|
||||
const attachmentIndex = this.attachments.findIndex(mediafile => mediafile.id === update.id);
|
||||
if (attachmentIndex < 0) {
|
||||
this.attachments.push(update);
|
||||
} else {
|
||||
this.attachments[attachmentIndex] = update;
|
||||
}
|
||||
}
|
||||
if (update instanceof ViewItem && this.agenda_item_id === update.id) {
|
||||
this._agendaItem = update;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4,7 +4,7 @@ import { BaseFilterListService, OsFilter, OsFilterOption } from 'app/core/ui-ser
|
||||
import { Item, itemVisibilityChoices } from 'app/shared/models/agenda/item';
|
||||
import { ViewItem } from '../models/view-item';
|
||||
import { StorageService } from 'app/core/core-services/storage.service';
|
||||
import { AgendaRepositoryService } from 'app/core/repositories/agenda/agenda-repository.service';
|
||||
import { ItemRepositoryService } from 'app/core/repositories/agenda/item-repository.service';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
@ -19,7 +19,7 @@ export class AgendaFilterListService extends BaseFilterListService<Item, ViewIte
|
||||
* @param store
|
||||
* @param repo
|
||||
*/
|
||||
public constructor(store: StorageService, repo: AgendaRepositoryService) {
|
||||
public constructor(store: StorageService, repo: ItemRepositoryService) {
|
||||
super(store, repo);
|
||||
this.filterOptions = [
|
||||
{
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { AppConfig } from '../../core/app-config';
|
||||
import { Assignment } from '../../shared/models/assignments/assignment';
|
||||
import { AssignmentRepositoryService } from 'app/core/repositories/assignments/assignment-repository.service';
|
||||
import { ViewAssignment } from './models/view-assignment';
|
||||
|
||||
export const AssignmentsAppConfig: AppConfig = {
|
||||
name: 'assignments',
|
||||
@ -8,6 +9,7 @@ export const AssignmentsAppConfig: AppConfig = {
|
||||
{
|
||||
collectionString: 'assignments/assignment',
|
||||
model: Assignment,
|
||||
viewModel: ViewAssignment,
|
||||
searchOrder: 3,
|
||||
repository: AssignmentRepositoryService
|
||||
}
|
||||
|
@ -1,14 +1,17 @@
|
||||
import { BaseViewModel } from '../../base/base-view-model';
|
||||
import { Assignment } from 'app/shared/models/assignments/assignment';
|
||||
import { Tag } from 'app/shared/models/core/tag';
|
||||
import { User } from 'app/shared/models/users/user';
|
||||
import { Item } from 'app/shared/models/agenda/item';
|
||||
import { BaseAgendaViewModel } from 'app/site/base/base-agenda-view-model';
|
||||
import { SearchRepresentation } from 'app/core/ui-services/search.service';
|
||||
import { ProjectorElementBuildDeskriptor } from 'app/site/base/projectable';
|
||||
import { ViewUser } from 'app/site/users/models/view-user';
|
||||
import { ViewItem } from 'app/site/agenda/models/view-item';
|
||||
import { ViewTag } from 'app/site/tags/models/view-tag';
|
||||
import { BaseViewModel } from 'app/site/base/base-view-model';
|
||||
|
||||
export class ViewAssignment extends BaseViewModel {
|
||||
export class ViewAssignment extends BaseAgendaViewModel {
|
||||
private _assignment: Assignment;
|
||||
private _relatedUser: User[];
|
||||
private _agendaItem: Item;
|
||||
private _tags: Tag[];
|
||||
private _relatedUser: ViewUser[];
|
||||
private _agendaItem: ViewItem;
|
||||
private _tags: ViewTag[];
|
||||
|
||||
public get id(): number {
|
||||
return this._assignment ? this._assignment.id : null;
|
||||
@ -18,15 +21,15 @@ export class ViewAssignment extends BaseViewModel {
|
||||
return this._assignment;
|
||||
}
|
||||
|
||||
public get candidates(): User[] {
|
||||
public get candidates(): ViewUser[] {
|
||||
return this._relatedUser;
|
||||
}
|
||||
|
||||
public get agendaItem(): Item {
|
||||
public get agendaItem(): ViewItem {
|
||||
return this._agendaItem;
|
||||
}
|
||||
|
||||
public get tags(): Tag[] {
|
||||
public get tags(): ViewTag[] {
|
||||
return this._tags;
|
||||
}
|
||||
|
||||
@ -41,17 +44,35 @@ export class ViewAssignment extends BaseViewModel {
|
||||
return this.candidates ? this.candidates.length : 0;
|
||||
}
|
||||
|
||||
public constructor(assignment: Assignment, relatedUser: User[], agendaItem?: Item, tags?: Tag[]) {
|
||||
super();
|
||||
public constructor(assignment: Assignment, relatedUser?: ViewUser[], agendaItem?: ViewItem, tags?: ViewTag[]) {
|
||||
super('Election');
|
||||
this._assignment = assignment;
|
||||
this._relatedUser = relatedUser;
|
||||
this._agendaItem = agendaItem;
|
||||
this._tags = tags;
|
||||
}
|
||||
|
||||
public updateValues(): void {}
|
||||
public updateDependencies(update: BaseViewModel): void {
|
||||
console.log('TODO: assignment updateDependencies');
|
||||
}
|
||||
|
||||
public getAgendaItem(): ViewItem {
|
||||
return this.agendaItem;
|
||||
}
|
||||
|
||||
public getTitle(): string {
|
||||
return this.assignment ? this.assignment.title : null;
|
||||
return this.assignment.title;
|
||||
}
|
||||
|
||||
public formatForSearch(): SearchRepresentation {
|
||||
throw new Error('TODO');
|
||||
}
|
||||
|
||||
public getDetailStateURL(): string {
|
||||
return 'TODO';
|
||||
}
|
||||
|
||||
public getSlide(): ProjectorElementBuildDeskriptor {
|
||||
throw new Error('TODO');
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { DetailNavigable } from './detail-navigable';
|
||||
import { DetailNavigable } from '../../shared/models/base/detail-navigable';
|
||||
import { ViewItem } from '../agenda/models/view-item';
|
||||
|
||||
/**
|
||||
* An Interface for all extra information needed for content objects of items.
|
||||
@ -13,4 +14,14 @@ export interface AgendaInformation extends DetailNavigable {
|
||||
* Should return the title for the list of speakers view.
|
||||
*/
|
||||
getAgendaTitleWithType(): string;
|
||||
|
||||
/**
|
||||
* An (optional) descriptive text to be exported in the CSV.
|
||||
*/
|
||||
getCSVExportText(): string;
|
||||
|
||||
/**
|
||||
* Get access to the agenda item
|
||||
*/
|
||||
getAgendaItem(): ViewItem;
|
||||
}
|
@ -1,22 +1,13 @@
|
||||
import { AgendaInformation } from './agenda-information';
|
||||
import { Searchable } from './searchable';
|
||||
import { AgendaInformation } from 'app/site/base/agenda-information';
|
||||
import { BaseProjectableViewModel } from './base-projectable-view-model';
|
||||
import { SearchRepresentation } from 'app/core/ui-services/search.service';
|
||||
import { BaseModel } from './base-model';
|
||||
import { ViewItem } from '../agenda/models/view-item';
|
||||
|
||||
/**
|
||||
* A base model for models, that can be content objects in the agenda. Provides title and navigation
|
||||
* information for the agenda.
|
||||
* Base view class for projectable models.
|
||||
*/
|
||||
export abstract class AgendaBaseModel extends BaseModel<AgendaBaseModel> implements AgendaInformation, Searchable {
|
||||
/**
|
||||
* A model that can be a content object of an agenda item.
|
||||
* @param collectionString
|
||||
* @param verboseName
|
||||
* @param input
|
||||
*/
|
||||
protected constructor(collectionString: string, verboseName: string, input?: any) {
|
||||
super(collectionString, verboseName, input);
|
||||
}
|
||||
export abstract class BaseAgendaViewModel extends BaseProjectableViewModel implements AgendaInformation {
|
||||
public abstract getAgendaItem(): ViewItem;
|
||||
|
||||
/**
|
||||
* @returns the agenda title
|
||||
@ -41,14 +32,10 @@ export abstract class AgendaBaseModel extends BaseModel<AgendaBaseModel> impleme
|
||||
return '';
|
||||
}
|
||||
|
||||
public abstract getDetailStateURL(): string;
|
||||
|
||||
/**
|
||||
* Should return a string representation of the object, so there can be searched for.
|
||||
*/
|
||||
public abstract formatForSearch(): SearchRepresentation;
|
||||
|
||||
/**
|
||||
* Should return the URL to the detail view. Used for the agenda, that the
|
||||
* user can navigate to the content object.
|
||||
*/
|
||||
public abstract getDetailStateURL(): string;
|
||||
}
|
@ -4,6 +4,10 @@ import { BaseViewModel } from './base-view-model';
|
||||
/**
|
||||
* Base view class for projectable models.
|
||||
*/
|
||||
export abstract class BaseProjectableModel extends BaseViewModel implements Projectable {
|
||||
export abstract class BaseProjectableViewModel extends BaseViewModel implements Projectable {
|
||||
public constructor(verboseName: string) {
|
||||
super(verboseName);
|
||||
}
|
||||
|
||||
public abstract getSlide(): ProjectorElementBuildDeskriptor;
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
import { Displayable } from '../../shared/models/base/displayable';
|
||||
import { Displayable } from './displayable';
|
||||
import { Identifiable } from '../../shared/models/base/identifiable';
|
||||
import { Deserializable } from 'app/shared/models/base/deserializable';
|
||||
|
||||
export type ViewModelConstructor<T extends BaseViewModel> = new (...args: any[]) => T;
|
||||
|
||||
/**
|
||||
* Base class for view models. alls view models should have titles.
|
||||
@ -11,7 +12,32 @@ export abstract class BaseViewModel implements Displayable, Identifiable {
|
||||
*/
|
||||
public abstract id: number;
|
||||
|
||||
public abstract updateValues(update: Deserializable): void;
|
||||
/**
|
||||
* Children should also have a verbose name for generic display purposes
|
||||
*/
|
||||
protected _verboseName: string;
|
||||
|
||||
public constructor(verboseName: string) {
|
||||
this._verboseName = verboseName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the verbose name. Makes it plural by adding a 's'.
|
||||
*
|
||||
* @param plural If the name should be plural
|
||||
* @returns the verbose name of the model
|
||||
*/
|
||||
public getVerboseName(plural: boolean = false): string {
|
||||
if (plural) {
|
||||
return this._verboseName + 's'; // I love english. This works for all our models (participantS, electionS,
|
||||
// topicS, motionS, (media)fileS, motion blockS, commentS, personal noteS, projectorS, messageS, countdownS, ...)
|
||||
// Just categorIES need to overwrite this...
|
||||
} else {
|
||||
return this._verboseName;
|
||||
}
|
||||
}
|
||||
|
||||
public abstract updateDependencies(update: BaseViewModel): void;
|
||||
|
||||
public abstract getTitle(): string;
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { Displayable } from 'app/shared/models/base/displayable';
|
||||
import { Displayable } from 'app/site/base/displayable';
|
||||
import { IdentifiableProjectorElement } from 'app/shared/models/core/projector';
|
||||
import { SlideOptions } from './slide-options';
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { DetailNavigable } from './detail-navigable';
|
||||
import { SearchRepresentation } from 'app/core/ui-services/search.service';
|
||||
import { DetailNavigable } from 'app/shared/models/base/detail-navigable';
|
||||
|
||||
/**
|
||||
* Asserts, if the given object is searchable.
|
@ -1,10 +1,18 @@
|
||||
import { AppConfig } from '../../core/app-config';
|
||||
import { ChatMessage } from '../../shared/models/core/chat-message';
|
||||
import { ChatMessageRepositoryService } from 'app/core/repositories/common/chatmessage-repository.service';
|
||||
import { ViewChatMessage } from './models/view-chatmessage';
|
||||
|
||||
export const CommonAppConfig: AppConfig = {
|
||||
name: 'common',
|
||||
models: [{ collectionString: 'core/chat-message', model: ChatMessage, repository: ChatMessageRepositoryService }],
|
||||
models: [
|
||||
{
|
||||
collectionString: 'core/chat-message',
|
||||
model: ChatMessage,
|
||||
viewModel: ViewChatMessage,
|
||||
repository: ChatMessageRepositoryService
|
||||
}
|
||||
],
|
||||
mainMenuEntries: [
|
||||
{
|
||||
route: '/',
|
||||
|
@ -1,9 +1,10 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { Title } from '@angular/platform-browser';
|
||||
import { BaseComponent } from 'app/base.component';
|
||||
|
||||
import { TranslateService } from '@ngx-translate/core'; // showcase
|
||||
|
||||
import { BaseComponent } from 'app/base.component';
|
||||
|
||||
// for testing the DS and BaseModel
|
||||
import { Config } from 'app/shared/models/core/config';
|
||||
import { DataStoreService } from 'app/core/core-services/data-store.service';
|
||||
|
@ -2,30 +2,28 @@ import { ChatMessage } from 'app/shared/models/core/chat-message';
|
||||
import { BaseViewModel } from 'app/site/base/base-view-model';
|
||||
|
||||
export class ViewChatMessage extends BaseViewModel {
|
||||
private _message: ChatMessage;
|
||||
private _chatMessage: ChatMessage;
|
||||
|
||||
public get chatmessage(): ChatMessage {
|
||||
return this._message ? this._message : null;
|
||||
return this._chatMessage;
|
||||
}
|
||||
|
||||
public get id(): number {
|
||||
return this.chatmessage ? this.chatmessage.id : null;
|
||||
return this.chatmessage.id;
|
||||
}
|
||||
|
||||
public get message(): string {
|
||||
return this.chatmessage ? this.chatmessage.message : null;
|
||||
return this.chatmessage.message;
|
||||
}
|
||||
|
||||
public constructor(message?: ChatMessage) {
|
||||
super();
|
||||
this._message = message;
|
||||
super('Chatmessage');
|
||||
this._chatMessage = message;
|
||||
}
|
||||
|
||||
public getTitle(): string {
|
||||
return 'Chatmessage';
|
||||
}
|
||||
|
||||
public updateValues(message: ChatMessage): void {
|
||||
console.log('Update message TODO with vals:', message);
|
||||
}
|
||||
public updateDependencies(message: BaseViewModel): void {}
|
||||
}
|
||||
|
@ -1,15 +1,15 @@
|
||||
import { Component, OnInit, Input, ChangeDetectorRef, ChangeDetectionStrategy } from '@angular/core';
|
||||
import { FormGroup, FormBuilder } from '@angular/forms';
|
||||
import { Title } from '@angular/platform-browser';
|
||||
import { distinctUntilChanged } from 'rxjs/operators';
|
||||
|
||||
import { distinctUntilChanged } from 'rxjs/operators';
|
||||
import { DateTimeAdapter } from 'ng-pick-datetime';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
|
||||
import { BaseComponent } from '../../../../base.component';
|
||||
import { ConfigRepositoryService } from '../../services/config-repository.service';
|
||||
import { BaseComponent } from 'app/base.component';
|
||||
import { ParentErrorStateMatcher } from 'app/shared/parent-error-state-matcher';
|
||||
import { ViewConfig } from '../../models/view-config';
|
||||
import { ConfigRepositoryService } from 'app/core/repositories/config/config-repository.service';
|
||||
|
||||
/**
|
||||
* List view for the categories.
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { Title } from '@angular/platform-browser';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { ConfigRepositoryService, ConfigGroup } from '../../services/config-repository.service';
|
||||
import { ConfigRepositoryService, ConfigGroup } from '../../../../core/repositories/config/config-repository.service';
|
||||
import { BaseComponent } from '../../../../base.component';
|
||||
|
||||
/**
|
||||
|
@ -1,10 +1,13 @@
|
||||
import { AppConfig } from '../../core/app-config';
|
||||
import { Config } from '../../shared/models/core/config';
|
||||
import { ConfigRepositoryService } from './services/config-repository.service';
|
||||
import { ConfigRepositoryService } from '../../core/repositories/config/config-repository.service';
|
||||
import { ViewConfig } from './models/view-config';
|
||||
|
||||
export const ConfigAppConfig: AppConfig = {
|
||||
name: 'settings',
|
||||
models: [{ collectionString: 'core/config', model: Config, repository: ConfigRepositoryService }],
|
||||
models: [
|
||||
{ collectionString: 'core/config', model: Config, viewModel: ViewConfig, repository: ConfigRepositoryService }
|
||||
],
|
||||
mainMenuEntries: [
|
||||
{
|
||||
route: '/settings',
|
||||
|
@ -94,7 +94,7 @@ export class ViewConfig extends BaseViewModel {
|
||||
}
|
||||
|
||||
public constructor(config: Config) {
|
||||
super();
|
||||
super('Config');
|
||||
this._config = config;
|
||||
}
|
||||
|
||||
@ -102,9 +102,7 @@ export class ViewConfig extends BaseViewModel {
|
||||
return this.label;
|
||||
}
|
||||
|
||||
public updateValues(update: Config): void {
|
||||
this._config = update;
|
||||
}
|
||||
public updateDependencies(update: BaseViewModel): void {}
|
||||
|
||||
/**
|
||||
* Returns the time this config field needs to debounce before sending a request to the server.
|
||||
|
@ -2,16 +2,15 @@ import { Component, OnInit } from '@angular/core';
|
||||
import { Title } from '@angular/platform-browser';
|
||||
import { MatSnackBar } from '@angular/material';
|
||||
import { Router } from '@angular/router';
|
||||
import { Subject } from 'rxjs';
|
||||
|
||||
import { Subject } from 'rxjs';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
|
||||
import { ListViewBaseComponent } from 'app/site/base/list-view-base';
|
||||
import { HistoryRepositoryService } from 'app/core/repositories/history/history-repository.service';
|
||||
import { DataStoreService } from 'app/core/core-services/data-store.service';
|
||||
import { isDetailNavigable } from 'app/shared/models/base/detail-navigable';
|
||||
|
||||
import { ViewHistory } from '../../models/view-history';
|
||||
import { ViewModelStoreService } from 'app/core/core-services/view-model-store.service';
|
||||
|
||||
/**
|
||||
* A list view for the history.
|
||||
@ -42,7 +41,7 @@ export class HistoryListComponent extends ListViewBaseComponent<ViewHistory> imp
|
||||
translate: TranslateService,
|
||||
matSnackBar: MatSnackBar,
|
||||
private repo: HistoryRepositoryService,
|
||||
private DS: DataStoreService,
|
||||
private viewModelStore: ViewModelStoreService,
|
||||
private router: Router
|
||||
) {
|
||||
super(titleService, translate, matSnackBar);
|
||||
@ -89,7 +88,7 @@ export class HistoryListComponent extends ListViewBaseComponent<ViewHistory> imp
|
||||
* @returns the title of an old element or null if it could not be found
|
||||
*/
|
||||
public getElementInfo(history: ViewHistory): string {
|
||||
const oldElementTitle = this.repo.getOldModelInfo(history.getCollectionString(), history.getModelID());
|
||||
const oldElementTitle = this.repo.getOldModelInfo(history.getCollectionString(), history.getModelId());
|
||||
|
||||
if (oldElementTitle) {
|
||||
return oldElementTitle;
|
||||
@ -104,22 +103,19 @@ export class HistoryListComponent extends ListViewBaseComponent<ViewHistory> imp
|
||||
*
|
||||
* @param history Represents the selected element
|
||||
*/
|
||||
public onClickRow(history: ViewHistory): void {
|
||||
this.repo.browseHistory(history).then(() => {
|
||||
const element = this.DS.get(history.getCollectionString(), history.getModelID());
|
||||
let message = this.translate.instant('Temporarily reset OpenSlides to the state from ');
|
||||
message += history.getLocaleString('DE-de') + '. ';
|
||||
public async onClickRow(history: ViewHistory): Promise<void> {
|
||||
await this.repo.browseHistory(history);
|
||||
const element = this.viewModelStore.get(history.getCollectionString(), history.getModelId());
|
||||
let message = this.translate.instant('Temporarily reset OpenSlides to the state from ');
|
||||
message += history.getLocaleString('DE-de') + '. ';
|
||||
|
||||
if (isDetailNavigable(element)) {
|
||||
message += this.translate.instant(
|
||||
'You will be redirected to the detail state of the last changed item.'
|
||||
);
|
||||
this.raiseError(message);
|
||||
this.router.navigate([element.getDetailStateURL()]);
|
||||
} else {
|
||||
this.raiseError(message);
|
||||
}
|
||||
});
|
||||
if (isDetailNavigable(element)) {
|
||||
message += this.translate.instant('You will be redirected to the detail state of the last changed item.');
|
||||
this.raiseError(message);
|
||||
this.router.navigate([element.getDetailStateURL()]);
|
||||
} else {
|
||||
this.raiseError(message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { AppConfig } from '../../core/app-config';
|
||||
import { History } from 'app/shared/models/core/history';
|
||||
import { HistoryRepositoryService } from 'app/core/repositories/history/history-repository.service';
|
||||
import { ViewHistory } from './models/view-history';
|
||||
|
||||
/**
|
||||
* Config object for history.
|
||||
@ -8,7 +9,14 @@ import { HistoryRepositoryService } from 'app/core/repositories/history/history-
|
||||
*/
|
||||
export const HistoryAppConfig: AppConfig = {
|
||||
name: 'history',
|
||||
models: [{ collectionString: 'core/history', model: History, repository: HistoryRepositoryService }],
|
||||
models: [
|
||||
{
|
||||
collectionString: 'core/history',
|
||||
model: History,
|
||||
viewModel: ViewHistory,
|
||||
repository: HistoryRepositoryService
|
||||
}
|
||||
],
|
||||
mainMenuEntries: [
|
||||
{
|
||||
route: '/history',
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user