Merge pull request #4901 from FinnStutzenstein/reverse

Reverse relations
This commit is contained in:
Sean 2019-08-13 10:01:41 +02:00 committed by GitHub
commit 3ccf5e9bea
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
94 changed files with 923 additions and 664 deletions

View File

@ -1,7 +1,7 @@
import { Injectable, Injector } from '@angular/core';
import { AgendaAppConfig } from '../../site/agenda/agenda.config';
import { AppConfig, ModelEntry, SearchableModelEntry } from '../app-config';
import { AppConfig, ModelEntry, SearchableModelEntry } from '../definitions/app-config';
import { BaseRepository } from 'app/core/repositories/base-repository';
import { HistoryAppConfig } from 'app/site/history/history.config';
import { ProjectorAppConfig } from 'app/site/projector/projector.config';
@ -14,7 +14,7 @@ import { ServicesToLoadOnAppsLoaded } from '../core.module';
import { MainMenuService } from './main-menu.service';
import { MediafileAppConfig } from '../../site/mediafiles/mediafile.config';
import { MotionsAppConfig } from '../../site/motions/motions.config';
import { OnAfterAppsLoaded } from '../onAfterAppsLoaded';
import { OnAfterAppsLoaded } from '../definitions/on-after-apps-loaded';
import { plugins } from '../../../plugins';
import { SearchService } from '../ui-services/search.service';
import { isSearchable } from '../../site/base/searchable';

View File

@ -5,7 +5,7 @@ import { Observable, Subject } from 'rxjs';
import { BaseModel, ModelConstructor } from '../../shared/models/base/base-model';
import { BaseRepository } from '../repositories/base-repository';
import { CollectionStringMapperService } from './collection-string-mapper.service';
import { Deferred } from '../deferred';
import { Deferred } from '../promises/deferred';
import { StorageService } from './storage.service';
/**
@ -102,6 +102,13 @@ export class UpdateSlot {
return this.deletedModels[collection] || [];
}
/**
* @returns the mapping of all deleted models
*/
public getDeletedModels(): CollectionIds {
return this.deletedModels;
}
/**
* Compares this object to another update slot.
*/
@ -221,9 +228,12 @@ export class DataStoreUpdateManagerService {
}
});
// Phase 2: updating dependencies
// Phase 2: updating dependencies (deleting ad changing in this order)
repositories.forEach(repo => {
if (repo.updateDependencies(slot.getChangedModels())) {
if (repo.updateDependenciesForDeletedModels(slot.getDeletedModels())) {
affectedRepos[repo.collectionString] = repo;
}
if (repo.updateDependenciesForChangedModels(slot.getChangedModels())) {
affectedRepos[repo.collectionString] = repo;
}
});

View File

@ -4,7 +4,7 @@ import { Injectable } from '@angular/core';
import { TranslateService } from '@ngx-translate/core';
import { OpenSlidesStatusService } from './openslides-status.service';
import { formatQueryParams, QueryParams } from '../query-params';
import { formatQueryParams, QueryParams } from '../definitions/query-params';
/**
* Enum for different HTTPMethods

View File

@ -8,10 +8,10 @@ import { Group } from 'app/shared/models/users/group';
import { ViewUser } from 'app/site/users/models/view-user';
import { CollectionStringMapperService } from './collection-string-mapper.service';
import { DataStoreService } from './data-store.service';
import { Deferred } from '../deferred';
import { Deferred } from '../promises/deferred';
import { HttpService } from './http.service';
import { OfflineService } from './offline.service';
import { OnAfterAppsLoaded } from '../onAfterAppsLoaded';
import { OnAfterAppsLoaded } from '../definitions/on-after-apps-loaded';
import { OpenSlidesStatusService } from './openslides-status.service';
import { StorageService } from './storage.service';
import { User } from '../../shared/models/users/user';

View File

@ -3,8 +3,8 @@ import { ApplicationRef, Injectable } from '@angular/core';
import { first, take } from 'rxjs/operators';
import { ConstantsService } from './constants.service';
import { Deferred } from '../deferred';
import { TimeoutPromise } from '../timeout-promise';
import { Deferred } from '../promises/deferred';
import { TimeoutPromise } from '../promises/timeout-promise';
import { WebsocketService } from './websocket.service';
interface OpenSlidesSettings {

View File

@ -0,0 +1,16 @@
import { inject, TestBed } from '@angular/core/testing';
import { E2EImportsModule } from '../../../e2e-imports.module';
import { RelationManagerService } from './relation-manager.service';
describe('RelationManagerService', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [E2EImportsModule],
providers: [RelationManagerService]
});
});
it('should be created', inject([RelationManagerService], (service: RelationManagerService) => {
expect(service).toBeTruthy();
}));
});

View File

@ -0,0 +1,275 @@
import { Injectable } from '@angular/core';
import { BaseModel } from 'app/shared/models/base/base-model';
import { BaseViewModel, ViewModelConstructor } from 'app/site/base/base-view-model';
import {
BaseOrderedRelation,
isCustomRelationDefinition,
isGenericRelationDefinition,
isNestedRelationDefinition,
isNormalRelationDefinition,
isReverseRelationDefinition,
RelationDefinition,
ReverseRelationDefinition
} from '../definitions/relations';
import { ViewModelStoreService } from './view-model-store.service';
/**
* Manages relations between view models. This service is and should only used by the
* base repository to offload maanging relations between view models.
*/
@Injectable({
providedIn: 'root'
})
export class RelationManagerService {
public constructor(private viewModelStoreService: ViewModelStoreService) {}
/**
* Sorts the array of foreign view models in the given view models for the given relation.
*/
public sortByRelation<V extends BaseViewModel, VForegin extends BaseViewModel>(
relation: BaseOrderedRelation<VForegin>,
viewModel: V
): void {
const order = relation.order;
viewModel['_' + relation.ownKey].sort((a: BaseViewModel, b: BaseViewModel) => {
if (!order || a[order] === b[order]) {
return a.id - b.id;
} else {
return a[order] - b[order];
}
});
}
/**
* Creates a view model from the given model and model ctor. All dependencies will be
* set accorting to relations.
*/
public createViewModel<M extends BaseModel, V extends BaseViewModel>(
model: M,
modelCtor: ViewModelConstructor<V>,
relations: RelationDefinition[]
): V {
const viewModel = new modelCtor(model) as V;
relations.forEach(relation => {
this.setRelationsInViewModel(model, viewModel, relation);
});
return viewModel;
}
/**
* Sets one foreign view model in the view model according to the relation and the information
* from the model.
*/
protected setRelationsInViewModel<M extends BaseModel, V extends BaseViewModel>(
model: M,
viewModel: V,
relation: RelationDefinition
): void {
// no reverse setting needed. This is done in the second phase of the ds-upgrade-manager
if (isReverseRelationDefinition(relation)) {
return;
}
if (
(relation.type === 'M2M' || relation.type === 'O2M') &&
model[relation.ownIdKey] &&
model[relation.ownIdKey].constructor === Array
) {
const foreignViewModels = this.viewModelStoreService.getMany(
relation.foreignModel,
model[relation.ownIdKey]
);
viewModel['_' + relation.ownKey] = foreignViewModels;
this.sortByRelation(relation, viewModel);
} else if (relation.type === 'M2O') {
const foreignViewModel = this.viewModelStoreService.get(relation.foreignModel, model[relation.ownIdKey]);
viewModel['_' + relation.ownKey] = foreignViewModel;
} else if (isNestedRelationDefinition(relation)) {
const foreignViewModels: BaseViewModel[] = model[relation.ownKey].map(foreignModel =>
this.createViewModel(foreignModel, relation.foreignModel, relation.relationDefinition || [])
);
viewModel['_' + relation.ownKey] = foreignViewModels;
this.sortByRelation(relation, viewModel);
} else if (isGenericRelationDefinition(relation)) {
const contentObject = this.viewModelStoreService.get<BaseViewModel>(
model[relation.ownContentObjectDataKey].collection,
model[relation.ownContentObjectDataKey].id
);
if (contentObject && relation.isVForeign(contentObject)) {
viewModel['_' + relation.ownKey] = contentObject;
}
} else if (isCustomRelationDefinition(relation)) {
relation.setRelations(model, viewModel);
}
}
/**
* Updates an own view model with an deleted model implicit given by the deletedId and
* the collection via the relation.
*
* @return true, if something was updated.
*/
public updateSingleDependencyForDeletedModel(
ownViewModel: BaseViewModel,
relation: ReverseRelationDefinition,
deletedId: number
): boolean {
// In both relations, the ownViewModel holds an array of foreignViewModels. Try to find the deleted
// foreignViewModel in this array and remove it.
if (relation.type === 'O2M' || relation.type === 'M2M') {
const ownModelArray = <any>ownViewModel['_' + relation.ownKey];
if (!ownModelArray) {
return false;
}
// We have the array of foreign view models for our own view model. Put the foreignViewModel
// into it (replace or push).
const index = ownModelArray.findIndex(foreignViewModel => foreignViewModel.id === deletedId);
if (index > -1) {
ownModelArray.splice(index, 1);
return true;
}
}
// The ownViewModel holds one foreignViewModel. Check, if it is the deleted one.
else if (relation.type === 'M2O') {
if (ownViewModel['_' + relation.ownKey] && ownViewModel['_' + relation.ownKey].id === deletedId) {
ownViewModel['_' + relation.ownKey] = null;
return true;
}
}
return false;
}
/**
* Updates an own view model with an implicit given model by the collection and changedId.
*
* @return true, if something was updated.
*/
public updateSingleDependencyForChangedModel(
ownViewModel: BaseViewModel,
relation: RelationDefinition,
collection: string,
changedId: number
): boolean {
if (isNormalRelationDefinition(relation)) {
if (relation.type === 'M2M' || relation.type === 'O2M') {
// For the side of the ownViewModel these relations are the same:
// the ownViewModel does have may foreign models and we do have a normal relation (not a
// reverse one), we just set the many-part of the relation in the ownViewModel.
if (
ownViewModel[relation.ownIdKey] &&
ownViewModel[relation.ownIdKey].constructor === Array &&
ownViewModel[relation.ownIdKey].includes(changedId) // The foreign view model belongs to us.
) {
const foreignViewModel = <any>this.viewModelStoreService.get(collection, changedId);
this.setForeingViewModelInOwnViewModelArray(foreignViewModel, ownViewModel, relation.ownKey);
return true;
}
} else if (relation.type === 'M2O') {
if (ownViewModel[relation.ownIdKey] === <any>changedId) {
// Check, if this is the matching foreign view model.
ownViewModel['_' + relation.ownKey] = <any>this.viewModelStoreService.get(collection, changedId);
return true;
}
}
} else if (isReverseRelationDefinition(relation)) {
const foreignViewModel = <any>this.viewModelStoreService.get(collection, changedId);
// The foreign model has one id. Check, if the ownViewModel is the matching view model.
// If so, add the foreignViewModel to the array from the ownViewModel (with many foreignViewModels)
if (relation.type === 'O2M') {
if (foreignViewModel[relation.foreignIdKey] === ownViewModel.id) {
this.setForeingViewModelInOwnViewModelArray(foreignViewModel, ownViewModel, relation.ownKey);
return true;
}
}
// The foreign model should hold an array of ids. If the ownViewModel is in it, the foreignViewModel must
// be included into the array from the ownViewModel (with many foreignViewModels)
else if (relation.type === 'M2M') {
if (
foreignViewModel[relation.foreignIdKey] &&
foreignViewModel[relation.foreignIdKey].constructor === Array &&
foreignViewModel[relation.foreignIdKey].includes(ownViewModel.id)
) {
this.setForeingViewModelInOwnViewModelArray(foreignViewModel, ownViewModel, relation.ownKey);
return true;
}
}
// The foreign model should hold an array of ids. If the ownViewModel is in it, the foreignViewModel is the
// one and only matching model for the ownViewModel
else if (relation.type === 'M2O') {
if (
foreignViewModel[relation.foreignIdKey] &&
foreignViewModel[relation.foreignIdKey].constructor === Array &&
foreignViewModel[relation.foreignIdKey].includes(ownViewModel.id)
) {
ownViewModel['_' + relation.ownKey] = foreignViewModel;
return true;
}
}
} else if (isNestedRelationDefinition(relation)) {
let updated = false;
(relation.relationDefinition || []).forEach(nestedRelation => {
const nestedViewModels = ownViewModel[relation.ownKey] as BaseViewModel[];
nestedViewModels.forEach(nestedViewModel => {
if (
this.updateSingleDependencyForChangedModel(
nestedViewModel,
nestedRelation,
collection,
changedId
)
) {
updated = true;
}
});
});
return updated;
} else if (isCustomRelationDefinition(relation)) {
const foreignViewModel = <any>this.viewModelStoreService.get(collection, changedId);
return relation.updateDependency(ownViewModel, foreignViewModel);
} else if (isGenericRelationDefinition(relation)) {
const foreignModel = <any>this.viewModelStoreService.get(collection, changedId);
if (
foreignModel &&
foreignModel.collectionString === ownViewModel[relation.ownContentObjectDataKey].collection &&
foreignModel.id === ownViewModel[relation.ownContentObjectDataKey].id
) {
if (relation.isVForeign(foreignModel)) {
ownViewModel['_' + relation.ownKey] = foreignModel;
return true;
} else {
console.warn(`The object is not an ${relation.VForeignVerbose}:` + foreignModel);
}
}
}
return false;
}
private setForeingViewModelInOwnViewModelArray(
foreignViewModel: BaseViewModel,
ownViewModel: BaseViewModel,
ownKey: string
): void {
let ownViewModelArray = <any>ownViewModel['_' + ownKey];
if (!ownViewModelArray) {
ownViewModel['_' + ownKey] = [];
ownViewModelArray = <any>ownViewModel['_' + ownKey]; // get the new reference
}
// We have the array of foreign view models for our own view model. Put the foreignViewModel
// into it (replace or push).
const index = ownViewModelArray.findIndex(_foreignViewModel => _foreignViewModel.id === foreignViewModel.id);
if (index < 0) {
ownViewModelArray.push(foreignViewModel);
} else {
ownViewModelArray[index] = foreignViewModel;
}
}
}

View File

@ -9,7 +9,7 @@ import { take } from 'rxjs/operators';
import { TextDecoder, TextEncoder } from 'text-encoding';
import { OpenSlidesStatusService } from './openslides-status.service';
import { formatQueryParams, QueryParams } from '../query-params';
import { formatQueryParams, QueryParams } from '../definitions/query-params';
/**
* The generic message format in which messages are send and recieved by the server.

View File

@ -4,7 +4,7 @@ import { Title } from '@angular/platform-browser';
import { ProjectionDialogComponent } from 'app/shared/components/projection-dialog/projection-dialog.component';
import { ChoiceDialogComponent } from '../shared/components/choice-dialog/choice-dialog.component';
import { OnAfterAppsLoaded } from './onAfterAppsLoaded';
import { OnAfterAppsLoaded } from './definitions/on-after-apps-loaded';
import { OperatorService } from './core-services/operator.service';
import { PromptDialogComponent } from '../shared/components/prompt-dialog/prompt-dialog.component';

View File

@ -1,10 +1,10 @@
import { Type } from '@angular/core';
import { BaseViewModel, ViewModelConstructor } from 'app/site/base/base-view-model';
import { BaseModel, ModelConstructor } from '../shared/models/base/base-model';
import { BaseRepository } from './repositories/base-repository';
import { MainMenuEntry } from './core-services/main-menu.service';
import { Searchable } from '../site/base/searchable';
import { BaseModel, ModelConstructor } from '../../shared/models/base/base-model';
import { BaseRepository } from '../repositories/base-repository';
import { MainMenuEntry } from '../core-services/main-menu.service';
import { Searchable } from '../../site/base/searchable';
interface BaseModelEntry {
collectionString: string;

View File

@ -0,0 +1,190 @@
import { BaseModel } from 'app/shared/models/base/base-model';
import { BaseViewModel, ViewModelConstructor } from 'app/site/base/base-view-model';
// All "standard" relations.
export type RelationDefinition<VForeign extends BaseViewModel = BaseViewModel> =
| NormalRelationDefinition<VForeign>
| ReverseRelationDefinition<VForeign>
| NestedRelationDefinition<VForeign>
| CustomRelationDefinition<VForeign>
| GenericRelationDefinition<VForeign>;
interface BaseRelationDefinition<VForeign extends BaseViewModel> {
/**
* The name of the property, where the foreign view model should be accessable.
* Note, that this must be a getter to a private variable `_<ownKey`!
*
* E.g. `category`. (private variable `_category`)
*/
ownKey: string;
/**
* The model on the other side of the relation.
*/
foreignModel: ViewModelConstructor<VForeign>;
}
export interface BaseOrderedRelation<VForeign extends BaseViewModel> extends BaseRelationDefinition<VForeign> {
/**
* Provide an extra key (holding a number) to order by.
* If the value is equal or no order key is given, the models
* will be sorted by id.
*/
order?: string;
}
interface BaseNormalRelationDefinition<VForeign extends BaseViewModel> extends BaseRelationDefinition<VForeign> {
/**
* This is the key in the own model where the id(s) are given. Must be present in
* the model and view model. E.g. `category_id` in a motion.
*/
ownIdKey: string;
}
/**
* These relations has to be read as in an ER-model. The right side is always the
* model where the relation is defined.
* - M2O: From this model to another one, where this model is the right (many).
* E.g. motions<->categories: One motions has One category; One category has
* Many motions.
* - O2M: Reverse relation to M2O. E.g. defined for categories: One category has
* Many motions and One motion has One category.
* - M2M: M2M relation from this to another model.
*/
interface NormalM2MRelationDefinition<VForeign extends BaseViewModel>
extends BaseNormalRelationDefinition<VForeign>,
BaseOrderedRelation<VForeign> {
type: 'M2M';
}
interface NormalO2MRelationDefinition<VForeign extends BaseViewModel>
extends BaseNormalRelationDefinition<VForeign>,
BaseOrderedRelation<VForeign> {
type: 'O2M';
}
interface NormalM2ORelationDefinition<VForeign extends BaseViewModel> extends BaseNormalRelationDefinition<VForeign> {
type: 'M2O';
}
export type NormalRelationDefinition<VForeign extends BaseViewModel = BaseViewModel> =
| NormalM2MRelationDefinition<VForeign>
| NormalO2MRelationDefinition<VForeign>
| NormalM2ORelationDefinition<VForeign>;
export function isNormalRelationDefinition(obj: RelationDefinition): obj is NormalRelationDefinition<BaseViewModel> {
const relation = obj as NormalRelationDefinition<BaseViewModel>;
return (relation.type === 'M2O' || relation.type === 'O2M' || relation.type === 'M2M') && !!relation.ownIdKey;
}
interface BaseReverseRelationDefinition<VForeign extends BaseViewModel> {
/**
* The key with the id(s) is given in the foreign model. Must be present in
* the model and view model. E.g. `category_id` from a motion but the relation is
* defined for a category.
*/
foreignIdKey: string;
/**
* The name of the property, where the foreign view model should be accessable.
* Note, that this must be a getter to a private variable `_<ownKey`!
*
* E.g. `category`. (private variable `_category`)
*/
ownKey: string;
/**
* The model on the other side of the relation.
*/
foreignModel: ViewModelConstructor<VForeign>;
}
interface ReverseM2MRelationDefinition<VForeign extends BaseViewModel>
extends BaseReverseRelationDefinition<VForeign>,
BaseOrderedRelation<VForeign> {
type: 'M2M';
}
interface ReverseO2MRelationDefinition<VForeign extends BaseViewModel>
extends BaseReverseRelationDefinition<VForeign>,
BaseOrderedRelation<VForeign> {
type: 'O2M';
}
interface ReverseM2ORelationDefinition<VForeign extends BaseViewModel> extends BaseReverseRelationDefinition<VForeign> {
type: 'M2O';
}
export type ReverseRelationDefinition<VForeign extends BaseViewModel = BaseViewModel> =
| ReverseM2MRelationDefinition<VForeign>
| ReverseO2MRelationDefinition<VForeign>
| ReverseM2ORelationDefinition<VForeign>;
export function isReverseRelationDefinition(obj: RelationDefinition): obj is ReverseRelationDefinition<BaseViewModel> {
const relation = obj as ReverseRelationDefinition<BaseViewModel>;
return (relation.type === 'M2O' || relation.type === 'O2M' || relation.type === 'M2M') && !!relation.foreignIdKey;
}
/**
* Nested relations in the REST-API. For the most values see
* `NormalRelationDefinition`.
*/
interface NestedRelationDefinition<VForeign extends BaseViewModel> extends BaseOrderedRelation<VForeign> {
type: 'nested';
ownKey: string;
/**
* The nested relations.
*/
relationDefinition?: RelationDefinition[];
}
export function isNestedRelationDefinition(obj: RelationDefinition): obj is NestedRelationDefinition<BaseViewModel> {
return obj.type === 'nested';
}
interface GenericRelationDefinition<VForeign extends BaseViewModel = BaseViewModel> {
type: 'generic';
/**
* The key where the model and view model holds the ContentObject (object with collection and id).
* Similar to ownIdKey.
*/
ownContentObjectDataKey: string;
/**
* The key where to but the content object.
*/
ownKey: string;
possibleModels: ViewModelConstructor<BaseViewModel>[];
isVForeign: (obj: any) => obj is VForeign;
VForeignVerbose: string;
}
export function isGenericRelationDefinition(obj: RelationDefinition): obj is GenericRelationDefinition<BaseViewModel> {
return obj.type === 'generic';
}
/**
* A custom relation with callbacks with things todo.
*/
interface CustomRelationDefinition<VForeign extends BaseViewModel> {
type: 'custom';
foreignModel: ViewModelConstructor<VForeign>;
/**
* Called, when the view model is created from the model.
*/
setRelations: (model: BaseModel, viewModel: BaseViewModel) => void;
/**
* Called, when the dependency was updated.
*/
updateDependency: (ownViewModel: BaseViewModel, foreignViewModel: VForeign) => boolean;
}
export function isCustomRelationDefinition(obj: RelationDefinition): obj is CustomRelationDefinition<BaseViewModel> {
return obj.type === 'custom';
}

