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 { ModelConstructor, BaseModel } from '../shared/models/base/base-model';
|
||||||
import { MainMenuEntry } from './core-services/main-menu.service';
|
import { MainMenuEntry } from './core-services/main-menu.service';
|
||||||
import { Searchable } from '../shared/models/base/searchable';
|
import { Searchable } from '../site/base/searchable';
|
||||||
import { Type } from '@angular/core';
|
|
||||||
import { BaseRepository } from './repositories/base-repository';
|
import { BaseRepository } from './repositories/base-repository';
|
||||||
|
import { BaseViewModel, ViewModelConstructor } from 'app/site/base/base-view-model';
|
||||||
|
|
||||||
interface BaseModelEntry {
|
interface BaseModelEntry {
|
||||||
collectionString: string;
|
collectionString: string;
|
||||||
repository: Type<BaseRepository<any, any>>;
|
repository: Type<BaseRepository<any, any>>;
|
||||||
}
|
|
||||||
|
|
||||||
export interface ModelEntry extends BaseModelEntry {
|
|
||||||
model: ModelConstructor<BaseModel>;
|
model: ModelConstructor<BaseModel>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ModelEntry extends BaseModelEntry {
|
||||||
|
viewModel: ViewModelConstructor<BaseViewModel>;
|
||||||
|
}
|
||||||
|
|
||||||
export interface SearchableModelEntry extends BaseModelEntry {
|
export interface SearchableModelEntry extends BaseModelEntry {
|
||||||
model: new (...args: any[]) => BaseModel & Searchable;
|
viewModel: new (...args: any[]) => BaseViewModel & Searchable;
|
||||||
searchOrder: number;
|
searchOrder: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,9 +14,11 @@ import { TagAppConfig } from '../../site/tags/tag.config';
|
|||||||
import { MainMenuService } from './main-menu.service';
|
import { MainMenuService } from './main-menu.service';
|
||||||
import { HistoryAppConfig } from 'app/site/history/history.config';
|
import { HistoryAppConfig } from 'app/site/history/history.config';
|
||||||
import { SearchService } from '../ui-services/search.service';
|
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 { ProjectorAppConfig } from 'app/site/projector/projector.config';
|
||||||
import { BaseRepository } from 'app/core/repositories/base-repository';
|
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.
|
* A list of all app configurations of all delivered apps.
|
||||||
@ -63,16 +65,21 @@ export class AppLoadService {
|
|||||||
const plugin = await import('../../../../../plugins/' + pluginName + '/' + pluginName);
|
const plugin = await import('../../../../../plugins/' + pluginName + '/' + pluginName);
|
||||||
plugin.main();
|
plugin.main();
|
||||||
}*/
|
}*/
|
||||||
|
const repositories: OnAfterAppsLoaded[] = [];
|
||||||
appConfigs.forEach((config: AppConfig) => {
|
appConfigs.forEach((config: AppConfig) => {
|
||||||
if (config.models) {
|
if (config.models) {
|
||||||
config.models.forEach(entry => {
|
config.models.forEach(entry => {
|
||||||
let repository: BaseRepository<any, any> = null;
|
let repository: BaseRepository<any, any> = null;
|
||||||
if (entry.repository) {
|
repository = this.injector.get(entry.repository);
|
||||||
repository = this.injector.get(entry.repository);
|
repositories.push(repository);
|
||||||
}
|
this.modelMapper.registerCollectionElement(
|
||||||
this.modelMapper.registerCollectionElement(entry.collectionString, entry.model, repository);
|
entry.collectionString,
|
||||||
|
entry.model,
|
||||||
|
entry.viewModel,
|
||||||
|
repository
|
||||||
|
);
|
||||||
if (this.isSearchableModelEntry(entry)) {
|
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);
|
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 {
|
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
|
// 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
|
// 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.
|
// 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(
|
throw Error(
|
||||||
`Wrong configuration for ${
|
`Wrong configuration for ${
|
||||||
entry.collectionString
|
entry.collectionString
|
||||||
|
@ -120,7 +120,6 @@ export class AutoupdateService extends OpenSlidesComponent {
|
|||||||
await this.DS.add(this.mapObjectsToBaseModels(collection, autoupdate.changed[collection]));
|
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);
|
await this.DS.flushToStorage(autoupdate.to_change_id);
|
||||||
} else {
|
} else {
|
||||||
// autoupdate fully in the future. we are missing something!
|
// 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.
|
* @returns A list of basemodels constructed from the given models.
|
||||||
*/
|
*/
|
||||||
private mapObjectsToBaseModels(collection: string, models: object[]): BaseModel[] {
|
private mapObjectsToBaseModels(collection: string, models: object[]): BaseModel[] {
|
||||||
const targetClass = this.modelMapper.getModelConstructor(collection);
|
const targetClass = this.modelMapper.getModelConstructorFromCollectionString(collection);
|
||||||
if (!targetClass) {
|
if (!targetClass) {
|
||||||
throw new Error(`Unregistered resource ${collection}`);
|
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 { ModelConstructor, BaseModel } from '../../shared/models/base/base-model';
|
||||||
import { BaseRepository } from 'app/core/repositories/base-repository';
|
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({
|
@Injectable({
|
||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
})
|
})
|
||||||
export class CollectionStringMapperService {
|
export class CollectionStringMapperService {
|
||||||
/**
|
/**
|
||||||
* Mapps collection strings to model constructors. Accessed by {@method registerCollectionElement} and
|
* Maps collection strings to mapping entries
|
||||||
* {@method getCollectionStringType}.
|
|
||||||
*/
|
*/
|
||||||
private collectionStringsTypeMapping: {
|
private collectionStringMapping: {
|
||||||
[collectionString: string]: [ModelConstructor<BaseModel>, BaseRepository<any, any>];
|
[collectionString: string]: MappingEntry;
|
||||||
} = {};
|
} = {};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor to create the NotifyService. Registers itself to the WebsocketService.
|
* Maps models to mapping entries
|
||||||
* @param websocketService
|
|
||||||
*/
|
*/
|
||||||
|
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() {}
|
public constructor() {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Registers the type to the collection string
|
* Registers the combination of a collection string, model, view model and repository
|
||||||
* @param collectionString
|
* @param collectionString
|
||||||
* @param model
|
* @param model
|
||||||
*/
|
*/
|
||||||
public registerCollectionElement(
|
public registerCollectionElement<V extends BaseViewModel, M extends BaseModel>(
|
||||||
collectionString: string,
|
collectionString: string,
|
||||||
model: ModelConstructor<BaseModel>,
|
model: ModelConstructor<M>,
|
||||||
repository: BaseRepository<any, any>
|
viewModel: ViewModelConstructor<V>,
|
||||||
|
repository: BaseRepository<V, M>
|
||||||
): void {
|
): 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
// The following accessors are for giving one of EntryType by given a different object
|
||||||
* Returns the constructor of the requested collection or undefined, if it is not registered.
|
// of EntryType.
|
||||||
* @param collectionString the requested collection
|
|
||||||
*/
|
public getCollectionStringFromModelConstructor<M extends BaseModel>(ctor: ModelConstructor<M>): string {
|
||||||
public getModelConstructor(collectionString: string): ModelConstructor<BaseModel> {
|
return this.modelMapping[ctor.name][0];
|
||||||
return this.collectionStringsTypeMapping[collectionString][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];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public getModelConstructorFromCollectionString<M extends BaseModel>(collectionString: string): ModelConstructor<M> {
|
||||||
* Returns the repository of the requested collection or undefined, if it is not registered.
|
return this.collectionStringMapping[collectionString][1] as ModelConstructor<M>;
|
||||||
* @param collectionString the requested collection
|
}
|
||||||
*/
|
public getModelConstructorFromViewModelConstructor<V extends BaseViewModel, M extends BaseModel>(
|
||||||
public getRepository(collectionString: string): BaseRepository<any, any> {
|
ctor: ViewModelConstructor<V>
|
||||||
return this.collectionStringsTypeMapping[collectionString][1];
|
): 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>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public getViewModelConstructorFromCollectionString<M extends BaseViewModel>(
|
||||||
* Returns the collection string of a given ModelConstructor or undefined, if it is not registered.
|
collectionString: string
|
||||||
* @param ctor
|
): ViewModelConstructor<M> {
|
||||||
*/
|
return this.collectionStringMapping[collectionString][2] as ViewModelConstructor<M>;
|
||||||
public getCollectionString(ctor: ModelConstructor<BaseModel>): string {
|
}
|
||||||
return Object.keys(this.collectionStringsTypeMapping).find((collectionString: string) => {
|
public getViewModelConstructorFromModelConstructor<V extends BaseViewModel, M extends BaseModel>(
|
||||||
return ctor === this.collectionStringsTypeMapping[collectionString][0];
|
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>();
|
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.
|
* Observe the datastore for changes.
|
||||||
*
|
*
|
||||||
@ -127,7 +139,12 @@ export class DataStoreService {
|
|||||||
* @param storageService use StorageService to preserve the DataStore.
|
* @param storageService use StorageService to preserve the DataStore.
|
||||||
* @param modelMapper
|
* @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.
|
* Gets the DataStore from cache and instantiate all models out of the serialized version.
|
||||||
@ -170,7 +187,7 @@ export class DataStoreService {
|
|||||||
const storage: ModelStorage = {};
|
const storage: ModelStorage = {};
|
||||||
Object.keys(serializedStore).forEach(collectionString => {
|
Object.keys(serializedStore).forEach(collectionString => {
|
||||||
storage[collectionString] = {} as ModelCollection;
|
storage[collectionString] = {} as ModelCollection;
|
||||||
const target = this.modelMapper.getModelConstructor(collectionString);
|
const target = this.modelMapper.getModelConstructorFromCollectionString(collectionString);
|
||||||
if (target) {
|
if (target) {
|
||||||
Object.keys(serializedStore[collectionString]).forEach(id => {
|
Object.keys(serializedStore[collectionString]).forEach(id => {
|
||||||
const data = JSON.parse(serializedStore[collectionString][id]);
|
const data = JSON.parse(serializedStore[collectionString][id]);
|
||||||
@ -201,7 +218,7 @@ export class DataStoreService {
|
|||||||
if (typeof collectionType === 'string') {
|
if (typeof collectionType === 'string') {
|
||||||
return collectionType;
|
return collectionType;
|
||||||
} else {
|
} 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 { environment } from 'environments/environment';
|
||||||
import { DataStoreService } from './data-store.service';
|
import { DataStoreService } from './data-store.service';
|
||||||
import { OfflineService } from './offline.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
|
* Permissions on the client are just strings. This makes clear, that
|
||||||
@ -36,12 +40,19 @@ export interface WhoAmIResponse {
|
|||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
})
|
})
|
||||||
export class OperatorService extends OpenSlidesComponent {
|
export class OperatorService extends OpenSlidesComponent implements OnAfterAppsLoaded {
|
||||||
/**
|
/**
|
||||||
* The operator.
|
* The operator.
|
||||||
*/
|
*/
|
||||||
private _user: User;
|
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.
|
* Get the user that corresponds to operator.
|
||||||
*/
|
*/
|
||||||
@ -49,14 +60,20 @@ export class OperatorService extends OpenSlidesComponent {
|
|||||||
return this._user;
|
return this._user;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the user that corresponds to operator.
|
||||||
|
*/
|
||||||
|
public get viewUser(): ViewUser {
|
||||||
|
return this._viewUser;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the current operator.
|
* Sets the current operator.
|
||||||
*
|
*
|
||||||
* The permissions are updated and the new user published.
|
* The permissions are updated and the new user published.
|
||||||
*/
|
*/
|
||||||
public set user(user: User) {
|
public set user(user: User) {
|
||||||
this._user = user;
|
this.updateUser(user);
|
||||||
this.updatePermissions();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public get isAnonymous(): boolean {
|
public get isAnonymous(): boolean {
|
||||||
@ -78,6 +95,16 @@ export class OperatorService extends OpenSlidesComponent {
|
|||||||
*/
|
*/
|
||||||
private operatorSubject: BehaviorSubject<User> = new BehaviorSubject<User>(null);
|
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,
|
* Sets up an observer for watching changes in the DS. If the operator user or groups are changed,
|
||||||
* the operator's permissions are updated.
|
* the operator's permissions are updated.
|
||||||
@ -86,7 +113,12 @@ export class OperatorService extends OpenSlidesComponent {
|
|||||||
* @param DS
|
* @param DS
|
||||||
* @param offlineService
|
* @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();
|
super();
|
||||||
|
|
||||||
this.DS.changeObservable.subscribe(newModel => {
|
this.DS.changeObservable.subscribe(newModel => {
|
||||||
@ -96,8 +128,7 @@ export class OperatorService extends OpenSlidesComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (newModel instanceof User && this._user.id === newModel.id) {
|
if (newModel instanceof User && this._user.id === newModel.id) {
|
||||||
this._user = newModel;
|
this.updateUser(newModel);
|
||||||
this.updatePermissions();
|
|
||||||
}
|
}
|
||||||
} else if (newModel instanceof Group && newModel.id === 1) {
|
} else if (newModel instanceof Group && newModel.id === 1) {
|
||||||
// Group 1 (default) for anonymous changed
|
// 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.
|
* Calls `/apps/users/whoami` to find out the real operator.
|
||||||
* @returns The response of the WhoAmI request.
|
* @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
|
* Services an components can use it to get informed when something changes in
|
||||||
* the operator
|
* the operator
|
||||||
*/
|
*/
|
||||||
public getObservable(): Observable<User> {
|
public getUserObservable(): Observable<User> {
|
||||||
return this.operatorSubject.asObservable();
|
return this.operatorSubject.asObservable();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public getViewUserObservable(): Observable<ViewUser> {
|
||||||
|
return this.viewOperatorSubject.asObservable();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks, if the operator has at least one of the given permissions.
|
* Checks, if the operator has at least one of the given permissions.
|
||||||
* @param checkPerms The permissions to check, if at least one matches.
|
* @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.
|
// publish changes in the operator.
|
||||||
this.operatorSubject.next(this.user);
|
this.operatorSubject.next(this.user);
|
||||||
|
this.viewOperatorSubject.next(this.viewUser);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,8 @@ import {
|
|||||||
import { HttpService } from './http.service';
|
import { HttpService } from './http.service';
|
||||||
import { SlideManager } from 'app/slides/services/slide-manager.service';
|
import { SlideManager } from 'app/slides/services/slide-manager.service';
|
||||||
import { BaseModel } from 'app/shared/models/base/base-model';
|
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
|
* This service cares about Projectables being projected and manage all projection-related
|
||||||
@ -34,7 +36,12 @@ export class ProjectorService extends OpenSlidesComponent {
|
|||||||
* @param DS
|
* @param DS
|
||||||
* @param dataSend
|
* @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();
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -222,13 +229,12 @@ export class ProjectorService extends OpenSlidesComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a model associated with the identifiable projector element. Throws an error,
|
* Asserts, that the given element is mappable to a model or view model.
|
||||||
* if the element is not mappable.
|
* Throws an error, if this assertion fails.
|
||||||
*
|
*
|
||||||
* @param element The projector element
|
* @param element The element to check
|
||||||
* @returns the model from the projector element
|
|
||||||
*/
|
*/
|
||||||
public getModelFromProjectorElement<T extends BaseModel>(element: IdentifiableProjectorElement): T {
|
private assertElementIsMappable(element: IdentifiableProjectorElement): void {
|
||||||
if (!this.slideManager.canSlideBeMappedToModel(element.name)) {
|
if (!this.slideManager.canSlideBeMappedToModel(element.name)) {
|
||||||
throw new Error('This projector element cannot be mapped to a model');
|
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')) {
|
if (!identifiers.includes('name') || !identifiers.includes('id')) {
|
||||||
throw new Error('To map this element to a model, a name and id is needed.');
|
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);
|
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
|
* Projects the next slide in the queue. Moves all currently projected
|
||||||
* non-stable slides to the history.
|
* non-stable slides to the history.
|
||||||
|
@ -68,7 +68,7 @@ export class TimeTravelService {
|
|||||||
[collectionString, id] = historyObject.element_id.split(':');
|
[collectionString, id] = historyObject.element_id.split(':');
|
||||||
|
|
||||||
if (historyObject.full_data) {
|
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)]);
|
await this.DS.add([new targetClass(historyObject.full_data)]);
|
||||||
} else {
|
} else {
|
||||||
await this.DS.remove(collectionString, [+id]);
|
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 { CommonModule } from '@angular/common';
|
||||||
import { Title } from '@angular/platform-browser';
|
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 { PromptDialogComponent } from '../shared/components/prompt-dialog/prompt-dialog.component';
|
||||||
import { ChoiceDialogComponent } from '../shared/components/choice-dialog/choice-dialog.component';
|
import { ChoiceDialogComponent } from '../shared/components/choice-dialog/choice-dialog.component';
|
||||||
import { ProjectionDialogComponent } from 'app/shared/components/projection-dialog/projection-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.
|
* 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 { TestBed, inject } from '@angular/core/testing';
|
||||||
|
|
||||||
import { AgendaRepositoryService } from './agenda-repository.service';
|
import { ItemRepositoryService } from './item-repository.service';
|
||||||
import { E2EImportsModule } from 'e2e-imports.module';
|
import { E2EImportsModule } from 'e2e-imports.module';
|
||||||
|
|
||||||
describe('AgendaRepositoryService', () => {
|
describe('ItemRepositoryService', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
imports: [E2EImportsModule],
|
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();
|
expect(service).toBeTruthy();
|
||||||
}));
|
}));
|
||||||
});
|
});
|
@ -3,8 +3,6 @@ import { tap, map } from 'rxjs/operators';
|
|||||||
import { Observable } from 'rxjs';
|
import { Observable } from 'rxjs';
|
||||||
|
|
||||||
import { BaseRepository } from '../base-repository';
|
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 { CollectionStringMapperService } from '../../core-services/collectionStringMapper.service';
|
||||||
import { ConfigService } from 'app/core/ui-services/config.service';
|
import { ConfigService } from 'app/core/ui-services/config.service';
|
||||||
import { DataSendService } from 'app/core/core-services/data-send.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 { Item } from 'app/shared/models/agenda/item';
|
||||||
import { OSTreeSortEvent } from 'app/shared/components/sorting-tree/sorting-tree.component';
|
import { OSTreeSortEvent } from 'app/shared/components/sorting-tree/sorting-tree.component';
|
||||||
import { Speaker } from 'app/shared/models/agenda/speaker';
|
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 { ViewItem } from 'app/site/agenda/models/view-item';
|
||||||
import { ViewSpeaker } from 'app/site/agenda/models/view-speaker';
|
import { ViewSpeaker } from 'app/site/agenda/models/view-speaker';
|
||||||
import { TreeService } from 'app/core/ui-services/tree.service';
|
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
|
* Repository service for users
|
||||||
@ -27,7 +28,7 @@ import { TreeService } from 'app/core/ui-services/tree.service';
|
|||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
})
|
})
|
||||||
export class AgendaRepositoryService extends BaseRepository<ViewItem, Item> {
|
export class ItemRepositoryService extends BaseRepository<ViewItem, Item> {
|
||||||
/**
|
/**
|
||||||
* Contructor for agenda repository.
|
* 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
|
* @param treeService sort the data according to weight and parents
|
||||||
*/
|
*/
|
||||||
public constructor(
|
public constructor(
|
||||||
protected DS: DataStoreService,
|
DS: DataStoreService,
|
||||||
private httpService: HttpService,
|
|
||||||
mapperService: CollectionStringMapperService,
|
mapperService: CollectionStringMapperService,
|
||||||
|
viewModelStoreService: ViewModelStoreService,
|
||||||
|
private httpService: HttpService,
|
||||||
private config: ConfigService,
|
private config: ConfigService,
|
||||||
private dataSend: DataSendService,
|
private dataSend: DataSendService,
|
||||||
private treeService: TreeService
|
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
|
* Used dynamically because of heavy race conditions
|
||||||
*
|
*
|
||||||
* @param agendaItem the target agenda Item
|
* @param agendaItem the target agenda Item
|
||||||
* @returns the content object of the given item. Might be null if it was not found.
|
* @returns the content object of the given item. Might be null if it was not found.
|
||||||
*/
|
*/
|
||||||
public getContentObject(agendaItem: Item): AgendaBaseModel {
|
public getContentObject(agendaItem: Item): BaseAgendaViewModel {
|
||||||
const contentObject = this.DS.get<BaseModel>(
|
const contentObject = this.viewModelStoreService.get<BaseViewModel>(
|
||||||
agendaItem.content_object.collection,
|
agendaItem.content_object.collection,
|
||||||
agendaItem.content_object.id
|
agendaItem.content_object.id
|
||||||
);
|
);
|
||||||
if (!contentObject) {
|
if (!contentObject) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if (contentObject instanceof AgendaBaseModel) {
|
if (contentObject instanceof BaseAgendaViewModel) {
|
||||||
return contentObject as AgendaBaseModel;
|
return contentObject as BaseAgendaViewModel;
|
||||||
} else {
|
} else {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`The content object (${agendaItem.content_object.collection}, ${
|
`The content object (${agendaItem.content_object.collection}, ${
|
||||||
agendaItem.content_object.id
|
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;
|
const speakers = item.speakers;
|
||||||
if (speakers && speakers.length > 0) {
|
if (speakers && speakers.length > 0) {
|
||||||
speakers.forEach((speaker: Speaker) => {
|
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));
|
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 { Identifiable } from 'app/shared/models/base/identifiable';
|
||||||
import { CollectionStringMapperService } from 'app/core/core-services/collectionStringMapper.service';
|
import { CollectionStringMapperService } from 'app/core/core-services/collectionStringMapper.service';
|
||||||
import { CreateTopic } from 'app/site/agenda/models/create-topic';
|
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
|
* Repository for topics
|
||||||
@ -28,9 +31,10 @@ export class TopicRepositoryService extends BaseRepository<ViewTopic, Topic> {
|
|||||||
public constructor(
|
public constructor(
|
||||||
DS: DataStoreService,
|
DS: DataStoreService,
|
||||||
mapperService: CollectionStringMapperService,
|
mapperService: CollectionStringMapperService,
|
||||||
|
viewModelStoreService: ViewModelStoreService,
|
||||||
private dataSend: DataSendService
|
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
|
* @returns a new view topic
|
||||||
*/
|
*/
|
||||||
public createViewModel(topic: Topic): ViewTopic {
|
public createViewModel(topic: Topic): ViewTopic {
|
||||||
const attachments = this.DS.getMany(Mediafile, topic.attachments_id);
|
const attachments = this.viewModelStoreService.getMany(ViewMediafile, topic.attachments_id);
|
||||||
const item = this.getAgendaItem(topic);
|
const item = this.viewModelStoreService.get(ViewItem, topic.agenda_item_id);
|
||||||
return new ViewTopic(topic, attachments, item);
|
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
|
* Save a new topic
|
||||||
*
|
*
|
||||||
|
@ -8,6 +8,10 @@ import { BaseRepository } from '../base-repository';
|
|||||||
import { DataStoreService } from '../../core-services/data-store.service';
|
import { DataStoreService } from '../../core-services/data-store.service';
|
||||||
import { Identifiable } from 'app/shared/models/base/identifiable';
|
import { Identifiable } from 'app/shared/models/base/identifiable';
|
||||||
import { CollectionStringMapperService } from '../../core-services/collectionStringMapper.service';
|
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.
|
* Repository Service for Assignments.
|
||||||
@ -24,8 +28,12 @@ export class AssignmentRepositoryService extends BaseRepository<ViewAssignment,
|
|||||||
* @param DS The DataStore
|
* @param DS The DataStore
|
||||||
* @param mapperService Maps collection strings to classes
|
* @param mapperService Maps collection strings to classes
|
||||||
*/
|
*/
|
||||||
public constructor(DS: DataStoreService, mapperService: CollectionStringMapperService) {
|
public constructor(
|
||||||
super(DS, mapperService, Assignment, [User, Item, Tag]);
|
DS: DataStoreService,
|
||||||
|
mapperService: CollectionStringMapperService,
|
||||||
|
viewModelStoreService: ViewModelStoreService
|
||||||
|
) {
|
||||||
|
super(DS, mapperService, viewModelStoreService, Assignment, [User, Item, Tag]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async update(assignment: Partial<Assignment>, viewAssignment: ViewAssignment): Promise<void> {
|
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 {
|
public createViewModel(assignment: Assignment): ViewAssignment {
|
||||||
const relatedUser = this.DS.getMany(User, assignment.candidateIds);
|
const relatedUser = this.viewModelStoreService.getMany(ViewUser, assignment.candidateIds);
|
||||||
const agendaItem = this.DS.get(Item, assignment.agenda_item_id);
|
const agendaItem = this.viewModelStoreService.get(ViewItem, assignment.agenda_item_id);
|
||||||
const tags = this.DS.getMany(Tag, assignment.tags_id);
|
const tags = this.viewModelStoreService.getMany(ViewTag, assignment.tags_id);
|
||||||
|
|
||||||
return new ViewAssignment(assignment, relatedUser, agendaItem, tags);
|
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 { DataStoreService } from '../core-services/data-store.service';
|
||||||
import { Identifiable } from '../../shared/models/base/identifiable';
|
import { Identifiable } from '../../shared/models/base/identifiable';
|
||||||
import { auditTime } from 'rxjs/operators';
|
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
|
* 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>();
|
protected readonly generalViewModelSubject: Subject<V> = new Subject<V>();
|
||||||
|
|
||||||
|
private _name: string;
|
||||||
|
|
||||||
|
public get name(): string {
|
||||||
|
return this._name;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construction routine for the base repository
|
* Construction routine for the base repository
|
||||||
*
|
*
|
||||||
* @param DS: The DataStore
|
* @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 baseModelCtor The model constructor of which this repository is about.
|
||||||
* @param depsModelCtors A list of constructors that are used in the view model.
|
* @param depsModelCtors A list of constructors that are used in the view model.
|
||||||
* If one of those changes, the view models will be updated.
|
* If one of those changes, the view models will be updated.
|
||||||
*/
|
*/
|
||||||
public constructor(
|
public constructor(
|
||||||
protected DS: DataStoreService,
|
protected DS: DataStoreService,
|
||||||
protected collectionStringModelMapperService: CollectionStringMapperService,
|
protected collectionStringMapperService: CollectionStringMapperService,
|
||||||
|
protected viewModelStoreService: ViewModelStoreService,
|
||||||
protected baseModelCtor: ModelConstructor<M>,
|
protected baseModelCtor: ModelConstructor<M>,
|
||||||
protected depsModelCtors?: ModelConstructor<BaseModel>[]
|
protected depsModelCtors?: ModelConstructor<BaseModel>[]
|
||||||
) {
|
) {
|
||||||
super();
|
super();
|
||||||
this.setup();
|
this._name = baseModelCtor.name;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected setup(): void {
|
public onAfterAppsLoaded(): void {
|
||||||
// Populate the local viewModelStore with ViewModel Objects.
|
// Populate the local viewModelStore with ViewModel Objects.
|
||||||
this.DS.getAll(this.baseModelCtor).forEach((model: M) => {
|
this.DS.getAll(this.baseModelCtor).forEach((model: M) => {
|
||||||
this.viewModelStore[model.id] = this.createViewModel(model);
|
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
|
// 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) {
|
if (model instanceof this.baseModelCtor) {
|
||||||
// Add new and updated motions to the viewModelStore
|
// Add new and updated motions to the viewModelStore
|
||||||
this.viewModelStore[model.id] = this.createViewModel(model as M);
|
this.viewModelStore[model.id] = this.createViewModel(model as M);
|
||||||
this.updateAllObservables(model.id);
|
this.updateAllObservables(model.id);
|
||||||
} else if (this.depsModelCtors) {
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (this.depsModelCtors) {
|
||||||
|
this.DS.secondaryModelChangeSubject.subscribe(model => {
|
||||||
const dependencyChanged: boolean = this.depsModelCtors.some(ctor => {
|
const dependencyChanged: boolean = this.depsModelCtors.some(ctor => {
|
||||||
return model instanceof ctor;
|
return model instanceof ctor;
|
||||||
});
|
});
|
||||||
if (dependencyChanged) {
|
if (dependencyChanged) {
|
||||||
|
const viewModel = this.viewModelStoreService.get(model.collectionString, model.id);
|
||||||
|
|
||||||
// if an domain object we need was added or changed, update viewModelStore
|
// if an domain object we need was added or changed, update viewModelStore
|
||||||
this.getViewModelList().forEach(viewModel => {
|
this.getViewModelList().forEach(ownViewModel => {
|
||||||
viewModel.updateValues(model);
|
ownViewModel.updateDependencies(viewModel);
|
||||||
});
|
});
|
||||||
this.updateAllObservables(model.id);
|
this.updateAllObservables(model.id);
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
});
|
}
|
||||||
|
|
||||||
// Watch the Observables for deleting
|
// 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 => {
|
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];
|
delete this.viewModelStore[model.id];
|
||||||
this.updateAllObservables(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 {
|
protected updateViewModelObservable(id: number): void {
|
||||||
if (this.viewModelSubjects[id]) {
|
if (this.viewModelSubjects[id]) {
|
||||||
this.viewModelSubjects[id].next(this.viewModelStore[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 { CollectionStringMapperService } from '../../core-services/collectionStringMapper.service';
|
||||||
import { ChatMessage } from 'app/shared/models/core/chat-message';
|
import { ChatMessage } from 'app/shared/models/core/chat-message';
|
||||||
import { ViewChatMessage } from 'app/site/common/models/view-chatmessage';
|
import { ViewChatMessage } from 'app/site/common/models/view-chatmessage';
|
||||||
|
import { ViewModelStoreService } from 'app/core/core-services/view-model-store.service';
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
})
|
})
|
||||||
export class ChatMessageRepositoryService extends BaseRepository<ViewChatMessage, ChatMessage> {
|
export class ChatMessageRepositoryService extends BaseRepository<ViewChatMessage, ChatMessage> {
|
||||||
public constructor(DS: DataStoreService, mapperService: CollectionStringMapperService) {
|
public constructor(
|
||||||
super(DS, mapperService, ChatMessage);
|
DS: DataStoreService,
|
||||||
|
mapperService: CollectionStringMapperService,
|
||||||
|
viewModelStoreService: ViewModelStoreService
|
||||||
|
) {
|
||||||
|
super(DS, mapperService, viewModelStoreService, ChatMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected createViewModel(message: ChatMessage): ViewChatMessage {
|
protected createViewModel(message: ChatMessage): ViewChatMessage {
|
||||||
|
@ -1,14 +1,16 @@
|
|||||||
import { Injectable } from '@angular/core';
|
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 { 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 { DataStoreService } from 'app/core/core-services/data-store.service';
|
||||||
import { ConstantsService } from 'app/core/ui-services/constants.service';
|
import { ConstantsService } from 'app/core/ui-services/constants.service';
|
||||||
import { HttpService } from 'app/core/core-services/http.service';
|
import { HttpService } from 'app/core/core-services/http.service';
|
||||||
import { Identifiable } from 'app/shared/models/base/identifiable';
|
import { Identifiable } from 'app/shared/models/base/identifiable';
|
||||||
import { CollectionStringMapperService } from 'app/core/core-services/collectionStringMapper.service';
|
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.
|
* Holds a single config item.
|
||||||
@ -95,10 +97,11 @@ export class ConfigRepositoryService extends BaseRepository<ViewConfig, Config>
|
|||||||
public constructor(
|
public constructor(
|
||||||
DS: DataStoreService,
|
DS: DataStoreService,
|
||||||
mapperService: CollectionStringMapperService,
|
mapperService: CollectionStringMapperService,
|
||||||
|
viewModelStoreService: ViewModelStoreService,
|
||||||
private constantsService: ConstantsService,
|
private constantsService: ConstantsService,
|
||||||
private http: HttpService
|
private http: HttpService
|
||||||
) {
|
) {
|
||||||
super(DS, mapperService, Config);
|
super(DS, mapperService, viewModelStoreService, Config);
|
||||||
|
|
||||||
this.constantsService.get('OpenSlidesConfigVariables').subscribe(constant => {
|
this.constantsService.get('OpenSlidesConfigVariables').subscribe(constant => {
|
||||||
this.createConfigStructure(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
|
* Overwritten setup. Does only care about the custom list observable and inserts changed configs into the
|
||||||
* config group structure.
|
* config group structure.
|
||||||
*/
|
*/
|
||||||
protected setup(): void {
|
public onAfterAppsLoaded(): void {
|
||||||
if (!this.configListSubject) {
|
if (!this.configListSubject) {
|
||||||
this.configListSubject = new BehaviorSubject<ConfigGroup[]>(null);
|
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 { HttpService } from 'app/core/core-services/http.service';
|
||||||
import { ViewHistory } from 'app/site/history/models/view-history';
|
import { ViewHistory } from 'app/site/history/models/view-history';
|
||||||
import { TimeTravelService } from 'app/core/core-services/time-travel.service';
|
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.
|
* Repository for the history.
|
||||||
@ -31,10 +32,11 @@ export class HistoryRepositoryService extends BaseRepository<ViewHistory, Histor
|
|||||||
public constructor(
|
public constructor(
|
||||||
DS: DataStoreService,
|
DS: DataStoreService,
|
||||||
mapperService: CollectionStringMapperService,
|
mapperService: CollectionStringMapperService,
|
||||||
|
viewModelStoreService: ViewModelStoreService,
|
||||||
private httpService: HttpService,
|
private httpService: HttpService,
|
||||||
private timeTravel: TimeTravelService
|
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
|
* @returns the ListTitle or null if the model was deleted already
|
||||||
*/
|
*/
|
||||||
public getOldModelInfo(collectionString: string, id: number): string {
|
public getOldModelInfo(collectionString: string, id: number): string {
|
||||||
const oldModel: BaseModel = this.DS.get(collectionString, id);
|
const model = this.viewModelStoreService.get(collectionString, id);
|
||||||
if (oldModel) {
|
if (model) {
|
||||||
return oldModel.getListTitle();
|
return model.getListTitle();
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -85,7 +86,7 @@ export class HistoryRepositoryService extends BaseRepository<ViewHistory, Histor
|
|||||||
* @return a new ViewHistory object
|
* @return a new ViewHistory object
|
||||||
*/
|
*/
|
||||||
public createViewModel(history: History): ViewHistory {
|
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);
|
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 { DataSendService } from 'app/core/core-services/data-send.service';
|
||||||
import { HttpService } from 'app/core/core-services/http.service';
|
import { HttpService } from 'app/core/core-services/http.service';
|
||||||
import { HttpHeaders } from '@angular/common/http';
|
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
|
* Repository for MediaFiles
|
||||||
@ -28,10 +30,11 @@ export class MediafileRepositoryService extends BaseRepository<ViewMediafile, Me
|
|||||||
public constructor(
|
public constructor(
|
||||||
DS: DataStoreService,
|
DS: DataStoreService,
|
||||||
mapperService: CollectionStringMapperService,
|
mapperService: CollectionStringMapperService,
|
||||||
|
viewModelStoreService: ViewModelStoreService,
|
||||||
private dataSend: DataSendService,
|
private dataSend: DataSendService,
|
||||||
private httpService: HttpService
|
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
|
* @returns a new mediafile ViewModel
|
||||||
*/
|
*/
|
||||||
public createViewModel(file: Mediafile): ViewMediafile {
|
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);
|
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 { HttpService } from '../../core-services/http.service';
|
||||||
import { Identifiable } from 'app/shared/models/base/identifiable';
|
import { Identifiable } from 'app/shared/models/base/identifiable';
|
||||||
import { CollectionStringMapperService } from '../../core-services/collectionStringMapper.service';
|
import { CollectionStringMapperService } from '../../core-services/collectionStringMapper.service';
|
||||||
|
import { ViewModelStoreService } from 'app/core/core-services/view-model-store.service';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Repository Services for Categories
|
* Repository Services for Categories
|
||||||
@ -37,10 +38,11 @@ export class CategoryRepositoryService extends BaseRepository<ViewCategory, Cate
|
|||||||
public constructor(
|
public constructor(
|
||||||
protected DS: DataStoreService,
|
protected DS: DataStoreService,
|
||||||
mapperService: CollectionStringMapperService,
|
mapperService: CollectionStringMapperService,
|
||||||
|
viewModelStoreService: ViewModelStoreService,
|
||||||
private dataSend: DataSendService,
|
private dataSend: DataSendService,
|
||||||
private httpService: HttpService
|
private httpService: HttpService
|
||||||
) {
|
) {
|
||||||
super(DS, mapperService, Category);
|
super(DS, mapperService, viewModelStoreService, Category);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected createViewModel(category: Category): ViewCategory {
|
protected createViewModel(category: Category): ViewCategory {
|
||||||
|
@ -3,16 +3,17 @@ import { Injectable } from '@angular/core';
|
|||||||
import { Observable } from 'rxjs';
|
import { Observable } from 'rxjs';
|
||||||
import { map } from 'rxjs/operators';
|
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 { User } from 'app/shared/models/users/user';
|
||||||
import { Category } from 'app/shared/models/motions/category';
|
import { Category } from 'app/shared/models/motions/category';
|
||||||
import { Workflow } from 'app/shared/models/motions/workflow';
|
import { Workflow } from 'app/shared/models/motions/workflow';
|
||||||
import { BaseRepository } from '../base-repository';
|
import { BaseRepository } from '../base-repository';
|
||||||
import { DataStoreService } from '../../core-services/data-store.service';
|
import { DataStoreService } from 'app/core/core-services/data-store.service';
|
||||||
import { MotionChangeReco } from 'app/shared/models/motions/motion-change-reco';
|
import { MotionChangeRecommendation } from 'app/shared/models/motions/motion-change-reco';
|
||||||
import { ViewChangeReco } from 'app/site/motions/models/view-change-reco';
|
import { ViewMotionChangeRecommendation } from 'app/site/motions/models/view-change-recommendation';
|
||||||
import { Identifiable } from 'app/shared/models/base/identifiable';
|
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
|
* Repository Services for change recommendations
|
||||||
@ -27,7 +28,10 @@ import { CollectionStringMapperService } from '../../core-services/collectionStr
|
|||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
})
|
})
|
||||||
export class ChangeRecommendationRepositoryService extends BaseRepository<ViewChangeReco, MotionChangeReco> {
|
export class ChangeRecommendationRepositoryService extends BaseRepository<
|
||||||
|
ViewMotionChangeRecommendation,
|
||||||
|
MotionChangeRecommendation
|
||||||
|
> {
|
||||||
/**
|
/**
|
||||||
* Creates a MotionRepository
|
* Creates a MotionRepository
|
||||||
*
|
*
|
||||||
@ -41,18 +45,19 @@ export class ChangeRecommendationRepositoryService extends BaseRepository<ViewCh
|
|||||||
public constructor(
|
public constructor(
|
||||||
DS: DataStoreService,
|
DS: DataStoreService,
|
||||||
mapperService: CollectionStringMapperService,
|
mapperService: CollectionStringMapperService,
|
||||||
|
viewModelStoreService: ViewModelStoreService,
|
||||||
private dataSend: DataSendService
|
private dataSend: DataSendService
|
||||||
) {
|
) {
|
||||||
super(DS, mapperService, MotionChangeReco, [Category, User, Workflow]);
|
super(DS, mapperService, viewModelStoreService, MotionChangeRecommendation, [Category, User, Workflow]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a change recommendation
|
* Creates a change recommendation
|
||||||
* Creates a (real) change recommendation and delegates it to the {@link DataSendService}
|
* 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);
|
return await this.dataSend.createModel(changeReco);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -61,17 +66,17 @@ export class ChangeRecommendationRepositoryService extends BaseRepository<ViewCh
|
|||||||
* @param view
|
* @param view
|
||||||
* @returns The id of the created change recommendation
|
* @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);
|
return await this.dataSend.createModel(view.changeRecommendation);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates this view wrapper based on an actual Change Recommendation model
|
* Creates this view wrapper based on an actual Change Recommendation model
|
||||||
*
|
*
|
||||||
* @param {MotionChangeReco} model
|
* @param {MotionChangeRecommendation} model
|
||||||
*/
|
*/
|
||||||
protected createViewModel(model: MotionChangeReco): ViewChangeReco {
|
protected createViewModel(model: MotionChangeRecommendation): ViewMotionChangeRecommendation {
|
||||||
return new ViewChangeReco(model);
|
return new ViewMotionChangeRecommendation(model);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -79,9 +84,9 @@ export class ChangeRecommendationRepositoryService extends BaseRepository<ViewCh
|
|||||||
*
|
*
|
||||||
* Extract the change recommendation out of the viewModel and delegate
|
* Extract the change recommendation out of the viewModel and delegate
|
||||||
* to {@link DataSendService}
|
* 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);
|
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
|
* Updates a (real) change recommendation with patched data and delegate it
|
||||||
* to the {@link DataSendService}
|
* to the {@link DataSendService}
|
||||||
*
|
*
|
||||||
* @param {Partial<MotionChangeReco>} update the form data containing the update values
|
* @param {Partial<MotionChangeRecommendation>} 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 {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;
|
const changeReco = viewModel.changeRecommendation;
|
||||||
changeReco.patchValues(update);
|
changeReco.patchValues(update);
|
||||||
await this.dataSend.partialUpdateModel(changeReco);
|
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
|
* 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(
|
return this.viewModelListSubject.asObservable().pipe(
|
||||||
map((recos: ViewChangeReco[]) => {
|
map((recos: ViewMotionChangeRecommendation[]) => {
|
||||||
return recos.filter(reco => reco.motion_id === motion_id);
|
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
|
* @param motionId the id of the target motion
|
||||||
* @returns the array of change recommendations to the motions.
|
* @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);
|
return this.getViewModelList().filter(reco => reco.motion_id === motion_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets a change recommendation to accepted.
|
* 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;
|
const changeReco = change.changeRecommendation;
|
||||||
changeReco.patchValues({
|
changeReco.patchValues({
|
||||||
rejected: false
|
rejected: false
|
||||||
@ -137,9 +145,9 @@ export class ChangeRecommendationRepositoryService extends BaseRepository<ViewCh
|
|||||||
/**
|
/**
|
||||||
* Sets a change recommendation to rejected.
|
* 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;
|
const changeReco = change.changeRecommendation;
|
||||||
changeReco.patchValues({
|
changeReco.patchValues({
|
||||||
rejected: true
|
rejected: true
|
||||||
@ -150,10 +158,10 @@ export class ChangeRecommendationRepositoryService extends BaseRepository<ViewCh
|
|||||||
/**
|
/**
|
||||||
* Sets if a change recommendation is internal (for the administrators) or not.
|
* Sets if a change recommendation is internal (for the administrators) or not.
|
||||||
*
|
*
|
||||||
* @param {ViewChangeReco} change
|
* @param {ViewMotionChangeRecommendation} change
|
||||||
* @param {boolean} internal
|
* @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;
|
const changeReco = change.changeRecommendation;
|
||||||
changeReco.patchValues({
|
changeReco.patchValues({
|
||||||
internal: internal
|
internal: internal
|
||||||
|
@ -14,6 +14,9 @@ import { MotionBlock } from 'app/shared/models/motions/motion-block';
|
|||||||
import { MotionRepositoryService } from './motion-repository.service';
|
import { MotionRepositoryService } from './motion-repository.service';
|
||||||
import { ViewMotion } from 'app/site/motions/models/view-motion';
|
import { ViewMotion } from 'app/site/motions/models/view-motion';
|
||||||
import { ViewMotionBlock } from 'app/site/motions/models/view-motion-block';
|
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
|
* Repository service for motion blocks
|
||||||
@ -34,11 +37,12 @@ export class MotionBlockRepositoryService extends BaseRepository<ViewMotionBlock
|
|||||||
public constructor(
|
public constructor(
|
||||||
DS: DataStoreService,
|
DS: DataStoreService,
|
||||||
mapperService: CollectionStringMapperService,
|
mapperService: CollectionStringMapperService,
|
||||||
|
viewModelStoreService: ViewModelStoreService,
|
||||||
private dataSend: DataSendService,
|
private dataSend: DataSendService,
|
||||||
private motionRepo: MotionRepositoryService,
|
private motionRepo: MotionRepositoryService,
|
||||||
private httpService: HttpService
|
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
|
* @returns a new ViewMotionBlock
|
||||||
*/
|
*/
|
||||||
protected createViewModel(block: MotionBlock): 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 { Identifiable } from 'app/shared/models/base/identifiable';
|
||||||
import { CollectionStringMapperService } from '../../core-services/collectionStringMapper.service';
|
import { CollectionStringMapperService } from '../../core-services/collectionStringMapper.service';
|
||||||
import { HttpService } from 'app/core/core-services/http.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
|
* Repository Services for Categories
|
||||||
@ -38,12 +40,13 @@ export class MotionCommentSectionRepositoryService extends BaseRepository<
|
|||||||
* @param http Service to handle direct http-communication
|
* @param http Service to handle direct http-communication
|
||||||
*/
|
*/
|
||||||
public constructor(
|
public constructor(
|
||||||
|
DS: DataStoreService,
|
||||||
mapperService: CollectionStringMapperService,
|
mapperService: CollectionStringMapperService,
|
||||||
protected DS: DataStoreService,
|
viewModelStoreService: ViewModelStoreService,
|
||||||
private dataSend: DataSendService,
|
private dataSend: DataSendService,
|
||||||
private http: HttpService
|
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
|
* @returns the View Model representation of the MotionCommentSection
|
||||||
*/
|
*/
|
||||||
protected createViewModel(section: MotionCommentSection): ViewMotionCommentSection {
|
protected createViewModel(section: MotionCommentSection): ViewMotionCommentSection {
|
||||||
const read_groups = this.DS.getMany(Group, section.read_groups_id);
|
const readGroups = this.viewModelStoreService.getMany(ViewGroup, section.read_groups_id);
|
||||||
const write_groups = this.DS.getMany(Group, section.write_groups_id);
|
const writeGroups = this.viewModelStoreService.getMany(ViewGroup, section.write_groups_id);
|
||||||
return new ViewMotionCommentSection(section, read_groups, write_groups);
|
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 { Mediafile } from 'app/shared/models/mediafiles/mediafile';
|
||||||
import { Motion } from 'app/shared/models/motions/motion';
|
import { Motion } from 'app/shared/models/motions/motion';
|
||||||
import { MotionBlock } from 'app/shared/models/motions/motion-block';
|
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 { MotionPoll } from 'app/shared/models/motions/motion-poll';
|
||||||
import { OSTreeSortEvent } from 'app/shared/components/sorting-tree/sorting-tree.component';
|
import { OSTreeSortEvent } from 'app/shared/components/sorting-tree/sorting-tree.component';
|
||||||
import { PersonalNoteService } from '../../ui-services/personal-note.service';
|
import { PersonalNoteService } from '../../ui-services/personal-note.service';
|
||||||
import { TreeService } from 'app/core/ui-services/tree.service';
|
import { TreeService } from 'app/core/ui-services/tree.service';
|
||||||
import { User } from '../../../shared/models/users/user';
|
import { User } from 'app/shared/models/users/user';
|
||||||
import { ViewChangeReco } from '../../../site/motions/models/view-change-reco';
|
import { ViewMotionChangeRecommendation } from 'app/site/motions/models/view-change-recommendation';
|
||||||
import { ViewMotionAmendedParagraph } from '../../../site/motions/models/view-motion-amended-paragraph';
|
import { ViewMotionAmendedParagraph } from 'app/site/motions/models/view-motion-amended-paragraph';
|
||||||
import { ViewUnifiedChange } from '../../../site/motions/models/view-unified-change';
|
import { ViewUnifiedChange } from 'app/site/motions/models/view-unified-change';
|
||||||
import { ViewStatuteParagraph } from '../../../site/motions/models/view-statute-paragraph';
|
import { ViewStatuteParagraph } from 'app/site/motions/models/view-statute-paragraph';
|
||||||
import { Workflow } from '../../../shared/models/motions/workflow';
|
import { Workflow } from 'app/shared/models/motions/workflow';
|
||||||
import { WorkflowState } from '../../../shared/models/motions/workflow-state';
|
import { WorkflowState } from 'app/shared/models/motions/workflow-state';
|
||||||
import { Tag } from 'app/shared/models/core/tag';
|
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)
|
* Repository Services for motions (and potentially categories)
|
||||||
@ -64,6 +72,7 @@ export class MotionRepositoryService extends BaseRepository<ViewMotion, Motion>
|
|||||||
public constructor(
|
public constructor(
|
||||||
DS: DataStoreService,
|
DS: DataStoreService,
|
||||||
mapperService: CollectionStringMapperService,
|
mapperService: CollectionStringMapperService,
|
||||||
|
viewModelStoreService: ViewModelStoreService,
|
||||||
private dataSend: DataSendService,
|
private dataSend: DataSendService,
|
||||||
private httpService: HttpService,
|
private httpService: HttpService,
|
||||||
private readonly lineNumbering: LinenumberingService,
|
private readonly lineNumbering: LinenumberingService,
|
||||||
@ -72,7 +81,15 @@ export class MotionRepositoryService extends BaseRepository<ViewMotion, Motion>
|
|||||||
private personalNoteService: PersonalNoteService,
|
private personalNoteService: PersonalNoteService,
|
||||||
private translate: TranslateService
|
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
|
* @param motion blank motion domain object
|
||||||
*/
|
*/
|
||||||
protected createViewModel(motion: Motion): ViewMotion {
|
protected createViewModel(motion: Motion): ViewMotion {
|
||||||
const category = this.DS.get(Category, motion.category_id);
|
const category = this.viewModelStoreService.get(ViewCategory, motion.category_id);
|
||||||
const submitters = this.DS.getMany(User, motion.submitterIds);
|
const submitters = this.viewModelStoreService.getMany(ViewUser, motion.submitterIds);
|
||||||
const supporters = this.DS.getMany(User, motion.supporters_id);
|
const supporters = this.viewModelStoreService.getMany(ViewUser, motion.supporters_id);
|
||||||
const workflow = this.DS.get(Workflow, motion.workflow_id);
|
const workflow = this.viewModelStoreService.get(ViewWorkflow, motion.workflow_id);
|
||||||
const item = this.DS.get(Item, motion.agenda_item_id);
|
const item = this.viewModelStoreService.get(ViewItem, motion.agenda_item_id);
|
||||||
const block = this.DS.get(MotionBlock, motion.motion_block_id);
|
const block = this.viewModelStoreService.get(ViewMotionBlock, motion.motion_block_id);
|
||||||
const attachments = this.DS.getMany(Mediafile, motion.attachments_id);
|
const attachments = this.viewModelStoreService.getMany(ViewMediafile, motion.attachments_id);
|
||||||
const tags = this.DS.getMany(Tag, motion.tags_id);
|
const tags = this.viewModelStoreService.getMany(ViewTag, motion.tags_id);
|
||||||
const parent = this.DS.get(Motion, motion.parent_id);
|
const parent = this.viewModelStoreService.get(ViewMotion, motion.parent_id);
|
||||||
let state: WorkflowState = null;
|
let state: WorkflowState = null;
|
||||||
if (workflow) {
|
if (workflow) {
|
||||||
state = workflow.getStateById(motion.state_id);
|
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 viewMotion The motion to change the submitters from
|
||||||
* @param submitters The submitters to set
|
* @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 = {
|
const requestData = {
|
||||||
motions: [
|
motions: [
|
||||||
{
|
{
|
||||||
@ -525,8 +542,8 @@ export class MotionRepositoryService extends BaseRepository<ViewMotion, Motion>
|
|||||||
motionId: number,
|
motionId: number,
|
||||||
lineRange: LineRange,
|
lineRange: LineRange,
|
||||||
lineLength: number
|
lineLength: number
|
||||||
): ViewChangeReco {
|
): ViewMotionChangeRecommendation {
|
||||||
const changeReco = new MotionChangeReco();
|
const changeReco = new MotionChangeRecommendation();
|
||||||
changeReco.line_from = lineRange.from;
|
changeReco.line_from = lineRange.from;
|
||||||
changeReco.line_to = lineRange.to;
|
changeReco.line_to = lineRange.to;
|
||||||
changeReco.type = ModificationType.TYPE_REPLACEMENT;
|
changeReco.type = ModificationType.TYPE_REPLACEMENT;
|
||||||
@ -534,7 +551,7 @@ export class MotionRepositoryService extends BaseRepository<ViewMotion, Motion>
|
|||||||
changeReco.rejected = false;
|
changeReco.rejected = false;
|
||||||
changeReco.motion_id = motionId;
|
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 { StatuteParagraph } from 'app/shared/models/motions/statute-paragraph';
|
||||||
import { Identifiable } from 'app/shared/models/base/identifiable';
|
import { Identifiable } from 'app/shared/models/base/identifiable';
|
||||||
import { CollectionStringMapperService } from '../../core-services/collectionStringMapper.service';
|
import { CollectionStringMapperService } from '../../core-services/collectionStringMapper.service';
|
||||||
|
import { ViewModelStoreService } from 'app/core/core-services/view-model-store.service';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Repository Services for statute paragraphs
|
* Repository Services for statute paragraphs
|
||||||
@ -31,9 +32,10 @@ export class StatuteParagraphRepositoryService extends BaseRepository<ViewStatut
|
|||||||
public constructor(
|
public constructor(
|
||||||
DS: DataStoreService,
|
DS: DataStoreService,
|
||||||
mapperService: CollectionStringMapperService,
|
mapperService: CollectionStringMapperService,
|
||||||
|
viewModelStoreService: ViewModelStoreService,
|
||||||
private dataSend: DataSendService
|
private dataSend: DataSendService
|
||||||
) {
|
) {
|
||||||
super(DS, mapperService, StatuteParagraph);
|
super(DS, mapperService, viewModelStoreService, StatuteParagraph);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected createViewModel(statuteParagraph: StatuteParagraph): ViewStatuteParagraph {
|
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 { WorkflowState } from 'app/shared/models/motions/workflow-state';
|
||||||
import { ViewMotion } from 'app/site/motions/models/view-motion';
|
import { ViewMotion } from 'app/site/motions/models/view-motion';
|
||||||
import { HttpService } from 'app/core/core-services/http.service';
|
import { HttpService } from 'app/core/core-services/http.service';
|
||||||
|
import { ViewModelStoreService } from 'app/core/core-services/view-model-store.service';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Repository Services for Categories
|
* Repository Services for Categories
|
||||||
@ -40,12 +41,13 @@ export class WorkflowRepositoryService extends BaseRepository<ViewWorkflow, Work
|
|||||||
* @param httpService HttpService
|
* @param httpService HttpService
|
||||||
*/
|
*/
|
||||||
public constructor(
|
public constructor(
|
||||||
protected DS: DataStoreService,
|
DS: DataStoreService,
|
||||||
mapperService: CollectionStringMapperService,
|
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 { CollectionStringMapperService } from '../../core-services/collectionStringMapper.service';
|
||||||
import { ViewCountdown } from 'app/site/projector/models/view-countdown';
|
import { ViewCountdown } from 'app/site/projector/models/view-countdown';
|
||||||
import { Countdown } from 'app/shared/models/core/countdown';
|
import { Countdown } from 'app/shared/models/core/countdown';
|
||||||
|
import { ViewModelStoreService } from 'app/core/core-services/view-model-store.service';
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
@ -14,9 +15,10 @@ export class CountdownRepositoryService extends BaseRepository<ViewCountdown, Co
|
|||||||
public constructor(
|
public constructor(
|
||||||
DS: DataStoreService,
|
DS: DataStoreService,
|
||||||
mapperService: CollectionStringMapperService,
|
mapperService: CollectionStringMapperService,
|
||||||
|
viewModelStoreService: ViewModelStoreService,
|
||||||
private dataSend: DataSendService
|
private dataSend: DataSendService
|
||||||
) {
|
) {
|
||||||
super(DS, mapperService, Countdown);
|
super(DS, mapperService, viewModelStoreService, Countdown);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected createViewModel(countdown: Countdown): ViewCountdown {
|
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 { ViewProjector } from 'app/site/projector/models/view-projector';
|
||||||
import { Projector } from 'app/shared/models/core/projector';
|
import { Projector } from 'app/shared/models/core/projector';
|
||||||
import { HttpService } from 'app/core/core-services/http.service';
|
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.
|
* Directions for scale and scroll requests.
|
||||||
@ -36,10 +37,11 @@ export class ProjectorRepositoryService extends BaseRepository<ViewProjector, Pr
|
|||||||
public constructor(
|
public constructor(
|
||||||
DS: DataStoreService,
|
DS: DataStoreService,
|
||||||
mapperService: CollectionStringMapperService,
|
mapperService: CollectionStringMapperService,
|
||||||
|
viewModelStoreService: ViewModelStoreService,
|
||||||
private dataSend: DataSendService,
|
private dataSend: DataSendService,
|
||||||
private http: HttpService
|
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 { CollectionStringMapperService } from '../../core-services/collectionStringMapper.service';
|
||||||
import { ProjectorMessage } from 'app/shared/models/core/projector-message';
|
import { ProjectorMessage } from 'app/shared/models/core/projector-message';
|
||||||
import { ViewProjectorMessage } from 'app/site/projector/models/view-projectormessage';
|
import { ViewProjectorMessage } from 'app/site/projector/models/view-projectormessage';
|
||||||
|
import { ViewModelStoreService } from 'app/core/core-services/view-model-store.service';
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
})
|
})
|
||||||
export class ProjectorMessageRepositoryService extends BaseRepository<ViewProjectorMessage, ProjectorMessage> {
|
export class ProjectorMessageRepositoryService extends BaseRepository<ViewProjectorMessage, ProjectorMessage> {
|
||||||
public constructor(DS: DataStoreService, mapperService: CollectionStringMapperService) {
|
public constructor(
|
||||||
super(DS, mapperService, ProjectorMessage);
|
DS: DataStoreService,
|
||||||
|
mapperService: CollectionStringMapperService,
|
||||||
|
viewModelStoreService: ViewModelStoreService
|
||||||
|
) {
|
||||||
|
super(DS, mapperService, viewModelStoreService, ProjectorMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected createViewModel(message: ProjectorMessage): ViewProjectorMessage {
|
protected createViewModel(message: ProjectorMessage): ViewProjectorMessage {
|
||||||
|
@ -7,6 +7,7 @@ import { DataStoreService } from '../../core-services/data-store.service';
|
|||||||
import { BaseRepository } from '../base-repository';
|
import { BaseRepository } from '../base-repository';
|
||||||
import { Identifiable } from 'app/shared/models/base/identifiable';
|
import { Identifiable } from 'app/shared/models/base/identifiable';
|
||||||
import { CollectionStringMapperService } from '../../core-services/collectionStringMapper.service';
|
import { CollectionStringMapperService } from '../../core-services/collectionStringMapper.service';
|
||||||
|
import { ViewModelStoreService } from 'app/core/core-services/view-model-store.service';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Repository Services for Tags
|
* Repository Services for Tags
|
||||||
@ -34,9 +35,10 @@ export class TagRepositoryService extends BaseRepository<ViewTag, Tag> {
|
|||||||
public constructor(
|
public constructor(
|
||||||
protected DS: DataStoreService,
|
protected DS: DataStoreService,
|
||||||
mapperService: CollectionStringMapperService,
|
mapperService: CollectionStringMapperService,
|
||||||
|
viewModelStoreService: ViewModelStoreService,
|
||||||
private dataSend: DataSendService
|
private dataSend: DataSendService
|
||||||
) {
|
) {
|
||||||
super(DS, mapperService, Tag);
|
super(DS, mapperService, viewModelStoreService, Tag);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected createViewModel(tag: Tag): ViewTag {
|
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 { Group } from 'app/shared/models/users/group';
|
||||||
import { Identifiable } from 'app/shared/models/base/identifiable';
|
import { Identifiable } from 'app/shared/models/base/identifiable';
|
||||||
import { ViewGroup } from 'app/site/users/models/view-group';
|
import { ViewGroup } from 'app/site/users/models/view-group';
|
||||||
|
import { ViewModelStoreService } from 'app/core/core-services/view-model-store.service';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Shape of a permission
|
* Shape of a permission
|
||||||
@ -49,10 +50,11 @@ export class GroupRepositoryService extends BaseRepository<ViewGroup, Group> {
|
|||||||
public constructor(
|
public constructor(
|
||||||
DS: DataStoreService,
|
DS: DataStoreService,
|
||||||
mapperService: CollectionStringMapperService,
|
mapperService: CollectionStringMapperService,
|
||||||
|
viewModelStoreService: ViewModelStoreService,
|
||||||
private dataSend: DataSendService,
|
private dataSend: DataSendService,
|
||||||
private constants: ConstantsService
|
private constants: ConstantsService
|
||||||
) {
|
) {
|
||||||
super(DS, mapperService, Group);
|
super(DS, mapperService, viewModelStoreService, Group);
|
||||||
this.sortPermsPerApp();
|
this.sortPermsPerApp();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,34 +5,40 @@ import { BaseRepository } from '../base-repository';
|
|||||||
import { CollectionStringMapperService } from '../../core-services/collectionStringMapper.service';
|
import { CollectionStringMapperService } from '../../core-services/collectionStringMapper.service';
|
||||||
import { PersonalNote } from 'app/shared/models/users/personal-note';
|
import { PersonalNote } from 'app/shared/models/users/personal-note';
|
||||||
import { Identifiable } from 'app/shared/models/base/identifiable';
|
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({
|
@Injectable({
|
||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
})
|
})
|
||||||
export class PersonalNoteRepositoryService extends BaseRepository<any, PersonalNote> {
|
export class PersonalNoteRepositoryService extends BaseRepository<ViewPersonalNote, PersonalNote> {
|
||||||
/**
|
/**
|
||||||
* @param DS The DataStore
|
* @param DS The DataStore
|
||||||
* @param mapperService Maps collection strings to classes
|
* @param mapperService Maps collection strings to classes
|
||||||
*/
|
*/
|
||||||
public constructor(protected DS: DataStoreService, mapperService: CollectionStringMapperService) {
|
public constructor(
|
||||||
super(DS, mapperService, PersonalNote);
|
DS: DataStoreService,
|
||||||
|
mapperService: CollectionStringMapperService,
|
||||||
|
viewModelStoreService: ViewModelStoreService
|
||||||
|
) {
|
||||||
|
super(DS, mapperService, viewModelStoreService, PersonalNote);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected createViewModel(personalNote: PersonalNote): any {
|
protected createViewModel(personalNote: PersonalNote): ViewPersonalNote {
|
||||||
return {};
|
return new ViewPersonalNote();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async create(personalNote: PersonalNote): Promise<Identifiable> {
|
public async create(personalNote: PersonalNote): Promise<Identifiable> {
|
||||||
throw new Error('TODO');
|
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');
|
throw new Error('TODO');
|
||||||
}
|
}
|
||||||
|
|
||||||
public async delete(viewPersonalNote: any): Promise<void> {
|
public async delete(viewPersonalNote: ViewPersonalNote): Promise<void> {
|
||||||
throw new Error('TODO');
|
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 { HttpService } from 'app/core/core-services/http.service';
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
import { environment } from '../../../../environments/environment';
|
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.
|
* type for determining the user name from a string during import.
|
||||||
@ -38,12 +40,13 @@ export class UserRepositoryService extends BaseRepository<ViewUser, User> {
|
|||||||
public constructor(
|
public constructor(
|
||||||
DS: DataStoreService,
|
DS: DataStoreService,
|
||||||
mapperService: CollectionStringMapperService,
|
mapperService: CollectionStringMapperService,
|
||||||
|
viewModelStoreService: ViewModelStoreService,
|
||||||
private dataSend: DataSendService,
|
private dataSend: DataSendService,
|
||||||
private translate: TranslateService,
|
private translate: TranslateService,
|
||||||
private httpService: HttpService,
|
private httpService: HttpService,
|
||||||
private configService: ConfigService
|
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 {
|
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);
|
return new ViewUser(user, groups);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -218,20 +221,9 @@ export class UserRepositoryService extends BaseRepository<ViewUser, User> {
|
|||||||
* @returns all users matching that name
|
* @returns all users matching that name
|
||||||
*/
|
*/
|
||||||
public getUsersByName(name: string): ViewUser[] {
|
public getUsersByName(name: string): ViewUser[] {
|
||||||
const results: ViewUser[] = [];
|
return this.getViewModelList().filter(user => {
|
||||||
const users = this.DS.getAll(User).filter(user => {
|
return user.full_name === name || user.short_name === name || user.number === name;
|
||||||
if (user.full_name === name || user.short_name === name) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (user.number === name) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
});
|
});
|
||||||
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
|
* @returns all users matching that number
|
||||||
*/
|
*/
|
||||||
public getUsersByNumber(number: string): ViewUser[] {
|
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.
|
// 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.
|
* Watches for changes in the personal note model.
|
||||||
*/
|
*/
|
||||||
public constructor(private operator: OperatorService, private DS: DataStoreService, private http: HttpService) {
|
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 => {
|
this.DS.changeObservable.subscribe(model => {
|
||||||
if (model instanceof PersonalNote) {
|
if (model instanceof PersonalNote) {
|
||||||
this.updatePersonalNoteObject();
|
this.updatePersonalNoteObject();
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { BaseModel } from '../../shared/models/base/base-model';
|
import { Searchable } from '../../site/base/searchable';
|
||||||
import { DataStoreService } from '../core-services/data-store.service';
|
import { BaseViewModel } from 'app/site/base/base-view-model';
|
||||||
import { Searchable } from '../../shared/models/base/searchable';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The representation every searchable model should use to represent their data.
|
* The representation every searchable model should use to represent their data.
|
||||||
@ -46,7 +45,7 @@ export interface SearchResult {
|
|||||||
/**
|
/**
|
||||||
* All matched models.
|
* All matched models.
|
||||||
*/
|
*/
|
||||||
models: (BaseModel & Searchable)[];
|
models: (BaseViewModel & Searchable)[];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -61,7 +60,6 @@ export class SearchService {
|
|||||||
*/
|
*/
|
||||||
private searchModels: {
|
private searchModels: {
|
||||||
collectionString: string;
|
collectionString: string;
|
||||||
ctor: new (...args: any[]) => Searchable & BaseModel;
|
|
||||||
verboseNameSingular: string;
|
verboseNameSingular: string;
|
||||||
verboseNamePlural: string;
|
verboseNamePlural: string;
|
||||||
displayOrder: number;
|
displayOrder: number;
|
||||||
@ -70,7 +68,7 @@ export class SearchService {
|
|||||||
/**
|
/**
|
||||||
* @param DS The DataStore to search in.
|
* @param DS The DataStore to search in.
|
||||||
*/
|
*/
|
||||||
public constructor(private DS: DataStoreService) {}
|
public constructor() {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Registers a model by the given attributes.
|
* Registers a model by the given attributes.
|
||||||
@ -81,13 +79,12 @@ export class SearchService {
|
|||||||
*/
|
*/
|
||||||
public registerModel(
|
public registerModel(
|
||||||
collectionString: string,
|
collectionString: string,
|
||||||
ctor: new (...args: any[]) => Searchable & BaseModel,
|
ctor: new (...args: any[]) => Searchable & BaseViewModel,
|
||||||
displayOrder: number
|
displayOrder: number
|
||||||
): void {
|
): void {
|
||||||
const instance = new ctor();
|
const instance = new ctor();
|
||||||
this.searchModels.push({
|
this.searchModels.push({
|
||||||
collectionString: collectionString,
|
collectionString: collectionString,
|
||||||
ctor: ctor,
|
|
||||||
verboseNameSingular: instance.getVerboseName(),
|
verboseNameSingular: instance.getVerboseName(),
|
||||||
verboseNamePlural: instance.getVerboseName(true),
|
verboseNamePlural: instance.getVerboseName(true),
|
||||||
displayOrder: displayOrder
|
displayOrder: displayOrder
|
||||||
@ -115,10 +112,10 @@ export class SearchService {
|
|||||||
*/
|
*/
|
||||||
public search(query: string, inCollectionStrings: string[]): SearchResult[] {
|
public search(query: string, inCollectionStrings: string[]): SearchResult[] {
|
||||||
query = query.toLowerCase();
|
query = query.toLowerCase();
|
||||||
return this.searchModels
|
/*return this.searchModels
|
||||||
.filter(s => inCollectionStrings.includes(s.collectionString))
|
.filter(s => inCollectionStrings.includes(s.collectionString))
|
||||||
.map(searchModel => {
|
.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))
|
model.formatForSearch().some(text => text.toLowerCase().includes(query))
|
||||||
);
|
);
|
||||||
return {
|
return {
|
||||||
@ -126,6 +123,8 @@ export class SearchService {
|
|||||||
verboseName: results.length === 1 ? searchModel.verboseNameSingular : searchModel.verboseNamePlural,
|
verboseName: results.length === 1 ? searchModel.verboseNameSingular : searchModel.verboseNamePlural,
|
||||||
models: results
|
models: results
|
||||||
};
|
};
|
||||||
});
|
});*/
|
||||||
|
throw new Error('Todo');
|
||||||
|
return [];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
|
|
||||||
import { OpenSlidesComponent } from 'app/openslides.component';
|
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';
|
import { Identifiable } from 'app/shared/models/base/identifiable';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -88,7 +88,7 @@ export class FullscreenProjectorComponent implements OnInit {
|
|||||||
this.isLoading = false;
|
this.isLoading = false;
|
||||||
});
|
});
|
||||||
|
|
||||||
this.operator.getObservable().subscribe(() => {
|
this.operator.getUserObservable().subscribe(() => {
|
||||||
this.canSeeProjector = this.operator.hasPerms('projector.can_see');
|
this.canSeeProjector = this.operator.hasPerms('projector.can_see');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { Component, Inject } from '@angular/core';
|
import { Component, Inject } from '@angular/core';
|
||||||
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material';
|
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';
|
import { Identifiable } from 'app/shared/models/base/identifiable';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -373,7 +373,7 @@ export class C4DialogComponent implements OnInit, OnDestroy {
|
|||||||
* Returns the operators name.
|
* Returns the operators name.
|
||||||
*/
|
*/
|
||||||
public getPlayerName(): string {
|
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.projectorDataService.projectorClosed(from);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.dataSubscription = this.projectorDataService
|
this.dataSubscription = this.projectorDataService.getProjectorObservable(to).subscribe(data => {
|
||||||
.getProjectorObservable(to)
|
this.slides = data || [];
|
||||||
.subscribe(data => (this.slides = data || []));
|
});
|
||||||
this.projectorSubscription = this.projectorRepository.getViewModelObservable(to).subscribe(projector => {
|
this.projectorSubscription = this.projectorRepository.getViewModelObservable(to).subscribe(projector => {
|
||||||
if (projector) {
|
if (projector) {
|
||||||
this.scroll = projector.scroll || 0;
|
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';
|
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 { E2EImportsModule } from '../../../../e2e-imports.module';
|
||||||
import { SortingTreeComponent } from './sorting-tree.component';
|
import { SortingTreeComponent } from './sorting-tree.component';
|
||||||
import { Component, ViewChild } from '@angular/core';
|
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 { Identifiable } from 'app/shared/models/base/identifiable';
|
||||||
import { BehaviorSubject } from 'rxjs';
|
import { BehaviorSubject } from 'rxjs';
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@ import { auditTime } from 'rxjs/operators';
|
|||||||
import { Subscription, Observable } from 'rxjs';
|
import { Subscription, Observable } from 'rxjs';
|
||||||
|
|
||||||
import { Identifiable } from 'app/shared/models/base/identifiable';
|
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';
|
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 {
|
public ngOnInit(): void {
|
||||||
// observe groups of operator, so the directive can actively react to changes
|
// 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();
|
this.updateView();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -41,7 +41,7 @@ export class Item extends BaseModel<Item> {
|
|||||||
public parent_id: number;
|
public parent_id: number;
|
||||||
|
|
||||||
public constructor(input?: any) {
|
public constructor(input?: any) {
|
||||||
super('agenda/item', 'Item', input);
|
super('agenda/item', input);
|
||||||
}
|
}
|
||||||
|
|
||||||
public deserialize(input: any): void {
|
public deserialize(input: any): void {
|
||||||
@ -84,16 +84,4 @@ export class Item extends BaseModel<Item> {
|
|||||||
const type = itemVisibilityChoices.find(choice => choice.key === this.type);
|
const type = itemVisibilityChoices.find(choice => choice.key === this.type);
|
||||||
return type ? type.csvName : '';
|
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;
|
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 { AssignmentUser } from './assignment-user';
|
||||||
import { Poll } from './poll';
|
import { Poll } from './poll';
|
||||||
import { AgendaBaseModel } from '../base/agenda-base-model';
|
import { BaseModel } from '../base/base-model';
|
||||||
import { SearchRepresentation } from 'app/core/ui-services/search.service';
|
|
||||||
|
|
||||||
export const assignmentPhase = [
|
export const assignmentPhase = [
|
||||||
{ key: 0, name: 'Searching for candidates' },
|
{ key: 0, name: 'Searching for candidates' },
|
||||||
@ -13,7 +12,7 @@ export const assignmentPhase = [
|
|||||||
* Representation of an assignment.
|
* Representation of an assignment.
|
||||||
* @ignore
|
* @ignore
|
||||||
*/
|
*/
|
||||||
export class Assignment extends AgendaBaseModel {
|
export class Assignment extends BaseModel<Assignment> {
|
||||||
public id: number;
|
public id: number;
|
||||||
public title: string;
|
public title: string;
|
||||||
public description: string;
|
public description: string;
|
||||||
@ -26,7 +25,7 @@ export class Assignment extends AgendaBaseModel {
|
|||||||
public tags_id: number[];
|
public tags_id: number[];
|
||||||
|
|
||||||
public constructor(input?: any) {
|
public constructor(input?: any) {
|
||||||
super('assignments/assignment', 'Election', input);
|
super('assignments/assignment', input);
|
||||||
}
|
}
|
||||||
|
|
||||||
public get candidateIds(): number[] {
|
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 { OpenSlidesComponent } from 'app/openslides.component';
|
||||||
import { Deserializable } from './deserializable';
|
import { Deserializable } from './deserializable';
|
||||||
import { Displayable } from './displayable';
|
|
||||||
import { Identifiable } from './identifiable';
|
import { Identifiable } from './identifiable';
|
||||||
|
import { Collection } from './collection';
|
||||||
|
|
||||||
export type ModelConstructor<T extends BaseModel<T>> = new (...args: any[]) => T;
|
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>`
|
* 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
|
export abstract class BaseModel<T = object> extends OpenSlidesComponent
|
||||||
implements Deserializable, Displayable, Identifiable {
|
implements Deserializable, Identifiable, Collection {
|
||||||
/**
|
/**
|
||||||
* force children of BaseModel to have a collectionString.
|
* force children of BaseModel to have a collectionString.
|
||||||
*
|
*
|
||||||
@ -27,11 +27,6 @@ export abstract class BaseModel<T = object> extends OpenSlidesComponent
|
|||||||
return this._collectionString;
|
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
|
* force children of BaseModel to have an id
|
||||||
*/
|
*/
|
||||||
@ -44,10 +39,9 @@ export abstract class BaseModel<T = object> extends OpenSlidesComponent
|
|||||||
* @param verboseName
|
* @param verboseName
|
||||||
* @param input
|
* @param input
|
||||||
*/
|
*/
|
||||||
protected constructor(collectionString: string, verboseName: string, input?: any) {
|
protected constructor(collectionString: string, input?: any) {
|
||||||
super();
|
super();
|
||||||
this._collectionString = collectionString;
|
this._collectionString = collectionString;
|
||||||
this._verboseName = verboseName;
|
|
||||||
|
|
||||||
if (input) {
|
if (input) {
|
||||||
this.changeNullValuesToUndef(input);
|
this.changeNullValuesToUndef(input);
|
||||||
@ -74,32 +68,6 @@ export abstract class BaseModel<T = object> extends OpenSlidesComponent
|
|||||||
Object.assign(this, update);
|
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.
|
* Most simple and most commonly used deserialize function.
|
||||||
* Inherited to children, can be overwritten for special use cases
|
* 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
|
* @ignore
|
||||||
*/
|
*/
|
||||||
export class ChatMessage extends BaseModel<ChatMessage> {
|
export class ChatMessage extends BaseModel<ChatMessage> {
|
||||||
|
public static COLLECTIONSTRING = 'core/chat-message';
|
||||||
public id: number;
|
public id: number;
|
||||||
public message: string;
|
public message: string;
|
||||||
public timestamp: string; // TODO: Type for timestamp
|
public timestamp: string; // TODO: Type for timestamp
|
||||||
public user_id: number;
|
public user_id: number;
|
||||||
|
|
||||||
public constructor(input?: any) {
|
public constructor(input?: any) {
|
||||||
super('core/chat-message', 'Chatmessage', input);
|
super(ChatMessage.COLLECTIONSTRING, input);
|
||||||
}
|
|
||||||
|
|
||||||
public getTitle(): string {
|
|
||||||
return 'Chatmessage';
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,15 +5,12 @@ import { BaseModel } from '../base/base-model';
|
|||||||
* @ignore
|
* @ignore
|
||||||
*/
|
*/
|
||||||
export class Config extends BaseModel {
|
export class Config extends BaseModel {
|
||||||
|
public static COLLECTIONSTRING = 'core/config';
|
||||||
public id: number;
|
public id: number;
|
||||||
public key: string;
|
public key: string;
|
||||||
public value: Object;
|
public value: Object;
|
||||||
|
|
||||||
public constructor(input?: any) {
|
public constructor(input?: any) {
|
||||||
super('core/config', 'Config', input);
|
super(Config.COLLECTIONSTRING, input);
|
||||||
}
|
|
||||||
|
|
||||||
public getTitle(): string {
|
|
||||||
return this.key;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,10 +14,6 @@ export class Countdown extends BaseModel<Countdown> {
|
|||||||
public running: boolean;
|
public running: boolean;
|
||||||
|
|
||||||
public constructor(input?: any) {
|
public constructor(input?: any) {
|
||||||
super(Countdown.COLLECTIONSTRING, 'Countdown', input);
|
super(Countdown.COLLECTIONSTRING, input);
|
||||||
}
|
|
||||||
|
|
||||||
public getTitle(): string {
|
|
||||||
return this.description;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,7 @@ import { BaseModel } from '../base/base-model';
|
|||||||
* @ignore
|
* @ignore
|
||||||
*/
|
*/
|
||||||
export class History extends BaseModel {
|
export class History extends BaseModel {
|
||||||
|
public static COLLECTIONSTRING = 'core/history';
|
||||||
public id: number;
|
public id: number;
|
||||||
public element_id: string;
|
public element_id: string;
|
||||||
public now: string;
|
public now: string;
|
||||||
@ -29,10 +30,6 @@ export class History extends BaseModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public constructor(input?: any) {
|
public constructor(input?: any) {
|
||||||
super('core/history', 'History', input);
|
super(History.COLLECTIONSTRING, input);
|
||||||
}
|
|
||||||
|
|
||||||
public getTitle(): string {
|
|
||||||
return this.element_id;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,10 +11,6 @@ export class ProjectorMessage extends BaseModel<ProjectorMessage> {
|
|||||||
public message: string;
|
public message: string;
|
||||||
|
|
||||||
public constructor(input?: any) {
|
public constructor(input?: any) {
|
||||||
super(ProjectorMessage.COLLECTIONSTRING, 'Message', input);
|
super(ProjectorMessage.COLLECTIONSTRING, input);
|
||||||
}
|
|
||||||
|
|
||||||
public getTitle(): string {
|
|
||||||
return 'Projectormessage';
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -44,9 +44,14 @@ export interface ProjectionDefault {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Representation of a projector. Has the nested property "projectiondefaults"
|
* Representation of a projector. Has the nested property "projectiondefaults"
|
||||||
|
*
|
||||||
|
* TODO: Move all function to the viewprojector.
|
||||||
|
*
|
||||||
* @ignore
|
* @ignore
|
||||||
*/
|
*/
|
||||||
export class Projector extends BaseModel<Projector> {
|
export class Projector extends BaseModel<Projector> {
|
||||||
|
public static COLLECTIONSTRING = 'core/projector';
|
||||||
|
|
||||||
public id: number;
|
public id: number;
|
||||||
public elements: ProjectorElements;
|
public elements: ProjectorElements;
|
||||||
public elements_preview: ProjectorElements;
|
public elements_preview: ProjectorElements;
|
||||||
@ -60,7 +65,7 @@ export class Projector extends BaseModel<Projector> {
|
|||||||
public projectiondefaults: ProjectionDefault[];
|
public projectiondefaults: ProjectionDefault[];
|
||||||
|
|
||||||
public constructor(input?: any) {
|
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[]]
|
[[], []] as [T[], T[]]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public getTitle(): string {
|
|
||||||
return this.name;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,27 +1,16 @@
|
|||||||
import { BaseModel } from '../base/base-model';
|
import { BaseModel } from '../base/base-model';
|
||||||
import { Searchable } from '../base/searchable';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Representation of a tag.
|
* Representation of a tag.
|
||||||
* @ignore
|
* @ignore
|
||||||
*/
|
*/
|
||||||
export class Tag extends BaseModel<Tag> implements Searchable {
|
export class Tag extends BaseModel<Tag> {
|
||||||
|
public static COLLECTIONSTRING = 'core/tag';
|
||||||
|
|
||||||
public id: number;
|
public id: number;
|
||||||
public name: string;
|
public name: string;
|
||||||
|
|
||||||
public constructor(input?: any) {
|
public constructor(input?: any) {
|
||||||
super('core/tag', 'Tag', input);
|
super(Tag.COLLECTIONSTRING, input);
|
||||||
}
|
|
||||||
|
|
||||||
public getTitle(): string {
|
|
||||||
return this.name;
|
|
||||||
}
|
|
||||||
|
|
||||||
public formatForSearch(): string[] {
|
|
||||||
return [this.name];
|
|
||||||
}
|
|
||||||
|
|
||||||
public getDetailStateURL(): string {
|
|
||||||
return '/tags';
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,11 @@
|
|||||||
import { File } from './file';
|
import { File } from './file';
|
||||||
import { Searchable } from '../base/searchable';
|
|
||||||
import { BaseModel } from '../base/base-model';
|
import { BaseModel } from '../base/base-model';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Representation of MediaFile. Has the nested property "File"
|
* Representation of MediaFile. Has the nested property "File"
|
||||||
* @ignore
|
* @ignore
|
||||||
*/
|
*/
|
||||||
export class Mediafile extends BaseModel<Mediafile> implements Searchable {
|
export class Mediafile extends BaseModel<Mediafile> {
|
||||||
public id: number;
|
public id: number;
|
||||||
public title: string;
|
public title: string;
|
||||||
public mediafile: File;
|
public mediafile: File;
|
||||||
@ -17,7 +16,7 @@ export class Mediafile extends BaseModel<Mediafile> implements Searchable {
|
|||||||
public timestamp: string;
|
public timestamp: string;
|
||||||
|
|
||||||
public constructor(input?: any) {
|
public constructor(input?: any) {
|
||||||
super('mediafiles/mediafile', 'Mediafile', input);
|
super('mediafiles/mediafile', input);
|
||||||
}
|
}
|
||||||
|
|
||||||
public deserialize(input: any): void {
|
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
|
* @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}`;
|
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 { 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"
|
* Representation of a motion category. Has the nested property "File"
|
||||||
* @ignore
|
* @ignore
|
||||||
*/
|
*/
|
||||||
export class Category extends BaseModel<Category> implements Searchable {
|
export class Category extends BaseModel<Category> {
|
||||||
|
public static COLLECTIONSTRING = 'motions/category';
|
||||||
|
|
||||||
public id: number;
|
public id: number;
|
||||||
public name: string;
|
public name: string;
|
||||||
public prefix: string;
|
public prefix: string;
|
||||||
|
|
||||||
public constructor(input?: any) {
|
public constructor(input?: any) {
|
||||||
super('motions/category', 'Category', input);
|
super(Category.COLLECTIONSTRING, 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';
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,38 +1,17 @@
|
|||||||
import { AgendaBaseModel } from '../base/agenda-base-model';
|
import { BaseModel } from '../base/base-model';
|
||||||
import { SearchRepresentation } from 'app/core/ui-services/search.service';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Representation of a motion block.
|
* Representation of a motion block.
|
||||||
* @ignore
|
* @ignore
|
||||||
*/
|
*/
|
||||||
export class MotionBlock extends AgendaBaseModel {
|
export class MotionBlock extends BaseModel {
|
||||||
|
public static COLLECTIONSTRING = 'motions/motion-block';
|
||||||
|
|
||||||
public id: number;
|
public id: number;
|
||||||
public title: string;
|
public title: string;
|
||||||
public agenda_item_id: number;
|
public agenda_item_id: number;
|
||||||
|
|
||||||
public constructor(input?: any) {
|
public constructor(input?: any) {
|
||||||
super('motions/motion-block', 'Motion block', input);
|
super(MotionBlock.COLLECTIONSTRING, 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}`;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,9 @@ import { BaseModel } from '../base/base-model';
|
|||||||
* Representation of a motion change recommendation.
|
* Representation of a motion change recommendation.
|
||||||
* @ignore
|
* @ignore
|
||||||
*/
|
*/
|
||||||
export class MotionChangeReco extends BaseModel<MotionChangeReco> {
|
export class MotionChangeRecommendation extends BaseModel<MotionChangeRecommendation> {
|
||||||
|
public static COLLECTIONSTRING = 'motions/motion-change-recommendation';
|
||||||
|
|
||||||
public id: number;
|
public id: number;
|
||||||
public motion_id: number;
|
public motion_id: number;
|
||||||
public rejected: boolean;
|
public rejected: boolean;
|
||||||
@ -17,10 +19,6 @@ export class MotionChangeReco extends BaseModel<MotionChangeReco> {
|
|||||||
public creation_time: string;
|
public creation_time: string;
|
||||||
|
|
||||||
public constructor(input?: any) {
|
public constructor(input?: any) {
|
||||||
super('motions/motion-change-recommendation', 'Change recommendation', input);
|
super(MotionChangeRecommendation.COLLECTIONSTRING, input);
|
||||||
}
|
|
||||||
|
|
||||||
public getTitle(): string {
|
|
||||||
return 'Changerecommendation';
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,20 +1,18 @@
|
|||||||
import { BaseModel } from '../base/base-model';
|
import { BaseModel } from '../base/base-model';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Representation of a motion category. Has the nested property "File"
|
* Representation of a comment section.
|
||||||
* @ignore
|
* @ignore
|
||||||
*/
|
*/
|
||||||
export class MotionCommentSection extends BaseModel<MotionCommentSection> {
|
export class MotionCommentSection extends BaseModel<MotionCommentSection> {
|
||||||
|
public static COLLECTIONSTRING = 'motions/motion-comment-section';
|
||||||
|
|
||||||
public id: number;
|
public id: number;
|
||||||
public name: string;
|
public name: string;
|
||||||
public read_groups_id: number[];
|
public read_groups_id: number[];
|
||||||
public write_groups_id: number[];
|
public write_groups_id: number[];
|
||||||
|
|
||||||
public constructor(input?: any) {
|
public constructor(input?: any) {
|
||||||
super('motions/motion-comment-section', 'Comment section', input);
|
super(MotionCommentSection.COLLECTIONSTRING, input);
|
||||||
}
|
|
||||||
|
|
||||||
public getTitle(): string {
|
|
||||||
return this.name;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
import { MotionSubmitter } from './motion-submitter';
|
import { MotionSubmitter } from './motion-submitter';
|
||||||
import { MotionComment } from './motion-comment';
|
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 { MotionPoll } from './motion-poll';
|
||||||
|
import { BaseModel } from '../base/base-model';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Representation of Motion.
|
* Representation of Motion.
|
||||||
@ -11,7 +10,7 @@ import { MotionPoll } from './motion-poll';
|
|||||||
*
|
*
|
||||||
* @ignore
|
* @ignore
|
||||||
*/
|
*/
|
||||||
export class Motion extends AgendaBaseModel {
|
export class Motion extends BaseModel {
|
||||||
public static COLLECTIONSTRING = 'motions/motion';
|
public static COLLECTIONSTRING = 'motions/motion';
|
||||||
|
|
||||||
public id: number;
|
public id: number;
|
||||||
@ -45,7 +44,7 @@ export class Motion extends AgendaBaseModel {
|
|||||||
public last_modified: string;
|
public last_modified: string;
|
||||||
|
|
||||||
public constructor(input?: any) {
|
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);
|
.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 {
|
public deserialize(input: any): void {
|
||||||
Object.assign(this, input);
|
Object.assign(this, input);
|
||||||
|
|
||||||
|
@ -1,38 +1,18 @@
|
|||||||
import { BaseModel } from '../base/base-model';
|
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.
|
* Representation of a statute paragraph.
|
||||||
* @ignore
|
* @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 id: number;
|
||||||
public title: string;
|
public title: string;
|
||||||
public text: string;
|
public text: string;
|
||||||
public weight: number;
|
public weight: number;
|
||||||
|
|
||||||
public constructor(input?: any) {
|
public constructor(input?: any) {
|
||||||
super('motions/statute-paragraph', 'Statute paragraph', input);
|
super(StatuteParagraph.COLLECTIONSTRING, 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';
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,17 +6,15 @@ import { WorkflowState } from './workflow-state';
|
|||||||
* @ignore
|
* @ignore
|
||||||
*/
|
*/
|
||||||
export class Workflow extends BaseModel<Workflow> {
|
export class Workflow extends BaseModel<Workflow> {
|
||||||
|
public static COLLECTIONSTRING = 'motions/workflow';
|
||||||
|
|
||||||
public id: number;
|
public id: number;
|
||||||
public name: string;
|
public name: string;
|
||||||
public states: WorkflowState[];
|
public states: WorkflowState[];
|
||||||
public first_state_id: number;
|
public first_state_id: number;
|
||||||
|
|
||||||
public get firstState(): WorkflowState {
|
|
||||||
return this.getStateById(this.first_state_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
public constructor(input?: any) {
|
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 {
|
public deserialize(input: any): void {
|
||||||
Object.assign(this, input);
|
Object.assign(this, input);
|
||||||
if (input.states instanceof Array) {
|
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 { BaseModel } from '../base/base-model';
|
||||||
import { SearchRepresentation } from 'app/core/ui-services/search.service';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Representation of a topic.
|
* Representation of a topic.
|
||||||
* @ignore
|
* @ignore
|
||||||
*/
|
*/
|
||||||
export class Topic extends AgendaBaseModel {
|
export class Topic extends BaseModel<Topic> {
|
||||||
|
public static COLLECTIONSTRING = 'topics/topic';
|
||||||
|
|
||||||
public id: number;
|
public id: number;
|
||||||
public title: string;
|
public title: string;
|
||||||
public text: string;
|
public text: string;
|
||||||
@ -13,36 +14,6 @@ export class Topic extends AgendaBaseModel {
|
|||||||
public agenda_item_id: number;
|
public agenda_item_id: number;
|
||||||
|
|
||||||
public constructor(input?: any) {
|
public constructor(input?: any) {
|
||||||
super('topics/topic', 'Topic', input);
|
super(Topic.COLLECTIONSTRING, 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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,12 +5,13 @@ import { BaseModel } from '../base/base-model';
|
|||||||
* @ignore
|
* @ignore
|
||||||
*/
|
*/
|
||||||
export class Group extends BaseModel<Group> {
|
export class Group extends BaseModel<Group> {
|
||||||
|
public static COLLECTIONSTRING = 'users/group';
|
||||||
public id: number;
|
public id: number;
|
||||||
public name: string;
|
public name: string;
|
||||||
public permissions: string[];
|
public permissions: string[];
|
||||||
|
|
||||||
public constructor(input?: any) {
|
public constructor(input?: any) {
|
||||||
super('users/group', 'Group', input);
|
super(Group.COLLECTIONSTRING, input);
|
||||||
if (!input) {
|
if (!input) {
|
||||||
// permissions are required for new groups
|
// permissions are required for new groups
|
||||||
this.permissions = [];
|
this.permissions = [];
|
||||||
|
@ -49,12 +49,14 @@ export interface PersonalNoteObject {
|
|||||||
* @ignore
|
* @ignore
|
||||||
*/
|
*/
|
||||||
export class PersonalNote extends BaseModel<PersonalNote> implements PersonalNoteObject {
|
export class PersonalNote extends BaseModel<PersonalNote> implements PersonalNoteObject {
|
||||||
|
public static COLLECTIONSTRING = 'users/personal-note';
|
||||||
|
|
||||||
public id: number;
|
public id: number;
|
||||||
public user_id: number;
|
public user_id: number;
|
||||||
public notes: PersonalNotesFormat;
|
public notes: PersonalNotesFormat;
|
||||||
|
|
||||||
public constructor(input: any) {
|
public constructor(input: any) {
|
||||||
super('users/personal-note', 'Personal note', input);
|
super(PersonalNote.COLLECTIONSTRING, input);
|
||||||
}
|
}
|
||||||
|
|
||||||
public getTitle(): string {
|
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 { BaseModel } from '../base/base-model';
|
||||||
import { DetailNavigable } from '../base/detail-navigable';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Iterable pre selection of genders (sexes)
|
* 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.
|
* Representation of a user in contrast to the operator.
|
||||||
* @ignore
|
* @ignore
|
||||||
*/
|
*/
|
||||||
export class User extends BaseModel<User> implements Searchable, DetailNavigable {
|
export class User extends BaseModel<User> {
|
||||||
public static COLLECTIONSTRING = 'users/user';
|
public static COLLECTIONSTRING = 'users/user';
|
||||||
|
|
||||||
public id: number;
|
public id: number;
|
||||||
@ -34,89 +31,10 @@ export class User extends BaseModel<User> implements Searchable, DetailNavigable
|
|||||||
public default_password: string;
|
public default_password: string;
|
||||||
|
|
||||||
public constructor(input?: any) {
|
public constructor(input?: any) {
|
||||||
super(User.COLLECTIONSTRING, 'Participant', input);
|
super(User.COLLECTIONSTRING, 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();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public containsGroupId(id: number): boolean {
|
public containsGroupId(id: number): boolean {
|
||||||
return this.groups_id.some(groupId => groupId === id);
|
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 { AppConfig } from '../../core/app-config';
|
||||||
import { Item } from '../../shared/models/agenda/item';
|
import { Item } from '../../shared/models/agenda/item';
|
||||||
import { Topic } from '../../shared/models/topics/topic';
|
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 { 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 = {
|
export const AgendaAppConfig: AppConfig = {
|
||||||
name: 'agenda',
|
name: 'agenda',
|
||||||
models: [
|
models: [
|
||||||
{ collectionString: 'agenda/item', model: Item, repository: AgendaRepositoryService },
|
{ collectionString: 'agenda/item', model: Item, viewModel: ViewItem, repository: ItemRepositoryService },
|
||||||
{ collectionString: 'topics/topic', model: Topic, searchOrder: 1, repository: TopicRepositoryService }
|
{
|
||||||
|
collectionString: 'topics/topic',
|
||||||
|
model: Topic,
|
||||||
|
viewModel: ViewTopic,
|
||||||
|
searchOrder: 1,
|
||||||
|
repository: TopicRepositoryService
|
||||||
|
}
|
||||||
],
|
],
|
||||||
mainMenuEntries: [
|
mainMenuEntries: [
|
||||||
{
|
{
|
||||||
|
@ -5,7 +5,7 @@ import { Title } from '@angular/platform-browser';
|
|||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
|
||||||
import { AgendaFilterListService } from '../../services/agenda-filter-list.service';
|
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 { ListViewBaseComponent } from 'app/site/base/list-view-base';
|
||||||
import { PromptService } from 'app/core/ui-services/prompt.service';
|
import { PromptService } from 'app/core/ui-services/prompt.service';
|
||||||
import { ViewItem } from '../../models/view-item';
|
import { ViewItem } from '../../models/view-item';
|
||||||
@ -63,7 +63,7 @@ export class AgendaListComponent extends ListViewBaseComponent<ViewItem> impleme
|
|||||||
matSnackBar: MatSnackBar,
|
matSnackBar: MatSnackBar,
|
||||||
private route: ActivatedRoute,
|
private route: ActivatedRoute,
|
||||||
private router: Router,
|
private router: Router,
|
||||||
private repo: AgendaRepositoryService,
|
private repo: ItemRepositoryService,
|
||||||
private promptService: PromptService,
|
private promptService: PromptService,
|
||||||
private dialog: MatDialog,
|
private dialog: MatDialog,
|
||||||
private config: ConfigService,
|
private config: ConfigService,
|
||||||
|
@ -5,7 +5,7 @@ import { MatSnackBar } from '@angular/material';
|
|||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
import { Observable } from 'rxjs';
|
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 { BaseViewComponent } from '../../../base/base-view';
|
||||||
import { OSTreeSortEvent } from 'app/shared/components/sorting-tree/sorting-tree.component';
|
import { OSTreeSortEvent } from 'app/shared/components/sorting-tree/sorting-tree.component';
|
||||||
import { ViewItem } from '../../models/view-item';
|
import { ViewItem } from '../../models/view-item';
|
||||||
@ -39,7 +39,7 @@ export class AgendaSortComponent extends BaseViewComponent {
|
|||||||
title: Title,
|
title: Title,
|
||||||
translate: TranslateService,
|
translate: TranslateService,
|
||||||
matSnackBar: MatSnackBar,
|
matSnackBar: MatSnackBar,
|
||||||
private agendaRepo: AgendaRepositoryService
|
private agendaRepo: ItemRepositoryService
|
||||||
) {
|
) {
|
||||||
super(title, translate, matSnackBar);
|
super(title, translate, matSnackBar);
|
||||||
this.itemsObservable = this.agendaRepo.getViewModelListObservable();
|
this.itemsObservable = this.agendaRepo.getViewModelListObservable();
|
||||||
|
@ -1,25 +1,25 @@
|
|||||||
import { ActivatedRoute } from '@angular/router';
|
import { ActivatedRoute } from '@angular/router';
|
||||||
import { BehaviorSubject, Subscription } from 'rxjs';
|
|
||||||
import { Component, OnInit } from '@angular/core';
|
import { Component, OnInit } from '@angular/core';
|
||||||
import { FormGroup, FormControl } from '@angular/forms';
|
import { FormGroup, FormControl } from '@angular/forms';
|
||||||
import { MatSnackBar, MatSelectChange } from '@angular/material';
|
import { MatSnackBar, MatSelectChange } from '@angular/material';
|
||||||
|
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
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 { BaseViewComponent } from 'app/site/base/base-view';
|
||||||
import { CurrentListOfSpeakersSlideService } from 'app/site/projector/services/current-list-of-of-speakers-slide.service';
|
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 { OperatorService } from 'app/core/core-services/operator.service';
|
||||||
import { ProjectorRepositoryService } from 'app/core/repositories/projector/projector-repository.service';
|
import { ProjectorRepositoryService } from 'app/core/repositories/projector/projector-repository.service';
|
||||||
import { PromptService } from 'app/core/ui-services/prompt.service';
|
import { PromptService } from 'app/core/ui-services/prompt.service';
|
||||||
import { SpeakerState } from 'app/shared/models/agenda/speaker';
|
import { SpeakerState } from 'app/shared/models/agenda/speaker';
|
||||||
import { Title } from '@angular/platform-browser';
|
import { Title } from '@angular/platform-browser';
|
||||||
import { User } from 'app/shared/models/users/user';
|
|
||||||
import { ViewItem } from '../../models/view-item';
|
import { ViewItem } from '../../models/view-item';
|
||||||
import { ViewSpeaker } from '../../models/view-speaker';
|
import { ViewSpeaker } from '../../models/view-speaker';
|
||||||
import { ViewProjector } from 'app/site/projector/models/view-projector';
|
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.
|
* The list of speakers for agenda items.
|
||||||
@ -68,7 +68,7 @@ export class SpeakerListComponent extends BaseViewComponent implements OnInit {
|
|||||||
/**
|
/**
|
||||||
* Hold the users
|
* Hold the users
|
||||||
*/
|
*/
|
||||||
public users: BehaviorSubject<User[]>;
|
public users: BehaviorSubject<ViewUser[]>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Required for the user search selector
|
* Required for the user search selector
|
||||||
@ -113,12 +113,12 @@ export class SpeakerListComponent extends BaseViewComponent implements OnInit {
|
|||||||
snackBar: MatSnackBar,
|
snackBar: MatSnackBar,
|
||||||
projectorRepo: ProjectorRepositoryService,
|
projectorRepo: ProjectorRepositoryService,
|
||||||
private route: ActivatedRoute,
|
private route: ActivatedRoute,
|
||||||
private DS: DataStoreService,
|
private itemRepo: ItemRepositoryService,
|
||||||
private itemRepo: AgendaRepositoryService,
|
|
||||||
private op: OperatorService,
|
private op: OperatorService,
|
||||||
private promptService: PromptService,
|
private promptService: PromptService,
|
||||||
private currentListOfSpeakersService: CurrentListOfSpeakersSlideService,
|
private currentListOfSpeakersService: CurrentListOfSpeakersSlideService,
|
||||||
private durationService: DurationService
|
private durationService: DurationService,
|
||||||
|
private userRepository: UserRepositoryService
|
||||||
) {
|
) {
|
||||||
super(title, translate, snackBar);
|
super(title, translate, snackBar);
|
||||||
this.isCurrentListOfSpeakers();
|
this.isCurrentListOfSpeakers();
|
||||||
@ -143,12 +143,8 @@ export class SpeakerListComponent extends BaseViewComponent implements OnInit {
|
|||||||
*/
|
*/
|
||||||
public ngOnInit(): void {
|
public ngOnInit(): void {
|
||||||
// load and observe users
|
// load and observe users
|
||||||
this.users = new BehaviorSubject(this.DS.getAll(User));
|
this.users = new BehaviorSubject(this.userRepository.getViewModelList());
|
||||||
this.DS.changeObservable.subscribe(model => {
|
this.userRepository.getViewModelListObservable().subscribe(users => this.users.next(users));
|
||||||
if (model instanceof User) {
|
|
||||||
this.users.next(this.DS.getAll(User));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// detect changes in the form
|
// detect changes in the form
|
||||||
this.addSpeakerForm.valueChanges.subscribe(formResult => {
|
this.addSpeakerForm.valueChanges.subscribe(formResult => {
|
||||||
@ -190,10 +186,10 @@ export class SpeakerListComponent extends BaseViewComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.projectorSubscription = this.currentListOfSpeakersService
|
this.projectorSubscription = this.currentListOfSpeakersService
|
||||||
.getAgendaItemIdObservable(projector)
|
.getAgendaItemObservable(projector)
|
||||||
.subscribe(agendaId => {
|
.subscribe(item => {
|
||||||
if (agendaId) {
|
if (item) {
|
||||||
this.setSpeakerList(agendaId);
|
this.setSpeakerList(item.id);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -24,90 +24,89 @@
|
|||||||
</div>
|
</div>
|
||||||
</os-head-bar>
|
</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"
|
<div *ngIf="topic.hasAttachments() && !editTopic">
|
||||||
[ngClass]="editTopic ? 'os-form-card' : 'os-card'"
|
<h3>
|
||||||
class="on-transition-fade">
|
<span translate>Attachments</span>:
|
||||||
<div *ngIf="!editTopic">
|
<mat-list dense>
|
||||||
<h1>{{ topic.title }}</h1>
|
<mat-list-item *ngFor="let file of topic.attachments">
|
||||||
</div>
|
<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>
|
<div>
|
||||||
<span *ngIf="!editTopic">
|
<mat-form-field>
|
||||||
<!-- Render topic text as HTML -->
|
<input
|
||||||
<div [innerHTML]="topic.text"></div>
|
type="text"
|
||||||
</span>
|
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>
|
||||||
|
|
||||||
<div *ngIf="topic.hasAttachments() && !editTopic">
|
<!-- The editor -->
|
||||||
<h3>
|
<div class="spacer-bottom-20">
|
||||||
<span translate>Attachments</span>:
|
<h4 translate>Text</h4>
|
||||||
<mat-list dense>
|
<editor formControlName="text" [init]="tinyMceSettings"></editor>
|
||||||
<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>
|
|
||||||
</div>
|
</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>
|
<div>
|
||||||
<mat-form-field>
|
<mat-form-field>
|
||||||
<input
|
<mat-select formControlName="agenda_type" placeholder="{{ 'Agenda visibility' | translate }}">
|
||||||
type="text"
|
<mat-option *ngFor="let type of itemVisibility" [value]="type.key">
|
||||||
matInput
|
<span>{{ type.name | translate }}</span>
|
||||||
osAutofocus
|
</mat-option>
|
||||||
required
|
</mat-select>
|
||||||
formControlName="title"
|
|
||||||
placeholder="{{ 'Title' | translate }}"
|
|
||||||
/>
|
|
||||||
<mat-error *ngIf="topicForm.invalid" translate>A name is required</mat-error>
|
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- The editor -->
|
<!-- Parent item -->
|
||||||
<div class="spacer-bottom-20">
|
<div>
|
||||||
<h4 translate>Text</h4>
|
<os-search-value-selector
|
||||||
<editor formControlName="text" [init]="tinyMceSettings"></editor>
|
ngDefaultControl
|
||||||
|
[form]="topicForm"
|
||||||
|
[formControl]="topicForm.get('agenda_parent_id')"
|
||||||
|
[multiple]="false"
|
||||||
|
[includeNone]="true"
|
||||||
|
listname="{{ 'Parent item' | translate }}"
|
||||||
|
[InputListValues]="itemObserver"
|
||||||
|
></os-search-value-selector>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
<!-- Attachments -->
|
</form>
|
||||||
<os-search-value-selector
|
</mat-card>
|
||||||
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>
|
|
||||||
|
|
||||||
<mat-menu #topicExtraMenu="matMenu">
|
<mat-menu #topicExtraMenu="matMenu">
|
||||||
<button mat-menu-item *ngIf="topic" [routerLink]="getSpeakerLink()">
|
<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 { ViewTopic } from '../../models/view-topic';
|
||||||
import { OperatorService } from 'app/core/core-services/operator.service';
|
import { OperatorService } from 'app/core/core-services/operator.service';
|
||||||
import { BehaviorSubject } from 'rxjs';
|
import { BehaviorSubject } from 'rxjs';
|
||||||
import { DataStoreService } from 'app/core/core-services/data-store.service';
|
import { itemVisibilityChoices } from 'app/shared/models/agenda/item';
|
||||||
import { Mediafile } from 'app/shared/models/mediafiles/mediafile';
|
|
||||||
import { Item, itemVisibilityChoices } from 'app/shared/models/agenda/item';
|
|
||||||
import { CreateTopic } from '../../models/create-topic';
|
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.
|
* Detail page for topics.
|
||||||
@ -49,12 +52,12 @@ export class TopicDetailComponent extends BaseViewComponent {
|
|||||||
/**
|
/**
|
||||||
* Subject for mediafiles
|
* Subject for mediafiles
|
||||||
*/
|
*/
|
||||||
public mediafilesObserver: BehaviorSubject<Mediafile[]>;
|
public mediafilesObserver: BehaviorSubject<ViewMediafile[]>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Subject for agenda items
|
* Subject for agenda items
|
||||||
*/
|
*/
|
||||||
public agendaItemObserver: BehaviorSubject<Item[]>;
|
public itemObserver: BehaviorSubject<ViewItem[]>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determine visibility states for the agenda that will be created implicitly
|
* Determine visibility states for the agenda that will be created implicitly
|
||||||
@ -85,22 +88,20 @@ export class TopicDetailComponent extends BaseViewComponent {
|
|||||||
private repo: TopicRepositoryService,
|
private repo: TopicRepositoryService,
|
||||||
private promptService: PromptService,
|
private promptService: PromptService,
|
||||||
private operator: OperatorService,
|
private operator: OperatorService,
|
||||||
private DS: DataStoreService
|
private mediafileRepo: MediafileRepositoryService,
|
||||||
|
private itemRepo: ItemRepositoryService
|
||||||
) {
|
) {
|
||||||
super(title, translate, matSnackBar);
|
super(title, translate, matSnackBar);
|
||||||
this.getTopicByUrl();
|
this.getTopicByUrl();
|
||||||
this.createForm();
|
this.createForm();
|
||||||
|
|
||||||
this.mediafilesObserver = new BehaviorSubject(this.DS.getAll(Mediafile));
|
this.mediafilesObserver = new BehaviorSubject(this.mediafileRepo.getViewModelList());
|
||||||
this.agendaItemObserver = new BehaviorSubject(this.DS.getAll(Item));
|
this.itemObserver = new BehaviorSubject(this.itemRepo.getViewModelList());
|
||||||
|
|
||||||
this.DS.changeObservable.subscribe(newModel => {
|
this.mediafileRepo
|
||||||
if (newModel instanceof Item) {
|
.getViewModelListObservable()
|
||||||
this.agendaItemObserver.next(DS.getAll(Item));
|
.subscribe(mediafiles => this.mediafilesObserver.next(mediafiles));
|
||||||
} else if (newModel instanceof Mediafile) {
|
this.itemRepo.getViewModelListObservable().subscribe(items => this.itemObserver.next(items));
|
||||||
this.mediafilesObserver.next(DS.getAll(Mediafile));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -170,7 +171,7 @@ export class TopicDetailComponent extends BaseViewComponent {
|
|||||||
// creates a new topic
|
// creates a new topic
|
||||||
this.newTopic = true;
|
this.newTopic = true;
|
||||||
this.editTopic = true;
|
this.editTopic = true;
|
||||||
this.topic = new ViewTopic();
|
this.topic = new ViewTopic(new Topic());
|
||||||
} else {
|
} else {
|
||||||
// load existing topic
|
// load existing topic
|
||||||
this.route.params.subscribe(params => {
|
this.route.params.subscribe(params => {
|
||||||
@ -205,7 +206,7 @@ export class TopicDetailComponent extends BaseViewComponent {
|
|||||||
*/
|
*/
|
||||||
public getSpeakerLink(): string {
|
public getSpeakerLink(): string {
|
||||||
if (!this.newTopic && this.topic) {
|
if (!this.newTopic && this.topic) {
|
||||||
const item = this.repo.getAgendaItem(this.topic.topic);
|
const item = this.topic.getAgendaItem();
|
||||||
if (item) {
|
if (item) {
|
||||||
return `/agenda/${item.id}/speakers`;
|
return `/agenda/${item.id}/speakers`;
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
import { BaseViewModel } from '../../base/base-view-model';
|
import { BaseViewModel } from '../../base/base-view-model';
|
||||||
import { Item } from 'app/shared/models/agenda/item';
|
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 { Speaker } from 'app/shared/models/agenda/speaker';
|
||||||
|
import { BaseAgendaViewModel } from 'app/site/base/base-agenda-view-model';
|
||||||
|
|
||||||
export class ViewItem extends BaseViewModel {
|
export class ViewItem extends BaseViewModel {
|
||||||
private _item: Item;
|
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
|
* 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;
|
return this._item;
|
||||||
}
|
}
|
||||||
|
|
||||||
public get contentObject(): AgendaBaseModel {
|
public get contentObject(): BaseAgendaViewModel {
|
||||||
return this._contentObject;
|
return this._contentObject;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -92,8 +92,8 @@ export class ViewItem extends BaseViewModel {
|
|||||||
return this.item ? this.item.parent_id : null;
|
return this.item ? this.item.parent_id : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public constructor(item: Item, contentObject: AgendaBaseModel) {
|
public constructor(item: Item, contentObject: BaseAgendaViewModel) {
|
||||||
super();
|
super('Item');
|
||||||
this._item = item;
|
this._item = item;
|
||||||
this._contentObject = contentObject;
|
this._contentObject = contentObject;
|
||||||
}
|
}
|
||||||
@ -113,7 +113,7 @@ export class ViewItem extends BaseViewModel {
|
|||||||
* @returns the agenda list title as string
|
* @returns the agenda list title as string
|
||||||
*/
|
*/
|
||||||
public getListTitle(): string {
|
public getListTitle(): string {
|
||||||
const contentObject: AgendaBaseModel = this.contentObject;
|
const contentObject: BaseAgendaViewModel = this.contentObject;
|
||||||
const numberPrefix = this.itemNumber ? `${this.itemNumber} · ` : '';
|
const numberPrefix = this.itemNumber ? `${this.itemNumber} · ` : '';
|
||||||
|
|
||||||
if (contentObject) {
|
if (contentObject) {
|
||||||
@ -123,9 +123,5 @@ export class ViewItem extends BaseViewModel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public updateValues(update: Item): void {
|
public updateDependencies(update: BaseViewModel): void {}
|
||||||
if (this.id === update.id) {
|
|
||||||
this._item = update;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,63 +1,62 @@
|
|||||||
import { BaseViewModel } from 'app/site/base/base-view-model';
|
import { BaseViewModel } from 'app/site/base/base-view-model';
|
||||||
import { Speaker, SpeakerState } from 'app/shared/models/agenda/speaker';
|
import { Speaker, SpeakerState } from 'app/shared/models/agenda/speaker';
|
||||||
import { User } from 'app/shared/models/users/user';
|
import { ViewUser } from 'app/site/users/models/view-user';
|
||||||
import { Selectable } from 'app/shared/components/selectable';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides "safe" access to a speaker with all it's components
|
* 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 _speaker: Speaker;
|
||||||
private _user: User;
|
private _user: ViewUser | null;
|
||||||
|
|
||||||
public get speaker(): Speaker {
|
public get speaker(): Speaker {
|
||||||
return this._speaker;
|
return this._speaker;
|
||||||
}
|
}
|
||||||
|
|
||||||
public get user(): User {
|
public get user(): ViewUser {
|
||||||
return this._user;
|
return this._user;
|
||||||
}
|
}
|
||||||
|
|
||||||
public get id(): number {
|
public get id(): number {
|
||||||
return this.speaker ? this.speaker.id : null;
|
return this.speaker.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
public get weight(): number {
|
public get weight(): number {
|
||||||
return this.speaker ? this.speaker.weight : null;
|
return this.speaker.weight;
|
||||||
}
|
}
|
||||||
|
|
||||||
public get marked(): boolean {
|
public get marked(): boolean {
|
||||||
return this.speaker ? this.speaker.marked : null;
|
return this.speaker.marked;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @returns an ISO datetime string or null
|
* @returns an ISO datetime string or null
|
||||||
*/
|
*/
|
||||||
public get begin_time(): string {
|
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
|
* @returns an ISO datetime string or null
|
||||||
*/
|
*/
|
||||||
public get end_time(): string {
|
public get end_time(): string {
|
||||||
return this.speaker ? this.speaker.end_time : null;
|
return this.speaker.end_time;
|
||||||
}
|
}
|
||||||
|
|
||||||
public get state(): SpeakerState {
|
public get state(): SpeakerState {
|
||||||
return this.speaker ? this.speaker.state : null;
|
return this.speaker.state;
|
||||||
}
|
}
|
||||||
|
|
||||||
public get name(): string {
|
public get name(): string {
|
||||||
return this.user.full_name;
|
return this.user ? this.user.full_name : '';
|
||||||
}
|
}
|
||||||
|
|
||||||
public get gender(): string {
|
public get gender(): string {
|
||||||
return this.user.gender || '';
|
return this.user ? this.user.gender : '';
|
||||||
}
|
}
|
||||||
|
|
||||||
public constructor(speaker?: Speaker, user?: User) {
|
public constructor(speaker: Speaker, user?: ViewUser) {
|
||||||
super();
|
super('Speaker');
|
||||||
this._speaker = speaker;
|
this._speaker = speaker;
|
||||||
this._user = user;
|
this._user = user;
|
||||||
}
|
}
|
||||||
@ -70,9 +69,5 @@ export class ViewSpeaker extends BaseViewModel implements Selectable {
|
|||||||
* Speaker is not a base model,
|
* Speaker is not a base model,
|
||||||
* @param update the incoming update
|
* @param update the incoming update
|
||||||
*/
|
*/
|
||||||
public updateValues(update: Speaker): void {
|
public updateDependencies(update: BaseViewModel): void {}
|
||||||
if (this.id === update.id) {
|
|
||||||
this._speaker = update;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,71 +1,112 @@
|
|||||||
import { BaseViewModel } from '../../base/base-view-model';
|
|
||||||
import { Topic } from 'app/shared/models/topics/topic';
|
import { Topic } from 'app/shared/models/topics/topic';
|
||||||
import { Mediafile } from 'app/shared/models/mediafiles/mediafile';
|
import { BaseAgendaViewModel } from 'app/site/base/base-agenda-view-model';
|
||||||
import { Item } from 'app/shared/models/agenda/item';
|
import { SearchRepresentation } from 'app/core/ui-services/search.service';
|
||||||
import { BaseModel } from 'app/shared/models/base/base-model';
|
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
|
* Provides "safe" access to topic with all it's components
|
||||||
* @ignore
|
* @ignore
|
||||||
*/
|
*/
|
||||||
export class ViewTopic extends BaseViewModel {
|
export class ViewTopic extends BaseAgendaViewModel {
|
||||||
protected _topic: Topic;
|
protected _topic: Topic;
|
||||||
private _attachments: Mediafile[];
|
private _attachments: ViewMediafile[];
|
||||||
private _agenda_item: Item;
|
private _agendaItem: ViewItem;
|
||||||
|
|
||||||
public get topic(): Topic {
|
public get topic(): Topic {
|
||||||
return this._topic;
|
return this._topic;
|
||||||
}
|
}
|
||||||
|
|
||||||
public get attachments(): Mediafile[] {
|
public get attachments(): ViewMediafile[] {
|
||||||
return this._attachments;
|
return this._attachments;
|
||||||
}
|
}
|
||||||
|
|
||||||
public get agenda_item(): Item {
|
public get agendaItem(): ViewItem {
|
||||||
return this._agenda_item;
|
return this._agendaItem;
|
||||||
}
|
}
|
||||||
|
|
||||||
public get id(): number {
|
public get id(): number {
|
||||||
return this.topic ? this.topic.id : null;
|
return this.topic.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
public get agenda_item_id(): number {
|
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[] {
|
public get attachments_id(): number[] {
|
||||||
return this.topic ? this.topic.attachments_id : null;
|
return this.topic.attachments_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
public get title(): string {
|
public get title(): string {
|
||||||
return this.topic ? this.topic.title : null;
|
return this.topic.title;
|
||||||
}
|
}
|
||||||
|
|
||||||
public get text(): string {
|
public get text(): string {
|
||||||
return this.topic ? this.topic.text : null;
|
return this.topic.text;
|
||||||
}
|
}
|
||||||
|
|
||||||
public constructor(topic?: Topic, attachments?: Mediafile[], item?: Item) {
|
public constructor(topic: Topic, attachments?: ViewMediafile[], item?: ViewItem) {
|
||||||
super();
|
super('Topic');
|
||||||
this._topic = topic;
|
this._topic = topic;
|
||||||
this._attachments = attachments;
|
this._attachments = attachments;
|
||||||
this._agenda_item = item;
|
this._agendaItem = item;
|
||||||
}
|
}
|
||||||
|
|
||||||
public getTitle(): string {
|
public getTitle(): string {
|
||||||
return this.title;
|
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 {
|
public hasAttachments(): boolean {
|
||||||
return this.attachments && this.attachments.length > 0;
|
return this.attachments && this.attachments.length > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public updateValues(update: BaseModel): void {
|
public updateDependencies(update: BaseViewModel): void {
|
||||||
if (update instanceof Mediafile) {
|
if (update instanceof ViewMediafile && this.attachments_id.includes(update.id)) {
|
||||||
if (this.topic && this.attachments_id && this.attachments_id.includes(update.id)) {
|
const attachmentIndex = this.attachments.findIndex(mediafile => mediafile.id === update.id);
|
||||||
const attachmentIndex = this.attachments.findIndex(mediafile => mediafile.id === update.id);
|
if (attachmentIndex < 0) {
|
||||||
this.attachments[attachmentIndex] = update as Mediafile;
|
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 { Item, itemVisibilityChoices } from 'app/shared/models/agenda/item';
|
||||||
import { ViewItem } from '../models/view-item';
|
import { ViewItem } from '../models/view-item';
|
||||||
import { StorageService } from 'app/core/core-services/storage.service';
|
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({
|
@Injectable({
|
||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
@ -19,7 +19,7 @@ export class AgendaFilterListService extends BaseFilterListService<Item, ViewIte
|
|||||||
* @param store
|
* @param store
|
||||||
* @param repo
|
* @param repo
|
||||||
*/
|
*/
|
||||||
public constructor(store: StorageService, repo: AgendaRepositoryService) {
|
public constructor(store: StorageService, repo: ItemRepositoryService) {
|
||||||
super(store, repo);
|
super(store, repo);
|
||||||
this.filterOptions = [
|
this.filterOptions = [
|
||||||
{
|
{
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { AppConfig } from '../../core/app-config';
|
import { AppConfig } from '../../core/app-config';
|
||||||
import { Assignment } from '../../shared/models/assignments/assignment';
|
import { Assignment } from '../../shared/models/assignments/assignment';
|
||||||
import { AssignmentRepositoryService } from 'app/core/repositories/assignments/assignment-repository.service';
|
import { AssignmentRepositoryService } from 'app/core/repositories/assignments/assignment-repository.service';
|
||||||
|
import { ViewAssignment } from './models/view-assignment';
|
||||||
|
|
||||||
export const AssignmentsAppConfig: AppConfig = {
|
export const AssignmentsAppConfig: AppConfig = {
|
||||||
name: 'assignments',
|
name: 'assignments',
|
||||||
@ -8,6 +9,7 @@ export const AssignmentsAppConfig: AppConfig = {
|
|||||||
{
|
{
|
||||||
collectionString: 'assignments/assignment',
|
collectionString: 'assignments/assignment',
|
||||||
model: Assignment,
|
model: Assignment,
|
||||||
|
viewModel: ViewAssignment,
|
||||||
searchOrder: 3,
|
searchOrder: 3,
|
||||||
repository: AssignmentRepositoryService
|
repository: AssignmentRepositoryService
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,17 @@
|
|||||||
import { BaseViewModel } from '../../base/base-view-model';
|
|
||||||
import { Assignment } from 'app/shared/models/assignments/assignment';
|
import { Assignment } from 'app/shared/models/assignments/assignment';
|
||||||
import { Tag } from 'app/shared/models/core/tag';
|
import { BaseAgendaViewModel } from 'app/site/base/base-agenda-view-model';
|
||||||
import { User } from 'app/shared/models/users/user';
|
import { SearchRepresentation } from 'app/core/ui-services/search.service';
|
||||||
import { Item } from 'app/shared/models/agenda/item';
|
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 _assignment: Assignment;
|
||||||
private _relatedUser: User[];
|
private _relatedUser: ViewUser[];
|
||||||
private _agendaItem: Item;
|
private _agendaItem: ViewItem;
|
||||||
private _tags: Tag[];
|
private _tags: ViewTag[];
|
||||||
|
|
||||||
public get id(): number {
|
public get id(): number {
|
||||||
return this._assignment ? this._assignment.id : null;
|
return this._assignment ? this._assignment.id : null;
|
||||||
@ -18,15 +21,15 @@ export class ViewAssignment extends BaseViewModel {
|
|||||||
return this._assignment;
|
return this._assignment;
|
||||||
}
|
}
|
||||||
|
|
||||||
public get candidates(): User[] {
|
public get candidates(): ViewUser[] {
|
||||||
return this._relatedUser;
|
return this._relatedUser;
|
||||||
}
|
}
|
||||||
|
|
||||||
public get agendaItem(): Item {
|
public get agendaItem(): ViewItem {
|
||||||
return this._agendaItem;
|
return this._agendaItem;
|
||||||
}
|
}
|
||||||
|
|
||||||
public get tags(): Tag[] {
|
public get tags(): ViewTag[] {
|
||||||
return this._tags;
|
return this._tags;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -41,17 +44,35 @@ export class ViewAssignment extends BaseViewModel {
|
|||||||
return this.candidates ? this.candidates.length : 0;
|
return this.candidates ? this.candidates.length : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public constructor(assignment: Assignment, relatedUser: User[], agendaItem?: Item, tags?: Tag[]) {
|
public constructor(assignment: Assignment, relatedUser?: ViewUser[], agendaItem?: ViewItem, tags?: ViewTag[]) {
|
||||||
super();
|
super('Election');
|
||||||
this._assignment = assignment;
|
this._assignment = assignment;
|
||||||
this._relatedUser = relatedUser;
|
this._relatedUser = relatedUser;
|
||||||
this._agendaItem = agendaItem;
|
this._agendaItem = agendaItem;
|
||||||
this._tags = tags;
|
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 {
|
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.
|
* 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.
|
* Should return the title for the list of speakers view.
|
||||||
*/
|
*/
|
||||||
getAgendaTitleWithType(): string;
|
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 { AgendaInformation } from 'app/site/base/agenda-information';
|
||||||
import { Searchable } from './searchable';
|
import { BaseProjectableViewModel } from './base-projectable-view-model';
|
||||||
import { SearchRepresentation } from 'app/core/ui-services/search.service';
|
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
|
* Base view class for projectable models.
|
||||||
* information for the agenda.
|
|
||||||
*/
|
*/
|
||||||
export abstract class AgendaBaseModel extends BaseModel<AgendaBaseModel> implements AgendaInformation, Searchable {
|
export abstract class BaseAgendaViewModel extends BaseProjectableViewModel implements AgendaInformation {
|
||||||
/**
|
public abstract getAgendaItem(): ViewItem;
|
||||||
* 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @returns the agenda title
|
* @returns the agenda title
|
||||||
@ -41,14 +32,10 @@ export abstract class AgendaBaseModel extends BaseModel<AgendaBaseModel> impleme
|
|||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public abstract getDetailStateURL(): string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Should return a string representation of the object, so there can be searched for.
|
* Should return a string representation of the object, so there can be searched for.
|
||||||
*/
|
*/
|
||||||
public abstract formatForSearch(): SearchRepresentation;
|
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.
|
* 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;
|
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 { 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.
|
* 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 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;
|
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 { IdentifiableProjectorElement } from 'app/shared/models/core/projector';
|
||||||
import { SlideOptions } from './slide-options';
|
import { SlideOptions } from './slide-options';
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { DetailNavigable } from './detail-navigable';
|
|
||||||
import { SearchRepresentation } from 'app/core/ui-services/search.service';
|
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.
|
* Asserts, if the given object is searchable.
|
@ -1,10 +1,18 @@
|
|||||||
import { AppConfig } from '../../core/app-config';
|
import { AppConfig } from '../../core/app-config';
|
||||||
import { ChatMessage } from '../../shared/models/core/chat-message';
|
import { ChatMessage } from '../../shared/models/core/chat-message';
|
||||||
import { ChatMessageRepositoryService } from 'app/core/repositories/common/chatmessage-repository.service';
|
import { ChatMessageRepositoryService } from 'app/core/repositories/common/chatmessage-repository.service';
|
||||||
|
import { ViewChatMessage } from './models/view-chatmessage';
|
||||||
|
|
||||||
export const CommonAppConfig: AppConfig = {
|
export const CommonAppConfig: AppConfig = {
|
||||||
name: 'common',
|
name: 'common',
|
||||||
models: [{ collectionString: 'core/chat-message', model: ChatMessage, repository: ChatMessageRepositoryService }],
|
models: [
|
||||||
|
{
|
||||||
|
collectionString: 'core/chat-message',
|
||||||
|
model: ChatMessage,
|
||||||
|
viewModel: ViewChatMessage,
|
||||||
|
repository: ChatMessageRepositoryService
|
||||||
|
}
|
||||||
|
],
|
||||||
mainMenuEntries: [
|
mainMenuEntries: [
|
||||||
{
|
{
|
||||||
route: '/',
|
route: '/',
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
import { Component, OnInit } from '@angular/core';
|
import { Component, OnInit } from '@angular/core';
|
||||||
import { Title } from '@angular/platform-browser';
|
import { Title } from '@angular/platform-browser';
|
||||||
import { BaseComponent } from 'app/base.component';
|
|
||||||
|
|
||||||
import { TranslateService } from '@ngx-translate/core'; // showcase
|
import { TranslateService } from '@ngx-translate/core'; // showcase
|
||||||
|
|
||||||
|
import { BaseComponent } from 'app/base.component';
|
||||||
|
|
||||||
// for testing the DS and BaseModel
|
// for testing the DS and BaseModel
|
||||||
import { Config } from 'app/shared/models/core/config';
|
import { Config } from 'app/shared/models/core/config';
|
||||||
import { DataStoreService } from 'app/core/core-services/data-store.service';
|
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';
|
import { BaseViewModel } from 'app/site/base/base-view-model';
|
||||||
|
|
||||||
export class ViewChatMessage extends BaseViewModel {
|
export class ViewChatMessage extends BaseViewModel {
|
||||||
private _message: ChatMessage;
|
private _chatMessage: ChatMessage;
|
||||||
|
|
||||||
public get chatmessage(): ChatMessage {
|
public get chatmessage(): ChatMessage {
|
||||||
return this._message ? this._message : null;
|
return this._chatMessage;
|
||||||
}
|
}
|
||||||
|
|
||||||
public get id(): number {
|
public get id(): number {
|
||||||
return this.chatmessage ? this.chatmessage.id : null;
|
return this.chatmessage.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
public get message(): string {
|
public get message(): string {
|
||||||
return this.chatmessage ? this.chatmessage.message : null;
|
return this.chatmessage.message;
|
||||||
}
|
}
|
||||||
|
|
||||||
public constructor(message?: ChatMessage) {
|
public constructor(message?: ChatMessage) {
|
||||||
super();
|
super('Chatmessage');
|
||||||
this._message = message;
|
this._chatMessage = message;
|
||||||
}
|
}
|
||||||
|
|
||||||
public getTitle(): string {
|
public getTitle(): string {
|
||||||
return 'Chatmessage';
|
return 'Chatmessage';
|
||||||
}
|
}
|
||||||
|
|
||||||
public updateValues(message: ChatMessage): void {
|
public updateDependencies(message: BaseViewModel): void {}
|
||||||
console.log('Update message TODO with vals:', message);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,15 +1,15 @@
|
|||||||
import { Component, OnInit, Input, ChangeDetectorRef, ChangeDetectionStrategy } from '@angular/core';
|
import { Component, OnInit, Input, ChangeDetectorRef, ChangeDetectionStrategy } from '@angular/core';
|
||||||
import { FormGroup, FormBuilder } from '@angular/forms';
|
import { FormGroup, FormBuilder } from '@angular/forms';
|
||||||
import { Title } from '@angular/platform-browser';
|
import { Title } from '@angular/platform-browser';
|
||||||
import { distinctUntilChanged } from 'rxjs/operators';
|
|
||||||
|
|
||||||
|
import { distinctUntilChanged } from 'rxjs/operators';
|
||||||
import { DateTimeAdapter } from 'ng-pick-datetime';
|
import { DateTimeAdapter } from 'ng-pick-datetime';
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
|
||||||
import { BaseComponent } from '../../../../base.component';
|
import { BaseComponent } from 'app/base.component';
|
||||||
import { ConfigRepositoryService } from '../../services/config-repository.service';
|
|
||||||
import { ParentErrorStateMatcher } from 'app/shared/parent-error-state-matcher';
|
import { ParentErrorStateMatcher } from 'app/shared/parent-error-state-matcher';
|
||||||
import { ViewConfig } from '../../models/view-config';
|
import { ViewConfig } from '../../models/view-config';
|
||||||
|
import { ConfigRepositoryService } from 'app/core/repositories/config/config-repository.service';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* List view for the categories.
|
* List view for the categories.
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { Component, OnInit } from '@angular/core';
|
import { Component, OnInit } from '@angular/core';
|
||||||
import { Title } from '@angular/platform-browser';
|
import { Title } from '@angular/platform-browser';
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
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';
|
import { BaseComponent } from '../../../../base.component';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,10 +1,13 @@
|
|||||||
import { AppConfig } from '../../core/app-config';
|
import { AppConfig } from '../../core/app-config';
|
||||||
import { Config } from '../../shared/models/core/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 = {
|
export const ConfigAppConfig: AppConfig = {
|
||||||
name: 'settings',
|
name: 'settings',
|
||||||
models: [{ collectionString: 'core/config', model: Config, repository: ConfigRepositoryService }],
|
models: [
|
||||||
|
{ collectionString: 'core/config', model: Config, viewModel: ViewConfig, repository: ConfigRepositoryService }
|
||||||
|
],
|
||||||
mainMenuEntries: [
|
mainMenuEntries: [
|
||||||
{
|
{
|
||||||
route: '/settings',
|
route: '/settings',
|
||||||
|
@ -94,7 +94,7 @@ export class ViewConfig extends BaseViewModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public constructor(config: Config) {
|
public constructor(config: Config) {
|
||||||
super();
|
super('Config');
|
||||||
this._config = config;
|
this._config = config;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -102,9 +102,7 @@ export class ViewConfig extends BaseViewModel {
|
|||||||
return this.label;
|
return this.label;
|
||||||
}
|
}
|
||||||
|
|
||||||
public updateValues(update: Config): void {
|
public updateDependencies(update: BaseViewModel): void {}
|
||||||
this._config = update;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the time this config field needs to debounce before sending a request to the server.
|
* 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 { Title } from '@angular/platform-browser';
|
||||||
import { MatSnackBar } from '@angular/material';
|
import { MatSnackBar } from '@angular/material';
|
||||||
import { Router } from '@angular/router';
|
import { Router } from '@angular/router';
|
||||||
import { Subject } from 'rxjs';
|
|
||||||
|
|
||||||
|
import { Subject } from 'rxjs';
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
|
||||||
import { ListViewBaseComponent } from 'app/site/base/list-view-base';
|
import { ListViewBaseComponent } from 'app/site/base/list-view-base';
|
||||||
import { HistoryRepositoryService } from 'app/core/repositories/history/history-repository.service';
|
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 { isDetailNavigable } from 'app/shared/models/base/detail-navigable';
|
||||||
|
|
||||||
import { ViewHistory } from '../../models/view-history';
|
import { ViewHistory } from '../../models/view-history';
|
||||||
|
import { ViewModelStoreService } from 'app/core/core-services/view-model-store.service';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A list view for the history.
|
* A list view for the history.
|
||||||
@ -42,7 +41,7 @@ export class HistoryListComponent extends ListViewBaseComponent<ViewHistory> imp
|
|||||||
translate: TranslateService,
|
translate: TranslateService,
|
||||||
matSnackBar: MatSnackBar,
|
matSnackBar: MatSnackBar,
|
||||||
private repo: HistoryRepositoryService,
|
private repo: HistoryRepositoryService,
|
||||||
private DS: DataStoreService,
|
private viewModelStore: ViewModelStoreService,
|
||||||
private router: Router
|
private router: Router
|
||||||
) {
|
) {
|
||||||
super(titleService, translate, matSnackBar);
|
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
|
* @returns the title of an old element or null if it could not be found
|
||||||
*/
|
*/
|
||||||
public getElementInfo(history: ViewHistory): string {
|
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) {
|
if (oldElementTitle) {
|
||||||
return oldElementTitle;
|
return oldElementTitle;
|
||||||
@ -104,22 +103,19 @@ export class HistoryListComponent extends ListViewBaseComponent<ViewHistory> imp
|
|||||||
*
|
*
|
||||||
* @param history Represents the selected element
|
* @param history Represents the selected element
|
||||||
*/
|
*/
|
||||||
public onClickRow(history: ViewHistory): void {
|
public async onClickRow(history: ViewHistory): Promise<void> {
|
||||||
this.repo.browseHistory(history).then(() => {
|
await this.repo.browseHistory(history);
|
||||||
const element = this.DS.get(history.getCollectionString(), history.getModelID());
|
const element = this.viewModelStore.get(history.getCollectionString(), history.getModelId());
|
||||||
let message = this.translate.instant('Temporarily reset OpenSlides to the state from ');
|
let message = this.translate.instant('Temporarily reset OpenSlides to the state from ');
|
||||||
message += history.getLocaleString('DE-de') + '. ';
|
message += history.getLocaleString('DE-de') + '. ';
|
||||||
|
|
||||||
if (isDetailNavigable(element)) {
|
if (isDetailNavigable(element)) {
|
||||||
message += this.translate.instant(
|
message += this.translate.instant('You will be redirected to the detail state of the last changed item.');
|
||||||
'You will be redirected to the detail state of the last changed item.'
|
this.raiseError(message);
|
||||||
);
|
this.router.navigate([element.getDetailStateURL()]);
|
||||||
this.raiseError(message);
|
} else {
|
||||||
this.router.navigate([element.getDetailStateURL()]);
|
this.raiseError(message);
|
||||||
} else {
|
}
|
||||||
this.raiseError(message);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { AppConfig } from '../../core/app-config';
|
import { AppConfig } from '../../core/app-config';
|
||||||
import { History } from 'app/shared/models/core/history';
|
import { History } from 'app/shared/models/core/history';
|
||||||
import { HistoryRepositoryService } from 'app/core/repositories/history/history-repository.service';
|
import { HistoryRepositoryService } from 'app/core/repositories/history/history-repository.service';
|
||||||
|
import { ViewHistory } from './models/view-history';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Config object for history.
|
* Config object for history.
|
||||||
@ -8,7 +9,14 @@ import { HistoryRepositoryService } from 'app/core/repositories/history/history-
|
|||||||
*/
|
*/
|
||||||
export const HistoryAppConfig: AppConfig = {
|
export const HistoryAppConfig: AppConfig = {
|
||||||
name: 'history',
|
name: 'history',
|
||||||
models: [{ collectionString: 'core/history', model: History, repository: HistoryRepositoryService }],
|
models: [
|
||||||
|
{
|
||||||
|
collectionString: 'core/history',
|
||||||
|
model: History,
|
||||||
|
viewModel: ViewHistory,
|
||||||
|
repository: HistoryRepositoryService
|
||||||
|
}
|
||||||
|
],
|
||||||
mainMenuEntries: [
|
mainMenuEntries: [
|
||||||
{
|
{
|
||||||
route: '/history',
|
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