View File

@ -4,7 +4,9 @@ import { TranslateService } from '@ngx-translate/core';
import { DataSendService } from 'app/core/core-services/data-send.service';
import { HttpService } from 'app/core/core-services/http.service';
import { RelationManagerService } from 'app/core/core-services/relation-manager.service';
import { ViewModelStoreService } from 'app/core/core-services/view-model-store.service';
import { RelationDefinition } from 'app/core/definitions/relations';
import { ConfigService } from 'app/core/ui-services/config.service';
import { TreeIdNode } from 'app/core/ui-services/tree.service';
import { Item } from 'app/shared/models/agenda/item';
@ -19,18 +21,19 @@ import {
import { ViewMotion } from 'app/site/motions/models/view-motion';
import { ViewMotionBlock } from 'app/site/motions/models/view-motion-block';
import { ViewTopic } from 'app/site/topics/models/view-topic';
import { BaseHasContentObjectRepository, GenericRelationDefinition } from '../base-has-content-object-repository';
import { BaseHasContentObjectRepository } from '../base-has-content-object-repository';
import { BaseIsAgendaItemContentObjectRepository } from '../base-is-agenda-item-content-object-repository';
import { RelationDefinition } from '../base-repository';
import { CollectionStringMapperService } from '../../core-services/collection-string-mapper.service';
import { DataStoreService } from '../../core-services/data-store.service';
const ItemRelations: (RelationDefinition | GenericRelationDefinition)[] = [
const ItemRelations: RelationDefinition[] = [
{
type: 'generic',
possibleModels: [ViewMotion, ViewMotionBlock, ViewTopic, ViewAssignment],
isVForeign: isBaseViewModelWithAgendaItem,
VForeignVerbose: 'BaseViewModelWithAgendaItem'
VForeignVerbose: 'BaseViewModelWithAgendaItem',
ownContentObjectDataKey: 'contentObjectData',
ownKey: 'contentObject'
}
];
@ -64,10 +67,11 @@ export class ItemRepositoryService extends BaseHasContentObjectRepository<
mapperService: CollectionStringMapperService,
viewModelStoreService: ViewModelStoreService,
translate: TranslateService,
relationManager: RelationManagerService,
private httpService: HttpService,
private config: ConfigService
) {
super(DS, dataSend, mapperService, viewModelStoreService, translate, Item, ItemRelations);
super(DS, dataSend, mapperService, viewModelStoreService, translate, relationManager, Item, ItemRelations);
this.setSortFunction((a, b) => a.weight - b.weight);
}

View File

@ -4,7 +4,9 @@ import { TranslateService } from '@ngx-translate/core';
import { DataSendService } from 'app/core/core-services/data-send.service';
import { HttpService } from 'app/core/core-services/http.service';
import { RelationManagerService } from 'app/core/core-services/relation-manager.service';
import { ViewModelStoreService } from 'app/core/core-services/view-model-store.service';
import { RelationDefinition } from 'app/core/definitions/relations';
import { ListOfSpeakers } from 'app/shared/models/agenda/list-of-speakers';
import { Identifiable } from 'app/shared/models/base/identifiable';
import { ListOfSpeakersTitleInformation, ViewListOfSpeakers } from 'app/site/agenda/models/view-list-of-speakers';
@ -19,19 +21,20 @@ import { ViewMotion } from 'app/site/motions/models/view-motion';
import { ViewMotionBlock } from 'app/site/motions/models/view-motion-block';
import { ViewTopic } from 'app/site/topics/models/view-topic';
import { ViewUser } from 'app/site/users/models/view-user';
import { BaseHasContentObjectRepository, GenericRelationDefinition } from '../base-has-content-object-repository';
import { BaseHasContentObjectRepository } from '../base-has-content-object-repository';
import { BaseIsListOfSpeakersContentObjectRepository } from '../base-is-list-of-speakers-content-object-repository';
import { RelationDefinition } from '../base-repository';
import { CollectionStringMapperService } from '../../core-services/collection-string-mapper.service';
import { DataStoreService } from '../../core-services/data-store.service';
import { ItemRepositoryService } from './item-repository.service';
const ListOfSpeakersRelations: (RelationDefinition | GenericRelationDefinition)[] = [
const ListOfSpeakersRelations: RelationDefinition[] = [
{
type: 'generic',
possibleModels: [ViewMotion, ViewMotionBlock, ViewTopic, ViewAssignment, ViewMediafile],
isVForeign: isBaseViewModelWithListOfSpeakers,
VForeignVerbose: 'BaseViewModelWithListOfSpeakers'
VForeignVerbose: 'BaseViewModelWithListOfSpeakers',
ownContentObjectDataKey: 'contentObjectData',
ownKey: 'contentObject'
},
{
type: 'nested',
@ -40,7 +43,7 @@ const ListOfSpeakersRelations: (RelationDefinition | GenericRelationDefinition)[
order: 'weight',
relationDefinition: [
{
type: 'O2M',
type: 'M2O',
ownIdKey: 'user_id',
ownKey: 'user',
foreignModel: ViewUser
@ -79,10 +82,20 @@ export class ListOfSpeakersRepositoryService extends BaseHasContentObjectReposit
mapperService: CollectionStringMapperService,
viewModelStoreService: ViewModelStoreService,
translate: TranslateService,
relationManager: RelationManagerService,
private httpService: HttpService,
private itemRepo: ItemRepositoryService
) {
super(DS, dataSend, mapperService, viewModelStoreService, translate, ListOfSpeakers, ListOfSpeakersRelations);
super(
DS,
dataSend,
mapperService,
viewModelStoreService,
translate,
relationManager,
ListOfSpeakers,
ListOfSpeakersRelations
);
}
public getVerboseName = (plural: boolean = false) => {

View File

@ -4,7 +4,9 @@ import { TranslateService } from '@ngx-translate/core';
import { DataSendService } from 'app/core/core-services/data-send.service';
import { HttpService } from 'app/core/core-services/http.service';
import { RelationManagerService } from 'app/core/core-services/relation-manager.service';
import { ViewModelStoreService } from 'app/core/core-services/view-model-store.service';
import { RelationDefinition } from 'app/core/definitions/relations';
import { Assignment } from 'app/shared/models/assignments/assignment';
import { AssignmentPoll } from 'app/shared/models/assignments/assignment-poll';
import { AssignmentTitleInformation, ViewAssignment } from 'app/site/assignments/models/view-assignment';
@ -15,7 +17,6 @@ import { ViewMediafile } from 'app/site/mediafiles/models/view-mediafile';
import { ViewTag } from 'app/site/tags/models/view-tag';
import { ViewUser } from 'app/site/users/models/view-user';
import { BaseIsAgendaItemAndListOfSpeakersContentObjectRepository } from '../base-is-agenda-item-and-list-of-speakers-content-object-repository';
import { RelationDefinition } from '../base-repository';
import { CollectionStringMapperService } from '../../core-services/collection-string-mapper.service';
import { DataStoreService } from '../../core-services/data-store.service';
@ -39,7 +40,7 @@ const AssignmentRelations: RelationDefinition[] = [
order: 'weight',
relationDefinition: [
{
type: 'O2M',
type: 'M2O',
ownIdKey: 'user_id',
ownKey: 'user',
foreignModel: ViewUser
@ -58,7 +59,7 @@ const AssignmentRelations: RelationDefinition[] = [
order: 'weight',
relationDefinition: [
{
type: 'O2M',
type: 'M2O',
ownIdKey: 'user_id',
ownKey: 'user',
foreignModel: ViewUser
@ -104,10 +105,20 @@ export class AssignmentRepositoryService extends BaseIsAgendaItemAndListOfSpeake
dataSend: DataSendService,
mapperService: CollectionStringMapperService,
viewModelStoreService: ViewModelStoreService,
protected translate: TranslateService,
translate: TranslateService,
relationManager: RelationManagerService,
private httpService: HttpService
) {
super(DS, dataSend, mapperService, viewModelStoreService, translate, Assignment, AssignmentRelations);
super(
DS,
dataSend,
mapperService,
viewModelStoreService,
translate,
relationManager,
Assignment,
AssignmentRelations
);
}
public getTitle = (titleInformation: AssignmentTitleInformation) => {

View File

@ -1,25 +1,8 @@
import { TranslateService } from '@ngx-translate/core';
import { ModelConstructor } from 'app/shared/models/base/base-model';
import { BaseModelWithContentObject } from 'app/shared/models/base/base-model-with-content-object';
import { ContentObject } from 'app/shared/models/base/content-object';
import { BaseViewModel, TitleInformation, ViewModelConstructor } from 'app/site/base/base-view-model';
import { BaseViewModel, TitleInformation } from 'app/site/base/base-view-model';
import { BaseViewModelWithContentObject } from 'app/site/base/base-view-model-with-content-object';
import { BaseRepository, RelationDefinition } from './base-repository';
import { CollectionStringMapperService } from '../core-services/collection-string-mapper.service';
import { DataSendService } from '../core-services/data-send.service';
import { DataStoreService } from '../core-services/data-store.service';
import { ViewModelStoreService } from '../core-services/view-model-store.service';
/**
* A generic relation for models with a content_object
*/
export interface GenericRelationDefinition<VForeign extends BaseViewModel = BaseViewModel> {
type: 'generic';
possibleModels: ViewModelConstructor<BaseViewModel>[];
isVForeign: (obj: any) => obj is VForeign;
VForeignVerbose: string;
}
import { BaseRepository } from './base-repository';
/**
* A base repository for objects that *have* content objects, e.g. items and lists of speakers.
@ -40,99 +23,6 @@ export abstract class BaseHasContentObjectRepository<
};
} = {};
public constructor(
DS: DataStoreService,
dataSend: DataSendService,
collectionStringMapperService: CollectionStringMapperService,
viewModelStoreService: ViewModelStoreService,
translate: TranslateService,
baseModelCtor: ModelConstructor<M>,
relationDefinitions: (RelationDefinition | GenericRelationDefinition)[] = []
) {
super(DS, dataSend, collectionStringMapperService, viewModelStoreService, translate, baseModelCtor, <
RelationDefinition[]
>relationDefinitions); // This cast "hides" the new generic relation from the base repository. Typescript can't handle this...
}
protected _groupRelationsByCollections(
relation: RelationDefinition | GenericRelationDefinition,
baseRelation: RelationDefinition
): void {
if (relation.type === 'generic') {
relation.possibleModels.forEach(ctor => {
const collection = ctor.COLLECTIONSTRING;
if (!this.relationsByCollection[collection]) {
this.relationsByCollection[collection] = [];
}
// The cast to any is needed to convince Typescript, that a GenericRelationDefinition can also
// be used as a RelationDefinition
this.relationsByCollection[collection].push(<any>baseRelation);
});
} else {
super._groupRelationsByCollections(relation, baseRelation);
}
}
/**
* Adds the generic relation.
*/
protected updateSingleDependency(
ownViewModel: V,
relation: RelationDefinition | GenericRelationDefinition,
collection: string,
changedId: number
): boolean {
if (relation.type === 'generic') {
const foreignModel = <any>this.viewModelStoreService.get(collection, changedId);
if (
foreignModel &&
foreignModel.collectionString === ownViewModel.contentObjectData.collection &&
foreignModel.id === ownViewModel.contentObjectData.id
) {
if (relation.isVForeign(foreignModel)) {
(<any>ownViewModel)._contentObject = foreignModel;
return true;
} else {
console.warn(`The object is not an ${relation.VForeignVerbose}:` + foreignModel);
}
// TODO: set reverse
}
return false;
} else {
return super.updateSingleDependency(ownViewModel, relation, collection, changedId);
}
}
/**
* Adds the generic relation.
*/
protected setRelationsInViewModel<K extends BaseViewModel = V>(
model: M,
viewModel: K,
relation: RelationDefinition | GenericRelationDefinition
): void {
if (relation.type === 'generic') {
(<any>viewModel)._contentObject = this.getContentObject(model, relation);
} else {
super.setRelationsInViewModel(model, viewModel, relation);
}
}
/**
* Tries to get the content object (as a view model) from the given model and relation.
*/
protected getContentObject(model: M, relation: GenericRelationDefinition): BaseViewModel {
const contentObject = this.viewModelStoreService.get<BaseViewModel>(
model.content_object.collection,
model.content_object.id
);
if (!contentObject || !relation.isVForeign(contentObject)) {
return null;
}
return contentObject;
}
/**
* Returns the object with has the given content object as the content object.
*

View File

@ -1,6 +1,4 @@
import { TranslateService } from '@ngx-translate/core';
import { BaseModel, ModelConstructor } from 'app/shared/models/base/base-model';
import { BaseModel } from 'app/shared/models/base/base-model';
import { ViewItem } from 'app/site/agenda/models/view-item';
import { ViewListOfSpeakers } from 'app/site/agenda/models/view-list-of-speakers';
import { BaseProjectableViewModel } from 'app/site/base/base-projectable-view-model';
@ -17,11 +15,7 @@ import {
IBaseIsListOfSpeakersContentObjectRepository,
isBaseIsListOfSpeakersContentObjectRepository
} from './base-is-list-of-speakers-content-object-repository';
import { BaseRepository, RelationDefinition } from './base-repository';
import { CollectionStringMapperService } from '../core-services/collection-string-mapper.service';
import { DataSendService } from '../core-services/data-send.service';
import { DataStoreService } from '../core-services/data-store.service';
import { ViewModelStoreService } from '../core-services/view-model-store.service';
import { BaseRepository } from './base-repository';
export function isBaseIsAgendaItemAndListOfSpeakersContentObjectRepository(
obj: any
@ -43,35 +37,15 @@ export abstract class BaseIsAgendaItemAndListOfSpeakersContentObjectRepository<
implements
IBaseIsAgendaItemContentObjectRepository<V, M, T>,
IBaseIsListOfSpeakersContentObjectRepository<V, M, T> {
public constructor(
DS: DataStoreService,
dataSend: DataSendService,
collectionStringMapperService: CollectionStringMapperService,
viewModelStoreService: ViewModelStoreService,
translate: TranslateService,
baseModelCtor: ModelConstructor<M>,
relationDefinitions?: RelationDefinition[]
) {
super(
DS,
dataSend,
collectionStringMapperService,
viewModelStoreService,
translate,
baseModelCtor,
relationDefinitions
);
}
protected groupRelationsByCollections(): void {
this.relationDefinitions.push({
type: 'O2M',
type: 'M2O',
ownIdKey: 'agenda_item_id',
ownKey: 'item',
foreignModel: ViewItem
});
this.relationDefinitions.push({
type: 'O2M',
type: 'M2O',
ownIdKey: 'list_of_speakers_id',
ownKey: 'list_of_speakers',
foreignModel: ViewListOfSpeakers

View File

@ -6,10 +6,12 @@ import {
TitleInformationWithAgendaItem
} from 'app/site/base/base-view-model-with-agenda-item';
import { BaseModel, ModelConstructor } from '../../shared/models/base/base-model';
import { BaseRepository, RelationDefinition } from './base-repository';
import { BaseRepository } from './base-repository';
import { CollectionStringMapperService } from '../core-services/collection-string-mapper.service';
import { DataSendService } from '../core-services/data-send.service';
import { DataStoreService } from '../core-services/data-store.service';
import { RelationManagerService } from '../core-services/relation-manager.service';
import { RelationDefinition } from '../definitions/relations';
import { ViewModelStoreService } from '../core-services/view-model-store.service';
export function isBaseIsAgendaItemContentObjectRepository(
@ -45,6 +47,7 @@ export abstract class BaseIsAgendaItemContentObjectRepository<
collectionStringMapperService: CollectionStringMapperService,
viewModelStoreService: ViewModelStoreService,
translate: TranslateService,
relationManager: RelationManagerService,
baseModelCtor: ModelConstructor<M>,
relationDefinitions?: RelationDefinition[]
) {
@ -54,6 +57,7 @@ export abstract class BaseIsAgendaItemContentObjectRepository<
collectionStringMapperService,
viewModelStoreService,
translate,
relationManager,
baseModelCtor,
relationDefinitions
);
@ -61,7 +65,7 @@ export abstract class BaseIsAgendaItemContentObjectRepository<
protected groupRelationsByCollections(): void {
this.relationDefinitions.push({
type: 'O2M',
type: 'M2O',
ownIdKey: 'agenda_item_id',
ownKey: 'item',
foreignModel: ViewItem

View File

@ -3,11 +3,13 @@ import { TranslateService } from '@ngx-translate/core';
import { ViewListOfSpeakers } from 'app/site/agenda/models/view-list-of-speakers';
import { BaseViewModelWithListOfSpeakers } from 'app/site/base/base-view-model-with-list-of-speakers';
import { BaseModel, ModelConstructor } from '../../shared/models/base/base-model';
import { BaseRepository, RelationDefinition } from './base-repository';
import { BaseRepository } from './base-repository';
import { TitleInformation } from '../../site/base/base-view-model';
import { CollectionStringMapperService } from '../core-services/collection-string-mapper.service';
import { DataSendService } from '../core-services/data-send.service';
import { DataStoreService } from '../core-services/data-store.service';
import { RelationManagerService } from '../core-services/relation-manager.service';
import { RelationDefinition } from '../definitions/relations';
import { ViewModelStoreService } from '../core-services/view-model-store.service';
export function isBaseIsListOfSpeakersContentObjectRepository(
@ -43,6 +45,7 @@ export abstract class BaseIsListOfSpeakersContentObjectRepository<
collectionStringMapperService: CollectionStringMapperService,
viewModelStoreService: ViewModelStoreService,
translate: TranslateService,
relationManager: RelationManagerService,
baseModelCtor: ModelConstructor<M>,
relationDefinitions?: RelationDefinition[]
) {
@ -52,6 +55,7 @@ export abstract class BaseIsListOfSpeakersContentObjectRepository<
collectionStringMapperService,
viewModelStoreService,
translate,
relationManager,
baseModelCtor,
relationDefinitions
);
@ -59,7 +63,7 @@ export abstract class BaseIsListOfSpeakersContentObjectRepository<
protected groupRelationsByCollections(): void {
this.relationDefinitions.push({
type: 'O2M',
type: 'M2O',
ownIdKey: 'list_of_speakers_id',
ownKey: 'list_of_speakers',
foreignModel: ViewListOfSpeakers

View File

@ -9,92 +9,16 @@ import { CollectionStringMapperService } from '../core-services/collection-strin
import { DataSendService } from '../core-services/data-send.service';
import { CollectionIds, DataStoreService } from '../core-services/data-store.service';
import { Identifiable } from '../../shared/models/base/identifiable';
import { OnAfterAppsLoaded } from '../onAfterAppsLoaded';
import { OnAfterAppsLoaded } from '../definitions/on-after-apps-loaded';
import { RelationManagerService } from '../core-services/relation-manager.service';
import {
isNormalRelationDefinition,
isReverseRelationDefinition,
RelationDefinition,
ReverseRelationDefinition
} from '../definitions/relations';
import { ViewModelStoreService } from '../core-services/view-model-store.service';
// All "standard" relations.
export type RelationDefinition<VForeign extends BaseViewModel = BaseViewModel> =
| NormalRelationDefinition<VForeign>
| NestedRelationDefinition<VForeign>
| CustomRelationDefinition<VForeign>;
/**
* Normal relations.
*/
interface NormalRelationDefinition<VForeign extends BaseViewModel> {
/**
* - O2M: From this model to another one, where this model is the One-side.
* E.g. motions<->categories: One motions has One category; One category has
* Many motions
* - M2M: M2M relation from this to another model.
*/
type: 'M2M' | 'O2M';
/**
* The key where the id(s) are given. Must be present in the model and view model. E.g. `category_id`.
*/
ownIdKey: string;
/**
* The name of the property, where the foreign view model should be accessable.
* Note, that this must be a getter to a private variable `_<ownKey`!
*
* E.g. `category`. (private variable `_category`)
*/
ownKey: string;
/**
* The model on the other side of the relation.
*/
foreignModel: ViewModelConstructor<VForeign>;
/**
* TODO: reverse relations.
*/
foreignKey?: keyof VForeign;
}
/**
* Nested relations in the REST-API. For the most values see
* `NormalRelationDefinition`.
*/
interface NestedRelationDefinition<VForeign extends BaseViewModel> {
type: 'nested';
ownKey: string;
foreignModel: ViewModelConstructor<VForeign>;
foreignKey?: keyof VForeign;
/**
* The nested relations.
*/
relationDefinition?: RelationDefinition[];
/**
* Provide an extra key (holding a number) to order by.
* If the value is equal or no order key is given, the models
* will be sorted by id.
*/
order?: string;
}
/**
* A custom relation with callbacks with things todo.
*/
interface CustomRelationDefinition<VForeign extends BaseViewModel> {
type: 'custom';
foreignModel: ViewModelConstructor<VForeign>;
/**
* Called, when the view model is created from the model.
*/
setRelations: (model: BaseModel, viewModel: BaseViewModel) => void;
/**
* Called, when the dependency was updated.
*/
updateDependency: (ownViewModel: BaseViewModel, foreignViewModel: VForeign) => boolean;
}
export abstract class BaseRepository<V extends BaseViewModel & T, M extends BaseModel, T extends TitleInformation>
implements OnAfterAppsLoaded, Collection {
/**
@ -163,6 +87,8 @@ export abstract class BaseRepository<V extends BaseViewModel & T, M extends Base
*/
protected relationsByCollection: { [collection: string]: RelationDefinition<BaseViewModel>[] } = {};
protected reverseRelationsByCollection: { [collection: string]: ReverseRelationDefinition<BaseViewModel>[] } = {};
/**
* The view model ctor of the encapsulated view model.
*/
@ -183,12 +109,14 @@ export abstract class BaseRepository<V extends BaseViewModel & T, M extends Base
protected collectionStringMapperService: CollectionStringMapperService,
protected viewModelStoreService: ViewModelStoreService,
protected translate: TranslateService,
protected relationManager: RelationManagerService,
protected baseModelCtor: ModelConstructor<M>,
protected relationDefinitions: RelationDefinition<BaseViewModel>[] = []
) {
this._collectionString = baseModelCtor.COLLECTIONSTRING;
this.groupRelationsByCollections();
this.buildReverseRelationsGrouping();
// All data is piped through an auditTime of 1ms. This is to prevent massive
// updates, if e.g. an autoupdate with a lot motions come in. The result is just one
@ -219,13 +147,37 @@ export abstract class BaseRepository<V extends BaseViewModel & T, M extends Base
(relation.relationDefinition || []).forEach(nestedRelation => {
this._groupRelationsByCollections(nestedRelation, baseRelation);
});
} else if (relation.type === 'O2M' || relation.type === 'M2M' || relation.type === 'custom') {
} else if (
relation.type === 'M2O' ||
relation.type === 'M2M' ||
relation.type === 'O2M' ||
relation.type === 'custom'
) {
const collection = relation.foreignModel.COLLECTIONSTRING;
if (!this.relationsByCollection[collection]) {
this.relationsByCollection[collection] = [];
}
this.relationsByCollection[collection].push(baseRelation);
} else if (relation.type === 'generic') {
relation.possibleModels.forEach(ctor => {
const collection = ctor.COLLECTIONSTRING;
if (!this.relationsByCollection[collection]) {
this.relationsByCollection[collection] = [];
}
this.relationsByCollection[collection].push(baseRelation);
});
}
}
protected buildReverseRelationsGrouping(): void {
Object.keys(this.relationsByCollection).forEach(collection => {
const reverseRelations = this.relationsByCollection[collection].filter(relation =>
isReverseRelationDefinition(relation)
) as ReverseRelationDefinition<BaseViewModel>[];
if (reverseRelations.length) {
this.reverseRelationsByCollection[collection] = reverseRelations;
}
});
}
public onAfterAppsLoaded(): void {
@ -274,82 +226,27 @@ export abstract class BaseRepository<V extends BaseViewModel & T, M extends Base
* are assigned to the new view model.
*/
protected createViewModelWithTitles(model: M): V {
const viewModel = this.createViewModel(model, this.baseViewModelCtor, this.relationDefinitions);
const viewModel = this.relationManager.createViewModel(model, this.baseViewModelCtor, this.relationDefinitions);
viewModel.getTitle = () => this.getTitle(viewModel);
viewModel.getListTitle = () => this.getListTitle(viewModel);
viewModel.getVerboseName = this.getVerboseName;
return viewModel;
}
/**
* Creates a view model from the given model and model ctor. All dependencies will be
* set accorting to relations.
*/
protected createViewModel<K extends BaseViewModel = V>(
model: M,
modelCtor: ViewModelConstructor<K>,
relations: RelationDefinition[]
): K {
const viewModel = new modelCtor(model) as K;
// no reverse setting needed
relations.forEach(relation => {
this.setRelationsInViewModel(model, viewModel, relation);
});
return viewModel;
}
/**
* Sets one foreign view model in the view model according to the relation and the information
* from the model.
*/
protected setRelationsInViewModel<K extends BaseViewModel = V>(
model: M,
viewModel: K,
relation: RelationDefinition
): void {
if (relation.type === 'M2M' && model[relation.ownIdKey] instanceof Array) {
const foreignViewModels = this.viewModelStoreService.getMany(
relation.foreignModel,
model[relation.ownIdKey]
);
viewModel['_' + relation.ownKey] = foreignViewModels;
} else if (relation.type === 'O2M') {
const foreignViewModel = this.viewModelStoreService.get(relation.foreignModel, model[relation.ownIdKey]);
viewModel['_' + relation.ownKey] = foreignViewModel;
} else if (relation.type === 'nested') {
const foreignViewModels: BaseViewModel[] = model[relation.ownKey].map(foreignModel =>
this.createViewModel(foreignModel, relation.foreignModel, relation.relationDefinition || [])
);
foreignViewModels.sort((a: BaseViewModel, b: BaseViewModel) => {
const order = relation.order;
if (!relation.order || a[order] === b[order]) {
return a.id - b.id;
} else {
return a[order] - b[order];
}
});
viewModel['_' + relation.ownKey] = foreignViewModels;
} else if (relation.type === 'custom') {
relation.setRelations(model, viewModel);
}
}
/**
* Updates all models in this repository with all changed models.
*
* @param changedModels A mapping of collections to ids of all changed models.
* @returns if at least one model was affected.
*/
public updateDependencies(changedModels: CollectionIds): boolean {
public updateDependenciesForChangedModels(changedModels: CollectionIds): boolean {
if (!this.relationDefinitions.length) {
return;
return false;
}
// Get all viewModels from this repo once.
const viewModels = this.getViewModelList();
let somethingUpdated = false;
const ownViewModels = this.getViewModelList();
const updatedIds = [];
Object.keys(changedModels).forEach(collection => {
const dependencyChanged: boolean = Object.keys(this.relationsByCollection).includes(collection);
if (!dependencyChanged) {
@ -357,87 +254,81 @@ export abstract class BaseRepository<V extends BaseViewModel & T, M extends Base
}
// Ok, we are affected by this collection. Update all viewModels from this repo.
viewModels.forEach(ownViewModel => {
const relations = this.relationsByCollection[collection];
if (!relations || !relations.length) {
return;
}
ownViewModels.forEach(ownViewModel => {
relations.forEach(relation => {
changedModels[collection].forEach(id => {
if (this.updateSingleDependency(ownViewModel, relation, collection, id)) {
somethingUpdated = true;
}
});
});
});
});
if (somethingUpdated) {
viewModels.forEach(ownViewModel => {
this.updateViewModelObservable(ownViewModel.id);
});
}
return somethingUpdated;
}
/**
* Updates an own view model with an implicit given model by the collection and changedId.
*
* @return true, if something was updated.
*/
protected updateSingleDependency(
ownViewModel: BaseViewModel,
relation: RelationDefinition,
collection: string,
changedId: number
): boolean {
if (relation.type === 'M2M') {
if (
ownViewModel[relation.ownIdKey] &&
ownViewModel[relation.ownIdKey] instanceof Array &&
ownViewModel[relation.ownIdKey].includes(changedId)
this.relationManager.updateSingleDependencyForChangedModel(
ownViewModel,
relation,
collection,
id
)
) {
const foreignViewModel = <any>this.viewModelStoreService.get(collection, changedId);
let ownModelArray = <any>ownViewModel['_' + relation.ownKey];
if (!ownModelArray) {
ownViewModel['_' + relation.ownKey] = [];
ownModelArray = <any>ownViewModel['_' + relation.ownKey];
}
const index = ownModelArray.findIndex(user => user.id === changedId);
if (index < 0) {
ownModelArray.push(foreignViewModel);
} else {
ownModelArray[index] = foreignViewModel;
}
// TODO: set reverse
return true;
}
} else if (relation.type === 'O2M') {
if (ownViewModel[relation.ownIdKey] === <any>changedId) {
ownViewModel['_' + relation.ownKey] = <any>this.viewModelStoreService.get(collection, changedId);
// TODO: set reverse
return true;
}
} else if (relation.type === 'nested') {
let updated = false;
(relation.relationDefinition || []).forEach(nestedRelation => {
const nestedViewModels = ownViewModel[relation.ownKey] as BaseViewModel[];
nestedViewModels.forEach(nestedViewModel => {
if (this.updateSingleDependency(nestedViewModel, nestedRelation, collection, changedId)) {
updated = true;
updatedIds.push(ownViewModel.id);
}
});
});
return updated;
} else if (relation.type === 'custom') {
const foreignViewModel = <any>this.viewModelStoreService.get(collection, changedId);
return relation.updateDependency(ownViewModel, foreignViewModel);
});
// Order all relations, if neeed.
if (updatedIds.length) {
relations.forEach(relation => {
if (
(isNormalRelationDefinition(relation) || isReverseRelationDefinition(relation)) &&
(relation.type === 'M2M' || relation.type === 'O2M') &&
relation.order
) {
ownViewModels.forEach(ownViewModel => {
if (ownViewModel['_' + relation.ownKey]) {
this.relationManager.sortByRelation(relation, ownViewModel);
}
});
}
});
}
// Inform about changes. (List updates is done in `commitUpdate` via `DataStoreUpdateManagerService`)
updatedIds.forEach(id => {
this.updateViewModelObservable(id);
});
});
return !!updatedIds.length;
}
public updateDependenciesForDeletedModels(deletedModels: CollectionIds): boolean {
if (!Object.keys(this.reverseRelationsByCollection).length) {
return false;
}
// Get all viewModels from this repo once.
const ownViewModels = this.getViewModelList();
let somethingChanged = false;
Object.keys(deletedModels).forEach(collection => {
const dependencyChanged: boolean = Object.keys(this.reverseRelationsByCollection).includes(collection);
if (!dependencyChanged) {
return;
}
// Ok, we are affected by this collection. Update all viewModels from this repo.
const relations = this.reverseRelationsByCollection[collection];
ownViewModels.forEach(ownViewModel => {
relations.forEach(relation => {
deletedModels[collection].forEach(id => {
if (this.relationManager.updateSingleDependencyForDeletedModel(ownViewModel, relation, id)) {
// Inform about changes. (List updates is done in `commitUpdate` via `DataStoreUpdateManagerService`)
this.updateViewModelObservable(id);
somethingChanged = true;
}
});
});
});
// Ordering all relations is not needed, because just deleting things out of arrays
// will not unorder them.
});
return somethingChanged;
}
/**
* Saves the (full) update to an existing model. So called "update"-function
* Provides a default procedure, but can be overwritten if required

View File

@ -8,6 +8,7 @@ import { ConstantsService } from 'app/core/core-services/constants.service';
import { DataSendService } from 'app/core/core-services/data-send.service';
import { DataStoreService } from 'app/core/core-services/data-store.service';
import { HttpService } from 'app/core/core-services/http.service';
import { RelationManagerService } from 'app/core/core-services/relation-manager.service';
import { ViewModelStoreService } from 'app/core/core-services/view-model-store.service';
import { BaseRepository } from 'app/core/repositories/base-repository';
import { Identifiable } from 'app/shared/models/base/identifiable';
@ -112,10 +113,11 @@ export class ConfigRepositoryService extends BaseRepository<ViewConfig, Config,
mapperService: CollectionStringMapperService,
viewModelStoreService: ViewModelStoreService,
translate: TranslateService,
relationManager: RelationManagerService,
private constantsService: ConstantsService,
private http: HttpService
) {
super(DS, dataSend, mapperService, viewModelStoreService, translate, Config);
super(DS, dataSend, mapperService, viewModelStoreService, translate, relationManager, Config);
this.constantsService.get('ConfigVariables').subscribe(constant => {
this.createConfigStructure(constant);

View File

@ -7,19 +7,20 @@ import { first, map } from 'rxjs/operators';
import { DataSendService } from 'app/core/core-services/data-send.service';
import { HttpService } from 'app/core/core-services/http.service';
import { RelationManagerService } from 'app/core/core-services/relation-manager.service';
import { ViewModelStoreService } from 'app/core/core-services/view-model-store.service';
import { RelationDefinition } from 'app/core/definitions/relations';
import { Identifiable } from 'app/shared/models/base/identifiable';
import { Mediafile } from 'app/shared/models/mediafiles/mediafile';
import { MediafileTitleInformation, ViewMediafile } from 'app/site/mediafiles/models/view-mediafile';
import { ViewGroup } from 'app/site/users/models/view-group';
import { BaseIsListOfSpeakersContentObjectRepository } from '../base-is-list-of-speakers-content-object-repository';
import { RelationDefinition } from '../base-repository';
import { CollectionStringMapperService } from '../../core-services/collection-string-mapper.service';
import { DataStoreService } from '../../core-services/data-store.service';
const MediafileRelations: RelationDefinition[] = [
{
type: 'O2M',
type: 'M2O',
ownIdKey: 'parent_id',
ownKey: 'parent',
foreignModel: ViewMediafile
@ -64,9 +65,19 @@ export class MediafileRepositoryService extends BaseIsListOfSpeakersContentObjec
viewModelStoreService: ViewModelStoreService,
translate: TranslateService,
dataSend: DataSendService,
relationManager: RelationManagerService,
private httpService: HttpService
) {
super(DS, dataSend, mapperService, viewModelStoreService, translate, Mediafile, MediafileRelations);
super(
DS,
dataSend,
mapperService,
viewModelStoreService,
translate,
relationManager,
Mediafile,
MediafileRelations
);
this.directoryBehaviorSubject = new BehaviorSubject([]);
this.getViewModelListObservable().subscribe(mediafiles => {
if (mediafiles) {

View File

@ -2,12 +2,14 @@ import { Injectable } from '@angular/core';
import { TranslateService } from '@ngx-translate/core';
import { RelationManagerService } from 'app/core/core-services/relation-manager.service';
import { ViewModelStoreService } from 'app/core/core-services/view-model-store.service';
import { RelationDefinition } from 'app/core/definitions/relations';
import { TreeIdNode } from 'app/core/ui-services/tree.service';
import { Category } from 'app/shared/models/motions/category';
import { Motion } from 'app/shared/models/motions/motion';
import { CategoryTitleInformation, ViewCategory } from 'app/site/motions/models/view-category';
import { BaseRepository, RelationDefinition } from '../base-repository';
import { ViewMotion } from 'app/site/motions/models/view-motion';
import { BaseRepository } from '../base-repository';
import { CollectionStringMapperService } from '../../core-services/collection-string-mapper.service';
import { DataSendService } from '../../core-services/data-send.service';
import { DataStoreService } from '../../core-services/data-store.service';
@ -15,10 +17,24 @@ import { HttpService } from '../../core-services/http.service';
const CategoryRelations: RelationDefinition[] = [
{
type: 'O2M',
type: 'M2O',
ownIdKey: 'parent_id',
ownKey: 'parent',
foreignModel: ViewCategory
},
{
type: 'O2M',
foreignIdKey: 'category_id',
ownKey: 'motions',
foreignModel: ViewMotion,
order: 'category_weight'
},
{
type: 'O2M',
foreignIdKey: 'parent_id',
ownKey: 'children',
foreignModel: ViewCategory,
order: 'weight'
}
];
@ -54,9 +70,19 @@ export class CategoryRepositoryService extends BaseRepository<ViewCategory, Cate
mapperService: CollectionStringMapperService,
viewModelStoreService: ViewModelStoreService,
translate: TranslateService,
relationManager: RelationManagerService,
private httpService: HttpService
) {
super(DS, dataSend, mapperService, viewModelStoreService, translate, Category, CategoryRelations);
super(
DS,
dataSend,
mapperService,
viewModelStoreService,
translate,
relationManager,
Category,
CategoryRelations
);
this.setSortFunction((a, b) => a.weight - b.weight);
}
@ -98,14 +124,4 @@ export class CategoryRepositoryService extends BaseRepository<ViewCategory, Cate
public async sortCategories(data: TreeIdNode[]): Promise<void> {
await this.httpService.post('/rest/motions/category/sort_categories/', data);
}
/**
* Filter the DataStore by Motions and returns the amount of motions in the given category
*
* @param category the category
* @returns the number of motions inside the category
*/
public getMotionAmountByCategory(category: ViewCategory): number {
return this.DS.filter(Motion, motion => motion.category_id === category.id).length;
}
}

View File

@ -7,6 +7,7 @@ import { map } from 'rxjs/operators';
import { CollectionStringMapperService } from 'app/core/core-services/collection-string-mapper.service';
import { DataSendService } from 'app/core/core-services/data-send.service';
import { DataStoreService } from 'app/core/core-services/data-store.service';
import { RelationManagerService } from 'app/core/core-services/relation-manager.service';
import { ViewModelStoreService } from 'app/core/core-services/view-model-store.service';
import { Identifiable } from 'app/shared/models/base/identifiable';
import { MotionChangeRecommendation } from 'app/shared/models/motions/motion-change-reco';
@ -56,9 +57,18 @@ export class ChangeRecommendationRepositoryService extends BaseRepository<
mapperService: CollectionStringMapperService,
viewModelStoreService: ViewModelStoreService,
translate: TranslateService,
relationManager: RelationManagerService,
private diffService: DiffService
) {
super(DS, dataSend, mapperService, viewModelStoreService, translate, MotionChangeRecommendation);
super(
DS,
dataSend,
mapperService,
viewModelStoreService,
translate,
relationManager,
MotionChangeRecommendation
);
}
public getTitle = (titleInformation: MotionChangeRecommendationTitleInformation) => {

View File

@ -1,21 +1,29 @@
import { Injectable } from '@angular/core';
import { TranslateService } from '@ngx-translate/core';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import { CollectionStringMapperService } from 'app/core/core-services/collection-string-mapper.service';
import { DataSendService } from 'app/core/core-services/data-send.service';
import { DataStoreService } from 'app/core/core-services/data-store.service';
import { HttpService } from 'app/core/core-services/http.service';
import { RelationManagerService } from 'app/core/core-services/relation-manager.service';
import { ViewModelStoreService } from 'app/core/core-services/view-model-store.service';
import { Motion } from 'app/shared/models/motions/motion';
import { RelationDefinition } from 'app/core/definitions/relations';
import { MotionBlock } from 'app/shared/models/motions/motion-block';
import { ViewMotion } from 'app/site/motions/models/view-motion';
import { MotionBlockTitleInformation, ViewMotionBlock } from 'app/site/motions/models/view-motion-block';
import { BaseIsAgendaItemAndListOfSpeakersContentObjectRepository } from '../base-is-agenda-item-and-list-of-speakers-content-object-repository';
import { MotionRepositoryService } from './motion-repository.service';
const MotionBlockRelations: RelationDefinition[] = [
{
type: 'O2M',
foreignIdKey: 'motion_block_id',
ownKey: 'motions',
foreignModel: ViewMotion
}
];
/**
* Repository service for motion blocks
*/
@ -42,10 +50,20 @@ export class MotionBlockRepositoryService extends BaseIsAgendaItemAndListOfSpeak
mapperService: CollectionStringMapperService,
viewModelStoreService: ViewModelStoreService,
translate: TranslateService,
relationManager: RelationManagerService,
private motionRepo: MotionRepositoryService,
private httpService: HttpService
) {
super(DS, dataSend, mapperService, viewModelStoreService, translate, MotionBlock);
super(
DS,
dataSend,
mapperService,
viewModelStoreService,
translate,
relationManager,
MotionBlock,
MotionBlockRelations
);
this.initSorting();
}
@ -68,29 +86,6 @@ export class MotionBlockRepositoryService extends BaseIsAgendaItemAndListOfSpeak
this.motionRepo.update(updateMotion, viewMotion);
}
/**
* Filter the DataStore by Motions and returns the
*
* @param block the motion block
* @returns the number of motions inside a motion block
*/
public getMotionAmountByBlock(block: MotionBlock): number {
return this.DS.filter(Motion, motion => motion.motion_block_id === block.id).length;
}
/**
* Observe the motion repository and return the motions belonging to the given
* block as observable
*
* @param block a motion block
* @returns an observable to view motions
*/
public getViewMotionsByBlock(block: MotionBlock): Observable<ViewMotion[]> {
return this.motionRepo
.getViewModelListObservable()
.pipe(map(viewMotions => viewMotions.filter(viewMotion => viewMotion.motion_block_id === block.id)));
}
/**
* Retrieves motion blocks by name
*

View File

@ -3,7 +3,9 @@ import { Injectable } from '@angular/core';
import { TranslateService } from '@ngx-translate/core';
import { HttpService } from 'app/core/core-services/http.service';
import { RelationManagerService } from 'app/core/core-services/relation-manager.service';
import { ViewModelStoreService } from 'app/core/core-services/view-model-store.service';
import { RelationDefinition } from 'app/core/definitions/relations';
import { MotionCommentSection } from 'app/shared/models/motions/motion-comment-section';
import { ViewMotion } from 'app/site/motions/models/view-motion';
import {
@ -11,7 +13,7 @@ import {
ViewMotionCommentSection
} from 'app/site/motions/models/view-motion-comment-section';
import { ViewGroup } from 'app/site/users/models/view-group';
import { BaseRepository, RelationDefinition } from '../base-repository';
import { BaseRepository } from '../base-repository';
import { CollectionStringMapperService } from '../../core-services/collection-string-mapper.service';
import { DataSendService } from '../../core-services/data-send.service';
import { DataStoreService } from '../../core-services/data-store.service';
@ -65,6 +67,7 @@ export class MotionCommentSectionRepositoryService extends BaseRepository<
mapperService: CollectionStringMapperService,
viewModelStoreService: ViewModelStoreService,
translate: TranslateService,
relationManager: RelationManagerService,
private http: HttpService
) {
super(
@ -73,6 +76,7 @@ export class MotionCommentSectionRepositoryService extends BaseRepository<
mapperService,
viewModelStoreService,
translate,
relationManager,
MotionCommentSection,
MotionCommentSectionRelations
);

View File

@ -8,7 +8,9 @@ import { map } from 'rxjs/operators';
import { DataStoreService } from 'app/core/core-services/data-store.service';
import { HttpService } from 'app/core/core-services/http.service';
import { OperatorService } from 'app/core/core-services/operator.service';
import { RelationManagerService } from 'app/core/core-services/relation-manager.service';
import { ViewModelStoreService } from 'app/core/core-services/view-model-store.service';
import { RelationDefinition } from 'app/core/definitions/relations';
import { ConfigService } from 'app/core/ui-services/config.service';
import { DiffLinesInParagraph, DiffService } from 'app/core/ui-services/diff.service';
import { TreeIdNode } from 'app/core/ui-services/tree.service';
@ -30,7 +32,6 @@ import { ViewTag } from 'app/site/tags/models/view-tag';
import { ViewPersonalNote } from 'app/site/users/models/view-personal-note';
import { ViewUser } from 'app/site/users/models/view-user';
import { BaseIsAgendaItemAndListOfSpeakersContentObjectRepository } from '../base-is-agenda-item-and-list-of-speakers-content-object-repository';
import { RelationDefinition } from '../base-repository';
import { CollectionStringMapperService } from '../../core-services/collection-string-mapper.service';
import { DataSendService } from '../../core-services/data-send.service';
import { LinenumberingService, LineNumberRange } from '../../ui-services/linenumbering.service';
@ -69,31 +70,31 @@ export interface ParagraphToChoose {
const MotionRelations: RelationDefinition[] = [
{
type: 'O2M',
type: 'M2O',
ownIdKey: 'state_id',
ownKey: 'state',
foreignModel: ViewState
},
{
type: 'O2M',
type: 'M2O',
ownIdKey: 'recommendation_id',
ownKey: 'recommendation',
foreignModel: ViewState
},
{
type: 'O2M',
type: 'M2O',
ownIdKey: 'workflow_id',
ownKey: 'workflow',
foreignModel: ViewWorkflow
},
{
type: 'O2M',
type: 'M2O',
ownIdKey: 'category_id',
ownKey: 'category',
foreignModel: ViewCategory
},
{
type: 'O2M',
type: 'M2O',
ownIdKey: 'motion_block_id',
ownKey: 'motion_block',
foreignModel: ViewMotionBlock
@ -105,7 +106,7 @@ const MotionRelations: RelationDefinition[] = [
order: 'weight',
relationDefinition: [
{
type: 'O2M',
type: 'M2O',
ownIdKey: 'user_id',
ownKey: 'user',
foreignModel: ViewUser
@ -131,7 +132,7 @@ const MotionRelations: RelationDefinition[] = [
foreignModel: ViewTag
},
{
type: 'O2M',
type: 'M2O',
ownIdKey: 'parent_id',
ownKey: 'parent',
foreignModel: ViewMotion
@ -190,6 +191,7 @@ export class MotionRepositoryService extends BaseIsAgendaItemAndListOfSpeakersCo
mapperService: CollectionStringMapperService,
viewModelStoreService: ViewModelStoreService,
translate: TranslateService,
relationManager: RelationManagerService,
config: ConfigService,
private httpService: HttpService,
private readonly sanitizer: DomSanitizer,
@ -197,7 +199,7 @@ export class MotionRepositoryService extends BaseIsAgendaItemAndListOfSpeakersCo
private readonly diff: DiffService,
private operator: OperatorService
) {
super(DS, dataSend, mapperService, viewModelStoreService, translate, Motion, MotionRelations);
super(DS, dataSend, mapperService, viewModelStoreService, translate, relationManager, Motion, MotionRelations);
config.get<SortProperty>('motions_motions_sorting').subscribe(conf => {
this.sortProperty = conf;
this.setConfigSortFn();

View File

@ -2,18 +2,20 @@ import { Injectable } from '@angular/core';
import { TranslateService } from '@ngx-translate/core';
import { RelationManagerService } from 'app/core/core-services/relation-manager.service';
import { ViewModelStoreService } from 'app/core/core-services/view-model-store.service';
import { RelationDefinition } from 'app/core/definitions/relations';
import { State } from 'app/shared/models/motions/state';
import { StateTitleInformation, ViewState } from 'app/site/motions/models/view-state';
import { ViewWorkflow, WorkflowTitleInformation } from 'app/site/motions/models/view-workflow';
import { BaseRepository, RelationDefinition } from '../base-repository';
import { BaseRepository } from '../base-repository';
import { CollectionStringMapperService } from '../../core-services/collection-string-mapper.service';
import { DataSendService } from '../../core-services/data-send.service';
import { DataStoreService } from '../../core-services/data-store.service';
const StateRelations: RelationDefinition[] = [
{
type: 'O2M',
type: 'M2O',
ownIdKey: 'workflow_id',
ownKey: 'workflow',
foreignModel: ViewWorkflow
@ -54,9 +56,10 @@ export class StateRepositoryService extends BaseRepository<ViewState, State, Sta
dataSend: DataSendService,
mapperService: CollectionStringMapperService,
viewModelStoreService: ViewModelStoreService,
translate: TranslateService
translate: TranslateService,
relationManager: RelationManagerService
) {
super(DS, dataSend, mapperService, viewModelStoreService, translate, State, StateRelations);
super(DS, dataSend, mapperService, viewModelStoreService, translate, relationManager, State, StateRelations);
}
public getTitle = (titleInformation: WorkflowTitleInformation) => {

View File

@ -2,6 +2,7 @@ import { Injectable } from '@angular/core';
import { TranslateService } from '@ngx-translate/core';
import { RelationManagerService } from 'app/core/core-services/relation-manager.service';
import { ViewModelStoreService } from 'app/core/core-services/view-model-store.service';
import { StatuteParagraph } from 'app/shared/models/motions/statute-paragraph';
import { StatuteParagraphTitleInformation, ViewStatuteParagraph } from 'app/site/motions/models/view-statute-paragraph';
@ -39,9 +40,10 @@ export class StatuteParagraphRepositoryService extends BaseRepository<
dataSend: DataSendService,
mapperService: CollectionStringMapperService,
viewModelStoreService: ViewModelStoreService,
translate: TranslateService
translate: TranslateService,
relationManager: RelationManagerService
) {
super(DS, dataSend, mapperService, viewModelStoreService, translate, StatuteParagraph);
super(DS, dataSend, mapperService, viewModelStoreService, translate, relationManager, StatuteParagraph);
}
public getTitle = (titleInformation: StatuteParagraphTitleInformation) => {

View File

@ -2,12 +2,14 @@ import { Injectable } from '@angular/core';
import { TranslateService } from '@ngx-translate/core';
import { RelationManagerService } from 'app/core/core-services/relation-manager.service';
import { ViewModelStoreService } from 'app/core/core-services/view-model-store.service';
import { RelationDefinition } from 'app/core/definitions/relations';
import { Workflow } from 'app/shared/models/motions/workflow';
import { ViewMotion } from 'app/site/motions/models/view-motion';
import { ViewState } from 'app/site/motions/models/view-state';
import { ViewWorkflow, WorkflowTitleInformation } from 'app/site/motions/models/view-workflow';
import { BaseRepository, RelationDefinition } from '../base-repository';
import { BaseRepository } from '../base-repository';
import { CollectionStringMapperService } from '../../core-services/collection-string-mapper.service';
import { DataSendService } from '../../core-services/data-send.service';
import { DataStoreService } from '../../core-services/data-store.service';
@ -20,7 +22,7 @@ const WorkflowRelations: RelationDefinition[] = [
foreignModel: ViewState
},
{
type: 'O2M',
type: 'M2O',
ownIdKey: 'first_state_id',
ownKey: 'first_state',
foreignModel: ViewState
@ -55,9 +57,19 @@ export class WorkflowRepositoryService extends BaseRepository<ViewWorkflow, Work
dataSend: DataSendService,
mapperService: CollectionStringMapperService,
viewModelStoreService: ViewModelStoreService,
translate: TranslateService
translate: TranslateService,
relationManager: RelationManagerService
) {
super(DS, dataSend, mapperService, viewModelStoreService, translate, Workflow, WorkflowRelations);
super(
DS,
dataSend,
mapperService,
viewModelStoreService,
translate,
relationManager,
Workflow,
WorkflowRelations
);
}
public getTitle = (titleInformation: WorkflowTitleInformation) => {

View File

@ -2,6 +2,7 @@ import { Injectable } from '@angular/core';
import { TranslateService } from '@ngx-translate/core';
import { RelationManagerService } from 'app/core/core-services/relation-manager.service';
import { ServertimeService } from 'app/core/core-services/servertime.service';
import { ViewModelStoreService } from 'app/core/core-services/view-model-store.service';
import { Countdown } from 'app/shared/models/core/countdown';
@ -21,9 +22,10 @@ export class CountdownRepositoryService extends BaseRepository<ViewCountdown, Co
mapperService: CollectionStringMapperService,
viewModelStoreService: ViewModelStoreService,
translate: TranslateService,
relationManager: RelationManagerService,
private servertimeService: ServertimeService
) {
super(DS, dataSend, mapperService, viewModelStoreService, translate, Countdown);
super(DS, dataSend, mapperService, viewModelStoreService, translate, relationManager, Countdown);
}
public getTitle = (titleInformation: CountdownTitleInformation) => {

View File

@ -2,6 +2,7 @@ import { Injectable } from '@angular/core';
import { TranslateService } from '@ngx-translate/core';
import { RelationManagerService } from 'app/core/core-services/relation-manager.service';
import { ViewModelStoreService } from 'app/core/core-services/view-model-store.service';
import { Identifiable } from 'app/shared/models/base/identifiable';
import { ProjectionDefault } from 'app/shared/models/core/projection-default';
@ -39,9 +40,10 @@ export class ProjectionDefaultRepositoryService extends BaseRepository<
dataSend: DataSendService,
mapperService: CollectionStringMapperService,
viewModelStoreService: ViewModelStoreService,
translate: TranslateService
translate: TranslateService,
relationManager: RelationManagerService
) {
super(DS, dataSend, mapperService, viewModelStoreService, translate, ProjectionDefault);
super(DS, dataSend, mapperService, viewModelStoreService, translate, relationManager, ProjectionDefault);
}
public getVerboseName = (plural: boolean = false) => {

View File

@ -3,6 +3,7 @@ import { Injectable } from '@angular/core';
import { TranslateService } from '@ngx-translate/core';
import { DataSendService } from 'app/core/core-services/data-send.service';
import { RelationManagerService } from 'app/core/core-services/relation-manager.service';
import { ViewModelStoreService } from 'app/core/core-services/view-model-store.service';
import { ProjectorMessage } from 'app/shared/models/core/projector-message';
import {
@ -26,9 +27,10 @@ export class ProjectorMessageRepositoryService extends BaseRepository<
dataSend: DataSendService,
mapperService: CollectionStringMapperService,
viewModelStoreService: ViewModelStoreService,
translate: TranslateService
translate: TranslateService,
relationManager: RelationManagerService
) {
super(DS, dataSend, mapperService, viewModelStoreService, translate, ProjectorMessage);
super(DS, dataSend, mapperService, viewModelStoreService, translate, relationManager, ProjectorMessage);
}
public getTitle = (titleInformation: ProjectorMessageTitleInformation) => {

View File

@ -3,11 +3,13 @@ import { Injectable } from '@angular/core';
import { TranslateService } from '@ngx-translate/core';
import { HttpService } from 'app/core/core-services/http.service';
import { RelationManagerService } from 'app/core/core-services/relation-manager.service';
import { ViewModelStoreService } from 'app/core/core-services/view-model-store.service';
import { RelationDefinition } from 'app/core/definitions/relations';
import { Identifiable } from 'app/shared/models/base/identifiable';
import { Projector } from 'app/shared/models/core/projector';
import { ProjectorTitleInformation, ViewProjector } from 'app/site/projector/models/view-projector';
import { BaseRepository, RelationDefinition } from '../base-repository';
import { BaseRepository } from '../base-repository';
import { CollectionStringMapperService } from '../../core-services/collection-string-mapper.service';
import { DataSendService } from '../../core-services/data-send.service';
import { DataStoreService } from '../../core-services/data-store.service';
@ -23,7 +25,7 @@ export enum ScrollScaleDirection {
const ProjectorRelations: RelationDefinition[] = [
{
type: 'O2M',
type: 'M2O',
ownIdKey: 'reference_projector_id',
ownKey: 'referenceProjector',
foreignModel: ViewProjector
@ -51,9 +53,19 @@ export class ProjectorRepositoryService extends BaseRepository<ViewProjector, Pr
mapperService: CollectionStringMapperService,
viewModelStoreService: ViewModelStoreService,
translate: TranslateService,
relationManager: RelationManagerService,
private http: HttpService
) {
super(DS, dataSend, mapperService, viewModelStoreService, translate, Projector, ProjectorRelations);
super(
DS,
dataSend,
mapperService,
viewModelStoreService,
translate,
relationManager,
Projector,
ProjectorRelations
);
}
public getTitle = (titleInformation: ProjectorTitleInformation) => {

View File

@ -2,6 +2,7 @@ import { Injectable } from '@angular/core';
import { TranslateService } from '@ngx-translate/core';
import { RelationManagerService } from 'app/core/core-services/relation-manager.service';
import { ViewModelStoreService } from 'app/core/core-services/view-model-store.service';
import { Tag } from 'app/shared/models/core/tag';
import { TagTitleInformation, ViewTag } from 'app/site/tags/models/view-tag';
@ -38,9 +39,10 @@ export class TagRepositoryService extends BaseRepository<ViewTag, Tag, TagTitleI
dataSend: DataSendService,
mapperService: CollectionStringMapperService,
viewModelStoreService: ViewModelStoreService,
translate: TranslateService
translate: TranslateService,
relationManager: RelationManagerService
) {
super(DS, dataSend, mapperService, viewModelStoreService, translate, Tag);
super(DS, dataSend, mapperService, viewModelStoreService, translate, relationManager, Tag);
this.initSorting();
}

View File

@ -5,12 +5,13 @@ import { TranslateService } from '@ngx-translate/core';
import { CollectionStringMapperService } from 'app/core/core-services/collection-string-mapper.service';
import { DataSendService } from 'app/core/core-services/data-send.service';
import { DataStoreService } from 'app/core/core-services/data-store.service';
import { RelationManagerService } from 'app/core/core-services/relation-manager.service';
import { ViewModelStoreService } from 'app/core/core-services/view-model-store.service';
import { RelationDefinition } from 'app/core/definitions/relations';
import { Topic } from 'app/shared/models/topics/topic';
import { ViewMediafile } from 'app/site/mediafiles/models/view-mediafile';
import { TopicTitleInformation, ViewTopic } from 'app/site/topics/models/view-topic';
import { BaseIsAgendaItemAndListOfSpeakersContentObjectRepository } from '../base-is-agenda-item-and-list-of-speakers-content-object-repository';
import { RelationDefinition } from '../base-repository';
const TopicRelations: RelationDefinition[] = [
{
@ -44,9 +45,10 @@ export class TopicRepositoryService extends BaseIsAgendaItemAndListOfSpeakersCon
dataSend: DataSendService,
mapperService: CollectionStringMapperService,
viewModelStoreService: ViewModelStoreService,
translate: TranslateService
translate: TranslateService,
relationManager: RelationManagerService
) {
super(DS, dataSend, mapperService, viewModelStoreService, translate, Topic, TopicRelations);
super(DS, dataSend, mapperService, viewModelStoreService, translate, relationManager, Topic, TopicRelations);
}
public getTitle = (titleInformation: TopicTitleInformation) => {

View File

@ -3,6 +3,7 @@ import { Injectable } from '@angular/core';
import { TranslateService } from '@ngx-translate/core';
import { HttpService } from 'app/core/core-services/http.service';
import { RelationManagerService } from 'app/core/core-services/relation-manager.service';
import { ViewModelStoreService } from 'app/core/core-services/view-model-store.service';
import { Group } from 'app/shared/models/users/group';
import { GroupTitleInformation, ViewGroup } from 'app/site/users/models/view-group';
@ -55,10 +56,11 @@ export class GroupRepositoryService extends BaseRepository<ViewGroup, Group, Gro
mapperService: CollectionStringMapperService,
viewModelStoreService: ViewModelStoreService,
translate: TranslateService,
relationManager: RelationManagerService,
private constantsService: ConstantsService,
private http: HttpService
) {
super(DS, dataSend, mapperService, viewModelStoreService, translate, Group);
super(DS, dataSend, mapperService, viewModelStoreService, translate, relationManager, Group);
this.sortPermsPerApp();
}

View File

@ -3,6 +3,7 @@ import { Injectable } from '@angular/core';
import { TranslateService } from '@ngx-translate/core';
import { DataSendService } from 'app/core/core-services/data-send.service';
import { RelationManagerService } from 'app/core/core-services/relation-manager.service';
import { ViewModelStoreService } from 'app/core/core-services/view-model-store.service';
import { Identifiable } from 'app/shared/models/base/identifiable';
import { PersonalNote } from 'app/shared/models/users/personal-note';
@ -30,9 +31,10 @@ export class PersonalNoteRepositoryService extends BaseRepository<
dataSend: DataSendService,
mapperService: CollectionStringMapperService,
viewModelStoreService: ViewModelStoreService,
translate: TranslateService
translate: TranslateService,
relationManager: RelationManagerService
) {
super(DS, dataSend, mapperService, viewModelStoreService, translate, PersonalNote);
super(DS, dataSend, mapperService, viewModelStoreService, translate, relationManager, PersonalNote);
}
public getTitle = (titleInformation: PersonalNoteTitleInformation) => {

View File

@ -3,13 +3,15 @@ import { Injectable } from '@angular/core';
import { TranslateService } from '@ngx-translate/core';
import { HttpService } from 'app/core/core-services/http.service';
import { RelationManagerService } from 'app/core/core-services/relation-manager.service';
import { ViewModelStoreService } from 'app/core/core-services/view-model-store.service';
import { RelationDefinition } from 'app/core/definitions/relations';
import { NewEntry } from 'app/core/ui-services/base-import.service';
import { ConfigService } from 'app/core/ui-services/config.service';
import { User } from 'app/shared/models/users/user';
import { ViewGroup } from 'app/site/users/models/view-group';
import { UserTitleInformation, ViewUser } from 'app/site/users/models/view-user';
import { BaseRepository, RelationDefinition } from '../base-repository';
import { BaseRepository } from '../base-repository';
import { CollectionStringMapperService } from '../../core-services/collection-string-mapper.service';
import { DataSendService } from '../../core-services/data-send.service';
import { DataStoreService } from '../../core-services/data-store.service';
@ -61,11 +63,12 @@ export class UserRepositoryService extends BaseRepository<ViewUser, User, UserTi
dataSend: DataSendService,
mapperService: CollectionStringMapperService,
viewModelStoreService: ViewModelStoreService,
relationManager: RelationManagerService,
protected translate: TranslateService,
private httpService: HttpService,
private configService: ConfigService
) {
super(DS, dataSend, mapperService, viewModelStoreService, translate, User, UserRelations);
super(DS, dataSend, mapperService, viewModelStoreService, translate, relationManager, User, UserRelations);
this.sortProperty = this.configService.instant('users_sort_by');
this.configService.get<SortProperty>('users_sort_by').subscribe(conf => {
this.sortProperty = conf;

View File

@ -6,4 +6,8 @@ import { ContentObject } from './content-object';
*/
export abstract class BaseModelWithContentObject<T = object> extends BaseModel<T> {
public abstract content_object: ContentObject;
public get contentObjectData(): ContentObject {
return this.content_object;
}
}

View File

@ -1,4 +1,4 @@
import { AppConfig } from '../../core/app-config';
import { AppConfig } from '../../core/definitions/app-config';
import { ItemRepositoryService } from 'app/core/repositories/agenda/item-repository.service';
import { ListOfSpeakersRepositoryService } from 'app/core/repositories/agenda/list-of-speakers-repository.service';
import { ListOfSpeakers } from 'app/shared/models/agenda/list-of-speakers';

View File

@ -12,6 +12,7 @@ export interface ItemTitleInformation {
export class ViewItem extends BaseViewModelWithContentObject<Item, BaseViewModelWithAgendaItem>
implements ItemTitleInformation {
public static COLLECTIONSTRING = Item.COLLECTIONSTRING;
protected _collectionString = Item.COLLECTIONSTRING;
public get item(): Item {
return this._model;
@ -83,8 +84,4 @@ export class ViewItem extends BaseViewModelWithContentObject<Item, BaseViewModel
public get parent_id(): number {
return this.item.parent_id;
}
public constructor(item: Item) {
super(Item.COLLECTIONSTRING, item);
}
}

View File

@ -1,4 +1,3 @@
import { Item } from 'app/shared/models/agenda/item';
import { ListOfSpeakers } from 'app/shared/models/agenda/list-of-speakers';
import { ContentObject } from 'app/shared/models/base/content-object';
import { BaseViewModelWithContentObject } from 'app/site/base/base-view-model-with-content-object';
@ -18,6 +17,7 @@ export interface ListOfSpeakersTitleInformation {
export class ViewListOfSpeakers extends BaseViewModelWithContentObject<ListOfSpeakers, BaseViewModelWithListOfSpeakers>
implements ListOfSpeakersTitleInformation, Projectable {
public static COLLECTIONSTRING = ListOfSpeakers.COLLECTIONSTRING;
protected _collectionString = ListOfSpeakers.COLLECTIONSTRING;
private _speakers?: ViewSpeaker[];
@ -48,10 +48,6 @@ export class ViewListOfSpeakers extends BaseViewModelWithContentObject<ListOfSpe
return `/agenda/speakers/${this.id}`;
}
public constructor(listOfSpeakers: ListOfSpeakers) {
super(Item.COLLECTIONSTRING, listOfSpeakers);
}
public getProjectorTitle(): string {
return this.getTitle();
}

View File

@ -16,6 +16,8 @@ export enum SpeakerState {
*/
export class ViewSpeaker extends BaseViewModel<Speaker> {
public static COLLECTIONSTRING = Speaker.COLLECTIONSTRING;
protected _collectionString = Speaker.COLLECTIONSTRING;
private _user?: ViewUser;
public get speaker(): Speaker {
@ -80,10 +82,6 @@ export class ViewSpeaker extends BaseViewModel<Speaker> {
return this.user ? this.user.gender : '';
}
public constructor(speaker: Speaker) {
super(Speaker.COLLECTIONSTRING, speaker);
}
public getTitle = () => {
return this.name;
};

View File

@ -1,4 +1,4 @@
import { AppConfig } from '../../core/app-config';
import { AppConfig } from '../../core/definitions/app-config';
import { AssignmentRepositoryService } from 'app/core/repositories/assignments/assignment-repository.service';
import { Assignment } from '../../shared/models/assignments/assignment';
import { ViewAssignment } from './models/view-assignment';

View File

@ -10,6 +10,8 @@ const votesOrder: PollVoteValue[] = ['Votes', 'Yes', 'No', 'Abstain'];
export class ViewAssignmentPollOption extends BaseViewModel<AssignmentPollOption> {
public static COLLECTIONSTRING = AssignmentPollOption.COLLECTIONSTRING;
protected _collectionString = AssignmentPollOption.COLLECTIONSTRING;
private _user?: ViewUser; // This is the "candidate". We'll stay consistent wich user here...
public get option(): AssignmentPollOption {
@ -49,8 +51,4 @@ export class ViewAssignmentPollOption extends BaseViewModel<AssignmentPollOption
public get weight(): number {
return this.option.weight;
}
public constructor(assignmentPollOption: AssignmentPollOption) {
super(AssignmentPollOption.COLLECTIONSTRING, assignmentPollOption);
}
}

View File

@ -7,6 +7,8 @@ import { ViewAssignmentPollOption } from './view-assignment-poll-option';
export class ViewAssignmentPoll extends BaseProjectableViewModel<AssignmentPoll> {
public static COLLECTIONSTRING = AssignmentPoll.COLLECTIONSTRING;
protected _collectionString = AssignmentPoll.COLLECTIONSTRING;
private _options: ViewAssignmentPollOption[];
public get poll(): AssignmentPoll {
@ -76,10 +78,6 @@ export class ViewAssignmentPoll extends BaseProjectableViewModel<AssignmentPoll>
return this.poll.assignment_id;
}
public constructor(assignmentPoll: AssignmentPoll) {
super(AssignmentPoll.COLLECTIONSTRING, assignmentPoll);
}
public getTitle = () => {
return 'Poll';
};

View File

@ -4,6 +4,7 @@ import { ViewUser } from 'app/site/users/models/view-user';
export class ViewAssignmentRelatedUser extends BaseViewModel<AssignmentRelatedUser> {
public static COLLECTIONSTRING = AssignmentRelatedUser.COLLECTIONSTRING;
protected _collectionString = AssignmentRelatedUser.COLLECTIONSTRING;
private _user?: ViewUser;
@ -37,10 +38,6 @@ export class ViewAssignmentRelatedUser extends BaseViewModel<AssignmentRelatedUs
public getListTitle: () => string = this.getTitle;
public constructor(assignmentRelatedUser: AssignmentRelatedUser) {
super(AssignmentRelatedUser.COLLECTIONSTRING, assignmentRelatedUser);
}
public getTitle: () => string = () => {
return this.user ? this.user.getFullName() : '';
};

View File

@ -39,6 +39,7 @@ export const AssignmentPhases: { name: string; value: number; display_name: stri
export class ViewAssignment extends BaseViewModelWithAgendaItemAndListOfSpeakers<Assignment>
implements AssignmentTitleInformation {
public static COLLECTIONSTRING = Assignment.COLLECTIONSTRING;
protected _collectionString = Assignment.COLLECTIONSTRING;
private _assignment_related_users?: ViewAssignmentRelatedUser[];
private _polls?: ViewAssignmentPoll[];
@ -125,10 +126,6 @@ export class ViewAssignment extends BaseViewModelWithAgendaItemAndListOfSpeakers
return this._assignment_related_users ? this._assignment_related_users.length : 0;
}
public constructor(assignment: Assignment) {
super(Assignment.COLLECTIONSTRING, assignment);
}
public formatForSearch(): SearchRepresentation {
return [this.title];
}

View File

@ -49,10 +49,6 @@ export abstract class BaseViewModelWithAgendaItemAndListOfSpeakers<
public getListOfSpeakersTitle: () => string;
public getListOfSpeakersSlideTitle: () => string;
public constructor(collectionString: string, model: M) {
super(collectionString, model);
}
/**
* @returns the (optional) descriptive text to be exported in the CSV.
* May be overridden by inheriting classes

View File

@ -85,8 +85,8 @@ export abstract class BaseViewModelWithAgendaItem<M extends BaseModelWithAgendaI
*/
public getAgendaListTitle: () => string;
public constructor(collecitonString: string, model: M, item?: any) {
super(collecitonString, model);
public constructor(model: M, item?: any) {
super(model);
this._item = item || null; // Explicit set to null instead of undefined, if not given
}

View File

@ -22,12 +22,4 @@ export abstract class BaseViewModelWithContentObject<
public get contentObject(): C | null {
return this._contentObject;
}
/**
* @param collectionString The collection string of this model
* @param model the model this view model captures
*/
public constructor(collectionString: string, model: M) {
super(collectionString, model);
}
}

View File

@ -49,8 +49,8 @@ export abstract class BaseViewModelWithListOfSpeakers<M extends BaseModelWithLis
public getListOfSpeakersTitle: () => string;
public getListOfSpeakersSlideTitle: () => string;
public constructor(collectionString: string, model: M, listOfSpeakers?: any) {
super(collectionString, model);
public constructor(model: M, listOfSpeakers?: any) {
super(model);
this._list_of_speakers = listOfSpeakers || null; // Explicit set to null instead of undefined, if not given
}

View File

@ -14,8 +14,6 @@ export interface ViewModelConstructor<T extends BaseViewModel> {
* Base class for view models. alls view models should have titles.
*/
export abstract class BaseViewModel<M extends BaseModel = any> implements Displayable, Identifiable, Collection {
protected _model: M;
public get id(): number {
return this._model.id;
}
@ -58,10 +56,7 @@ export abstract class BaseViewModel<M extends BaseModel = any> implements Displa
* @param collectionString
* @param model
*/
public constructor(collectionString: string, model: M) {
this._collectionString = collectionString;
this._model = model;
}
public constructor(protected _model: M) {}
/**
* @returns the main underlying model of the view model

View File

@ -1,4 +1,4 @@
import { AppConfig } from '../../core/app-config';
import { AppConfig } from '../../core/definitions/app-config';
export const CommonAppConfig: AppConfig = {
name: 'common',

View File

@ -1,4 +1,4 @@
import { AppConfig } from '../../core/app-config';
import { AppConfig } from '../../core/definitions/app-config';
import { Config } from '../../shared/models/core/config';
import { ConfigRepositoryService } from '../../core/repositories/config/config-repository.service';
import { ViewConfig } from './models/view-config';

View File

@ -41,6 +41,7 @@ export interface ConfigTitleInformation {
*/
export class ViewConfig extends BaseViewModel<Config> implements ConfigTitleInformation {
public static COLLECTIONSTRING = Config.COLLECTIONSTRING;
protected _collectionString = Config.COLLECTIONSTRING;
/* This private members are set by setConstantsInfo. */
private _helpText: string;
@ -90,10 +91,6 @@ export class ViewConfig extends BaseViewModel<Config> implements ConfigTitleInfo
return this._defaultValue;
}
public constructor(config: Config) {
super(Config.COLLECTIONSTRING, config);
}
/**
* Returns the time this config field needs to debounce before sending a request to the server.
* A little debounce time for all inputs is given here and is usefull, if inputs sends multiple onChange-events,

View File

@ -1,4 +1,4 @@
import { AppConfig } from '../../core/app-config';
import { AppConfig } from '../../core/definitions/app-config';
/**
* Config object for history.

View File

@ -1,4 +1,4 @@
import { AppConfig } from '../../core/app-config';
import { AppConfig } from '../../core/definitions/app-config';
import { MediafileRepositoryService } from 'app/core/repositories/mediafiles/mediafile-repository.service';
import { Mediafile } from '../../shared/models/mediafiles/mediafile';
import { ViewMediafile } from './models/view-mediafile';

View File

@ -16,6 +16,7 @@ export interface MediafileTitleInformation {
export class ViewMediafile extends BaseViewModelWithListOfSpeakers<Mediafile>
implements MediafileTitleInformation, Searchable {
public static COLLECTIONSTRING = Mediafile.COLLECTIONSTRING;
protected _collectionString = Mediafile.COLLECTIONSTRING;
private _parent?: ViewMediafile;
private _access_groups?: ViewGroup[];
@ -101,10 +102,6 @@ export class ViewMediafile extends BaseViewModelWithListOfSpeakers<Mediafile>
return this.mediafile.create_timestamp ? this.mediafile.create_timestamp : null;
}
public constructor(mediafile: Mediafile) {
super(Mediafile.COLLECTIONSTRING, mediafile);
}
public formatForSearch(): SearchRepresentation {
return [this.title, this.path];
}

View File

@ -2,6 +2,7 @@ import { SearchRepresentation } from 'app/core/ui-services/search.service';
import { Category } from 'app/shared/models/motions/category';
import { Searchable } from 'app/site/base/searchable';
import { BaseViewModel } from '../../base/base-view-model';
import { ViewMotion } from './view-motion';
export interface CategoryTitleInformation {
prefix: string;
@ -17,8 +18,11 @@ export interface CategoryTitleInformation {
*/
export class ViewCategory extends BaseViewModel<Category> implements CategoryTitleInformation, Searchable {
public static COLLECTIONSTRING = Category.COLLECTIONSTRING;
protected _collectionString = Category.COLLECTIONSTRING;
private _parent?: ViewCategory;
private _children?: ViewCategory[];
private _motions?: ViewMotion[];
public get category(): Category {
return this._model;
@ -28,6 +32,14 @@ export class ViewCategory extends BaseViewModel<Category> implements CategoryTit
return this._parent;
}
public get children(): ViewCategory[] {
return this._children || [];
}
public get motions(): ViewMotion[] {
return this._motions || [];
}
public get name(): string {
return this.category.name;
}
@ -75,10 +87,6 @@ export class ViewCategory extends BaseViewModel<Category> implements CategoryTit
}
}
public constructor(category: Category) {
super(Category.COLLECTIONSTRING, category);
}
public formatForSearch(): SearchRepresentation {
return [this.name, this.prefix];
}

View File

@ -4,6 +4,7 @@ import { TitleInformationWithAgendaItem } from 'app/site/base/base-view-model-wi
import { BaseViewModelWithAgendaItemAndListOfSpeakers } from 'app/site/base/base-view-model-with-agenda-item-and-list-of-speakers';
import { ProjectorElementBuildDeskriptor } from 'app/site/base/projectable';
import { Searchable } from 'app/site/base/searchable';
import { ViewMotion } from './view-motion';
export interface MotionBlockTitleInformation extends TitleInformationWithAgendaItem {
title: string;
@ -16,10 +17,16 @@ export interface MotionBlockTitleInformation extends TitleInformationWithAgendaI
export class ViewMotionBlock extends BaseViewModelWithAgendaItemAndListOfSpeakers
implements MotionBlockTitleInformation, Searchable {
public static COLLECTIONSTRING = MotionBlock.COLLECTIONSTRING;
protected _collectionString = MotionBlock.COLLECTIONSTRING;
private _motions?: ViewMotion[];
public get motionBlock(): MotionBlock {
return this._model;
}
public get motions(): ViewMotion[] {
return this._motions || [];
}
public get title(): string {
return this.motionBlock.title;
@ -29,10 +36,6 @@ export class ViewMotionBlock extends BaseViewModelWithAgendaItemAndListOfSpeaker
return this.motionBlock.internal;
}
public constructor(motionBlock: MotionBlock) {
super(MotionBlock.COLLECTIONSTRING, motionBlock);
}
/**
* Formats the category for search
*

View File

@ -15,15 +15,12 @@ export type MotionChangeRecommendationTitleInformation = object;
export class ViewMotionChangeRecommendation extends BaseViewModel<MotionChangeRecommendation>
implements MotionChangeRecommendationTitleInformation, ViewUnifiedChange {
public static COLLECTIONSTRING = MotionChangeRecommendation.COLLECTIONSTRING;
protected _collectionString = MotionChangeRecommendation.COLLECTIONSTRING;
public get changeRecommendation(): MotionChangeRecommendation {
return this._model;
}
public constructor(motionChangeRecommendation: MotionChangeRecommendation) {
super(MotionChangeRecommendation.COLLECTIONSTRING, motionChangeRecommendation);
}
public updateChangeReco(type: number, text: string, internal: boolean): void {
// @TODO HTML sanitazion
this.changeRecommendation.type = type;

View File

@ -16,6 +16,7 @@ export interface MotionCommentSectionTitleInformation {
export class ViewMotionCommentSection extends BaseViewModel<MotionCommentSection>
implements MotionCommentSectionTitleInformation {
public static COLLECTIONSTRING = MotionCommentSection.COLLECTIONSTRING;
protected _collectionString = MotionCommentSection.COLLECTIONSTRING;
private _read_groups: ViewGroup[];
private _write_groups: ViewGroup[];
@ -59,10 +60,6 @@ export class ViewMotionCommentSection extends BaseViewModel<MotionCommentSection
this._model.name = name;
}
public constructor(motionCommentSection: MotionCommentSection) {
super(MotionCommentSection.COLLECTIONSTRING, motionCommentSection);
}
/**
* Updates the local objects if required
* @param section

View File

@ -62,6 +62,7 @@ export interface MotionTitleInformation extends TitleInformationWithAgendaItem {
export class ViewMotion extends BaseViewModelWithAgendaItemAndListOfSpeakers<Motion>
implements MotionTitleInformation, Searchable {
public static COLLECTIONSTRING = Motion.COLLECTIONSTRING;
protected _collectionString = Motion.COLLECTIONSTRING;
protected _category?: ViewCategory;
protected _submitters?: ViewSubmitter[];
@ -340,10 +341,6 @@ export class ViewMotion extends BaseViewModelWithAgendaItemAndListOfSpeakers<Mot
// This is set by the repository
public getIdentifierOrTitle: () => string;
public constructor(motion: Motion) {
super(Motion.COLLECTIONSTRING, motion);
}
/**
* Formats the category for search
*

View File

@ -12,6 +12,7 @@ export interface StateTitleInformation {
*/
export class ViewState extends BaseViewModel<State> implements StateTitleInformation {
public static COLLECTIONSTRING = State.COLLECTIONSTRING;
protected _collectionString = State.COLLECTIONSTRING;
private _next_states?: ViewState[];
public _workflow?: ViewWorkflow;
@ -92,8 +93,4 @@ export class ViewState extends BaseViewModel<State> implements StateTitleInforma
return state.next_states_id.includes(this.id);
});
}
public constructor(state: State) {
super(State.COLLECTIONSTRING, state);
}
}

View File

@ -17,6 +17,7 @@ export interface StatuteParagraphTitleInformation {
export class ViewStatuteParagraph extends BaseViewModel<StatuteParagraph>
implements StatuteParagraphTitleInformation, Searchable {
public static COLLECTIONSTRING = StatuteParagraph.COLLECTIONSTRING;
protected _collectionString = StatuteParagraph.COLLECTIONSTRING;
public get statuteParagraph(): StatuteParagraph {
return this._model;
@ -34,10 +35,6 @@ export class ViewStatuteParagraph extends BaseViewModel<StatuteParagraph>
return this.statuteParagraph.weight;
}
public constructor(statuteParagraph: StatuteParagraph) {
super(StatuteParagraph.COLLECTIONSTRING, statuteParagraph);
}
public formatForSearch(): SearchRepresentation {
return [this.title];
}

View File

@ -4,6 +4,8 @@ import { ViewUser } from 'app/site/users/models/view-user';
export class ViewSubmitter extends BaseViewModel<Submitter> {
public static COLLECTIONSTRING = Submitter.COLLECTIONSTRING;
protected _collectionString = Submitter.COLLECTIONSTRING;
private _user?: ViewUser;
public get submitter(): Submitter {
@ -30,10 +32,6 @@ export class ViewSubmitter extends BaseViewModel<Submitter> {
return this.submitter.weight;
}
public constructor(submitter: Submitter) {
super(Submitter.COLLECTIONSTRING, submitter);
}
public getTitle = () => {
return this.user ? this.user.getTitle() : '';
};

View File

@ -12,6 +12,7 @@ export interface WorkflowTitleInformation {
*/
export class ViewWorkflow extends BaseViewModel<Workflow> implements WorkflowTitleInformation {
public static COLLECTIONSTRING = Workflow.COLLECTIONSTRING;
protected _collectionString = Workflow.COLLECTIONSTRING;
private _states?: ViewState[];
private _first_state?: ViewState;
@ -39,8 +40,4 @@ export class ViewWorkflow extends BaseViewModel<Workflow> implements WorkflowTit
public get first_state(): ViewState | null {
return this._first_state;
}
public constructor(workflow: Workflow) {
super(Workflow.COLLECTIONSTRING, workflow);
}
}

View File

@ -107,7 +107,7 @@ export class CategoryDetailComponent extends BaseViewComponent implements OnInit
return;
}
// Find index of last child. THis can be easily done by searching, becuase this
// Find index of last child. This can be easily done by searching, because this
// is the flat sorted tree
this.selectedCategory = categories[selectedCategoryIndex];
super.setTitle(this.selectedCategory.prefixedName);
@ -122,24 +122,10 @@ export class CategoryDetailComponent extends BaseViewComponent implements OnInit
this.categories = categories.slice(selectedCategoryIndex, lastChildIndex);
// setup datasources:
const allMotions = this.motionRepo
.getViewModelList()
.sort((a, b) => a.category_weight - b.category_weight);
this.categories.forEach(category => {
if (!this.dataSources[category.id]) {
const dataSource = new MatTableDataSource<ViewMotion>();
dataSource.data = allMotions.filter(motion => motion.category_id === category.id);
dataSource.data = category.motions;
this.dataSources[category.id] = dataSource;
}
});
}),
this.motionRepo.getViewModelListObservable().subscribe(motions => {
motions = motions
.filter(motion => !!motion.category_id)
.sort((a, b) => a.category_weight - b.category_weight);
Object.keys(this.dataSources).forEach(_id => {
const id = +_id;
this.dataSources[id].data = motions.filter(motion => motion.category_id === id);
});
})
);

View File

@ -66,7 +66,7 @@
<span translate>Motions</span>
</mat-header-cell>
<mat-cell *matCellDef="let category">
<span class="os-amount-chip">{{ getMotionAmount(category) }}</span>
<span class="os-amount-chip">{{ category.motions.length }}</span>
</mat-cell>
</ng-container>

View File

@ -95,16 +95,6 @@ export class CategoryListComponent extends BaseViewComponent implements OnInit {
return ['title', 'amount', 'anchor'];
}
/**
* return the amount of motions in a category
*
* @param category the category to determine the amount of motions for
* @returns a number that indicates how many motions are in the given category
*/
public getMotionAmount(category: ViewCategory): number {
return this.repo.getMotionAmountByCategory(category);
}
/**
* Click handler for the plus button
*/

View File

@ -132,7 +132,7 @@ export class MotionBlockDetailComponent extends BaseViewComponent implements OnI
this.dataSource = createDS<ViewMotion>()
.onTrigger(() => {
return this.repo.getViewMotionsByBlock(this.block.motionBlock);
return this.block.motions;
})
.create();
}

View File

@ -15,22 +15,22 @@
(dataSourceChange)="onDataSourceChange($event)"
>
<!-- Title column -->
<div *pblNgridCellDef="'title'; value as title; row as block; rowContext as rowContext" class="cell-slot fill">
<div *pblNgridCellDef="'title'; value as title; row as motionBlock; rowContext as rowContext" class="cell-slot fill">
<a
class="detail-link"
(click)="saveScrollIndex('motionBlock', rowContext.identity)"
[routerLink]="block.id"
[routerLink]="motionBlock.id"
*ngIf="!isMultiSelect"
></a>
<div>
<mat-icon matTooltip="Internal" *ngIf="block.internal">lock</mat-icon>
<mat-icon matTooltip="Internal" *ngIf="motionBlock.internal">lock</mat-icon>
{{ title }}
</div>
</div>
<!-- Amount -->
<div *pblNgridCellDef="'amount'; row as block" class="cell-slot fill">
<span class="os-amount-chip">{{ getMotionAmount(block.motionBlock) }}</span>
<div *pblNgridCellDef="'amount'; row as motionBlock" class="cell-slot fill">
<span class="os-amount-chip">{{ motionBlock.motions.length }}</span>
</div>
</os-list-view-table>
</mat-card>

View File

@ -12,7 +12,6 @@ import { OperatorService } from 'app/core/core-services/operator.service';
import { StorageService } from 'app/core/core-services/storage.service';
import { ItemRepositoryService } from 'app/core/repositories/agenda/item-repository.service';
import { MotionBlockRepositoryService } from 'app/core/repositories/motions/motion-block-repository.service';
import { MotionBlock } from 'app/shared/models/motions/motion-block';
import { infoDialogSettings } from 'app/shared/utils/dialog-settings';
import { ViewItem } from 'app/site/agenda/models/view-item';
import { BaseListViewComponent } from 'app/site/base/base-list-view';
@ -117,16 +116,6 @@ export class MotionBlockListComponent extends BaseListViewComponent<ViewMotionBl
this.items = this.itemRepo.getViewModelListBehaviorSubject();
}
/**
* return the amount of motions in a motion block
*
* @param motionBlock the block to determine the amount of motions for
* @returns a number that indicates how many motions are in the given block
*/
public getMotionAmount(motionBlock: MotionBlock): number {
return this.repo.getMotionAmountByBlock(motionBlock);
}
/**
* Helper function reset the form and set the default values
*/

View File

@ -1,4 +1,4 @@
import { AppConfig } from '../../core/app-config';
import { AppConfig } from '../../core/definitions/app-config';
import { CategoryRepositoryService } from 'app/core/repositories/motions/category-repository.service';
import { ChangeRecommendationRepositoryService } from 'app/core/repositories/motions/change-recommendation-repository.service';
import { MotionBlockRepositoryService } from 'app/core/repositories/motions/motion-block-repository.service';

View File

@ -4,7 +4,7 @@ import { TranslateService } from '@ngx-translate/core';
import { OpenSlidesStatusService } from 'app/core/core-services/openslides-status.service';
import { StorageService } from 'app/core/core-services/storage.service';
import { Deferred } from 'app/core/deferred';
import { Deferred } from 'app/core/promises/deferred';
import { _ } from 'app/core/translate/translation-marker';
import { BaseSortListService, OsSortingDefinition, OsSortingOption } from 'app/core/ui-services/base-sort-list.service';
import { ConfigService } from 'app/core/ui-services/config.service';

View File

@ -9,6 +9,7 @@ export interface CountdownTitleInformation {
export class ViewCountdown extends BaseProjectableViewModel<Countdown> implements CountdownTitleInformation {
public static COLLECTIONSTRING = Countdown.COLLECTIONSTRING;
protected _collectionString = Countdown.COLLECTIONSTRING;
public get countdown(): Countdown {
return this._model;
@ -34,10 +35,6 @@ export class ViewCountdown extends BaseProjectableViewModel<Countdown> implement
return this.countdown.title;
}
public constructor(countdown: Countdown) {
super(Countdown.COLLECTIONSTRING, countdown);
}
public getSlide(): ProjectorElementBuildDeskriptor {
return {
getBasicProjectorElement: options => ({

View File

@ -8,6 +8,7 @@ export interface ProjectionDefaultTitleInformation {
export class ViewProjectionDefault extends BaseViewModel<ProjectionDefault>
implements ProjectionDefaultTitleInformation {
public static COLLECTIONSTRING = ProjectionDefault.COLLECTIONSTRING;
protected _collectionString = ProjectionDefault.COLLECTIONSTRING;
public get projectionDefault(): ProjectionDefault {
return this._model;
@ -24,8 +25,4 @@ export class ViewProjectionDefault extends BaseViewModel<ProjectionDefault>
public get display_name(): string {
return this.projectionDefault.display_name;
}
public constructor(projectionDefault: ProjectionDefault) {
super(ProjectionDefault.COLLECTIONSTRING, projectionDefault);
}
}

View File

@ -8,6 +8,7 @@ export type ProjectorMessageTitleInformation = object;
export class ViewProjectorMessage extends BaseProjectableViewModel<ProjectorMessage>
implements ProjectorMessageTitleInformation {
public static COLLECTIONSTRING = ProjectorMessage.COLLECTIONSTRING;
protected _collectionString = ProjectorMessage.COLLECTIONSTRING;
public get projectormessage(): ProjectorMessage {
return this._model;
@ -17,10 +18,6 @@ export class ViewProjectorMessage extends BaseProjectableViewModel<ProjectorMess
return this.projectormessage.message;
}
public constructor(projectorMessage: ProjectorMessage) {
super(ProjectorMessage.COLLECTIONSTRING, projectorMessage);
}
public getSlide(): ProjectorElementBuildDeskriptor {
return {
getBasicProjectorElement: options => ({

View File

@ -7,6 +7,7 @@ export interface ProjectorTitleInformation {
export class ViewProjector extends BaseViewModel<Projector> {
public static COLLECTIONSTRING = Projector.COLLECTIONSTRING;
protected _collectionString = Projector.COLLECTIONSTRING;
private _referenceProjector: ViewProjector;
@ -105,8 +106,4 @@ export class ViewProjector extends BaseViewModel<Projector> {
public get show_logo(): boolean {
return this.projector.show_logo;
}
public constructor(projector: Projector) {
super(Projector.COLLECTIONSTRING, projector);
}
}

View File

@ -1,4 +1,4 @@
import { AppConfig } from '../../core/app-config';
import { AppConfig } from '../../core/definitions/app-config';
import { CountdownRepositoryService } from 'app/core/repositories/projector/countdown-repository.service';
import { ProjectionDefaultRepositoryService } from 'app/core/repositories/projector/projection-default-repository.service';
import { ProjectorMessageRepositoryService } from 'app/core/repositories/projector/projector-message-repository.service';

View File

@ -16,6 +16,7 @@ export interface TagTitleInformation {
*/
export class ViewTag extends BaseViewModel<Tag> implements TagTitleInformation, Searchable {
public static COLLECTIONSTRING = Tag.COLLECTIONSTRING;
protected _collectionString = Tag.COLLECTIONSTRING;
public get tag(): Tag {
return this._model;
@ -25,10 +26,6 @@ export class ViewTag extends BaseViewModel<Tag> implements TagTitleInformation,
return this.tag.name;
}
public constructor(tag: Tag) {
super(Tag.COLLECTIONSTRING, tag);
}
public formatForSearch(): SearchRepresentation {
return [this.name];
}

View File

@ -1,4 +1,4 @@
import { AppConfig } from '../../core/app-config';
import { AppConfig } from '../../core/definitions/app-config';
import { TagRepositoryService } from 'app/core/repositories/tags/tag-repository.service';
import { Tag } from '../../shared/models/core/tag';
import { ViewTag } from './models/view-tag';

View File

@ -16,6 +16,7 @@ export interface TopicTitleInformation extends TitleInformationWithAgendaItem {
*/
export class ViewTopic extends BaseViewModelWithAgendaItemAndListOfSpeakers implements TopicTitleInformation {
public static COLLECTIONSTRING = Topic.COLLECTIONSTRING;
protected _collectionString = Topic.COLLECTIONSTRING;
private _attachments?: ViewMediafile[];
@ -39,10 +40,6 @@ export class ViewTopic extends BaseViewModelWithAgendaItemAndListOfSpeakers impl
return this.topic.text;
}
public constructor(topic: Topic) {
super(Topic.COLLECTIONSTRING, topic);
}
/**
* Formats the category for search
*

View File

@ -1,4 +1,4 @@
import { AppConfig } from '../../core/app-config';
import { AppConfig } from '../../core/definitions/app-config';
import { TopicRepositoryService } from 'app/core/repositories/topics/topic-repository.service';
import { Topic } from '../../shared/models/topics/topic';
import { ViewTopic } from './models/view-topic';

View File

@ -7,6 +7,7 @@ export interface GroupTitleInformation {
export class ViewGroup extends BaseViewModel<Group> implements GroupTitleInformation {
public static COLLECTIONSTRING = Group.COLLECTIONSTRING;
protected _collectionString = Group.COLLECTIONSTRING;
public get group(): Group {
return this._model;
@ -29,10 +30,6 @@ export class ViewGroup extends BaseViewModel<Group> implements GroupTitleInforma
return this.group.permissions;
}
public constructor(group?: Group) {
super(Group.COLLECTIONSTRING, group);
}
public hasPermission(perm: string): boolean {
return this.permissions.includes(perm);
}

View File

@ -5,6 +5,7 @@ export type PersonalNoteTitleInformation = object;
export class ViewPersonalNote extends BaseViewModel<PersonalNote> implements PersonalNoteTitleInformation {
public static COLLECTIONSTRING = PersonalNote.COLLECTIONSTRING;
protected _collectionString = PersonalNote.COLLECTIONSTRING;
public get personalNote(): PersonalNote {
return this._model;
@ -18,10 +19,6 @@ export class ViewPersonalNote extends BaseViewModel<PersonalNote> implements Per
return this.personalNote.notes;
}
public constructor(personalNote: PersonalNote) {
super(PersonalNote.COLLECTIONSTRING, personalNote);
}
public getNoteContent(collection: string, id: number): PersonalNoteContent | null {
if (this.notes[collection]) {
return this.notes[collection][id];

View File

@ -16,6 +16,7 @@ export interface UserTitleInformation {
export class ViewUser extends BaseProjectableViewModel<User> implements UserTitleInformation, Searchable {
public static COLLECTIONSTRING = User.COLLECTIONSTRING;
protected _collectionString = User.COLLECTIONSTRING;
private _groups: ViewGroup[];
@ -120,10 +121,6 @@ export class ViewUser extends BaseProjectableViewModel<User> implements UserTitl
public getFullName: () => string;
public getShortName: () => string;
public constructor(user: User) {
super(User.COLLECTIONSTRING, user);
}
/**
* Formats the category for search
*

View File

@ -1,4 +1,4 @@
import { AppConfig } from '../../core/app-config';
import { AppConfig } from '../../core/definitions/app-config';
import { GroupRepositoryService } from 'app/core/repositories/users/group-repository.service';
import { PersonalNoteRepositoryService } from 'app/core/repositories/users/personal-note-repository.service';
import { UserRepositoryService } from 'app/core/repositories/users/user-repository.service';