Split AgendaItem and ListOfSpeakers
Server: - ListOfSpeakers (LOS) is now a speprate model, containing of an id, speakers, closed and a content_object. - Moved all speaker related views from ItemViewSet to the new ListOfSpeakersViewSet. - Make Mixins for content objects of items and lists of speakers. - Migrations: Move the lists of speakers from items to the LOS model. Client: - Removed the speaker repo and moved functionality to the new ListOfSpeakersRepositoryService. - Splitted base classes for agenda item content objects to items and LOS. - CurrentAgendaItemService -> CurrentListOfSpeakersSerivce - Cleaned up the list of speakers component.
This commit is contained in:
parent
61057a6e09
commit
9f12763f8b
@ -8,7 +8,7 @@ import { BaseViewModel, ViewModelConstructor } from 'app/site/base/base-view-mod
|
||||
|
||||
interface BaseModelEntry {
|
||||
collectionString: string;
|
||||
repository: Type<BaseRepository<any, any>>;
|
||||
repository: Type<BaseRepository<any, any, any>>;
|
||||
model: ModelConstructor<BaseModel>;
|
||||
}
|
||||
|
||||
|
@ -69,7 +69,7 @@ export class AppLoadService {
|
||||
appConfigs.forEach((config: AppConfig) => {
|
||||
if (config.models) {
|
||||
config.models.forEach(entry => {
|
||||
let repository: BaseRepository<any, any> = null;
|
||||
let repository: BaseRepository<any, any, any> = null;
|
||||
repository = this.injector.get(entry.repository);
|
||||
repositories.push(repository);
|
||||
this.modelMapper.registerCollectionElement(
|
||||
|
@ -123,11 +123,7 @@ export class AutoupdateService {
|
||||
|
||||
// Add the objects to the DataStore.
|
||||
for (const collection of Object.keys(autoupdate.changed)) {
|
||||
if (this.modelMapper.isCollectionRegistered(collection)) {
|
||||
await this.DS.add(this.mapObjectsToBaseModels(collection, autoupdate.changed[collection]));
|
||||
} else {
|
||||
console.error(`Unregistered collection "${collection}". Ignore it.`);
|
||||
}
|
||||
await this.DS.add(this.mapObjectsToBaseModels(collection, autoupdate.changed[collection]));
|
||||
}
|
||||
|
||||
await this.DS.flushToStorage(autoupdate.to_change_id);
|
||||
@ -140,14 +136,21 @@ export class AutoupdateService {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates baseModels for each plain object
|
||||
* Creates baseModels for each plain object. If the collection is not registered,
|
||||
* A console error will be issued and an empty list returned.
|
||||
*
|
||||
* @param collection The collection all models have to be from.
|
||||
* @param models All models that should be mapped to BaseModels
|
||||
* @returns A list of basemodels constructed from the given models.
|
||||
*/
|
||||
private mapObjectsToBaseModels(collection: string, models: object[]): BaseModel[] {
|
||||
const targetClass = this.modelMapper.getModelConstructor(collection);
|
||||
return models.map(model => new targetClass(model));
|
||||
if (this.modelMapper.isCollectionRegistered(collection)) {
|
||||
const targetClass = this.modelMapper.getModelConstructor(collection);
|
||||
return models.map(model => new targetClass(model));
|
||||
} else {
|
||||
console.error(`Unregistered collection "${collection}". Ignore it.`);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -2,7 +2,7 @@ import { Injectable } from '@angular/core';
|
||||
|
||||
import { ModelConstructor, BaseModel } from '../../shared/models/base/base-model';
|
||||
import { BaseRepository } from 'app/core/repositories/base-repository';
|
||||
import { ViewModelConstructor, BaseViewModel } from 'app/site/base/base-view-model';
|
||||
import { ViewModelConstructor, BaseViewModel, TitleInformation } from 'app/site/base/base-view-model';
|
||||
|
||||
/**
|
||||
* Unifies the ModelConstructor and ViewModelConstructor.
|
||||
@ -15,12 +15,12 @@ interface UnifiedConstructors {
|
||||
/**
|
||||
* Every types supported: (View)ModelConstructors, repos and collectionstrings.
|
||||
*/
|
||||
type TypeIdentifier = UnifiedConstructors | BaseRepository<any, any> | string;
|
||||
type TypeIdentifier = UnifiedConstructors | BaseRepository<any, any, any> | string;
|
||||
|
||||
type CollectionStringMappedTypes = [
|
||||
ModelConstructor<BaseModel>,
|
||||
ViewModelConstructor<BaseViewModel>,
|
||||
BaseRepository<BaseViewModel, BaseModel>
|
||||
BaseRepository<BaseViewModel, BaseModel, TitleInformation>
|
||||
];
|
||||
|
||||
/**
|
||||
@ -50,7 +50,7 @@ export class CollectionStringMapperService {
|
||||
collectionString: string,
|
||||
model: ModelConstructor<M>,
|
||||
viewModel: ViewModelConstructor<V>,
|
||||
repository: BaseRepository<V, M>
|
||||
repository: BaseRepository<V, M, TitleInformation>
|
||||
): void {
|
||||
this.collectionStringMapping[collectionString] = [model, viewModel, repository];
|
||||
}
|
||||
@ -98,18 +98,18 @@ export class CollectionStringMapperService {
|
||||
* @param obj The object to get the repository from.
|
||||
* @returns the repository
|
||||
*/
|
||||
public getRepository<V extends BaseViewModel, M extends BaseModel>(
|
||||
public getRepository<V extends BaseViewModel, M extends BaseModel, T extends TitleInformation>(
|
||||
obj: TypeIdentifier
|
||||
): BaseRepository<V, M> | null {
|
||||
): BaseRepository<V & T, M, T> | null {
|
||||
if (this.isCollectionRegistered(this.getCollectionString(obj))) {
|
||||
return this.collectionStringMapping[this.getCollectionString(obj)][2] as BaseRepository<V, M>;
|
||||
return this.collectionStringMapping[this.getCollectionString(obj)][2] as BaseRepository<V & T, M, T>;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns all registered repositories.
|
||||
*/
|
||||
public getAllRepositories(): BaseRepository<any, any>[] {
|
||||
public getAllRepositories(): BaseRepository<any, any, any>[] {
|
||||
return Object.values(this.collectionStringMapping).map((types: CollectionStringMappedTypes) => types[2]);
|
||||
}
|
||||
}
|
||||
|
@ -120,7 +120,12 @@ export class HttpService {
|
||||
return error;
|
||||
}
|
||||
|
||||
if (!e.error) {
|
||||
if (e.status === 405) {
|
||||
// this should only happen, if the url is wrong -> a bug.
|
||||
error += this.translate.instant(
|
||||
'The requested method is not allowed. Please contact your system administrator.'
|
||||
);
|
||||
} else if (!e.error) {
|
||||
error += this.translate.instant("The server didn't respond.");
|
||||
} else if (typeof e.error === 'object') {
|
||||
if (e.error.detail) {
|
||||
|
@ -20,10 +20,10 @@ export class ViewModelStoreService {
|
||||
*
|
||||
* @param collectionType The collection string or constructor.
|
||||
*/
|
||||
private getRepository<T extends BaseViewModel>(
|
||||
collectionType: ViewModelConstructor<T> | string
|
||||
): BaseRepository<T, any> {
|
||||
return this.mapperService.getRepository(collectionType) as BaseRepository<T, any>;
|
||||
private getRepository<V extends BaseViewModel>(
|
||||
collectionType: ViewModelConstructor<V> | string
|
||||
): BaseRepository<V, any, any> {
|
||||
return this.mapperService.getRepository(collectionType) as BaseRepository<V, any, any>;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -32,7 +32,7 @@ export class ViewModelStoreService {
|
||||
* @param collectionString The collection of the view model
|
||||
* @param id The id of the view model
|
||||
*/
|
||||
public get<T extends BaseViewModel>(collectionType: ViewModelConstructor<T> | string, id: number): T {
|
||||
public get<V extends BaseViewModel>(collectionType: ViewModelConstructor<V> | string, id: number): V {
|
||||
return this.getRepository(collectionType).getViewModel(id);
|
||||
}
|
||||
|
||||
|
@ -4,33 +4,41 @@ import { map } from 'rxjs/operators';
|
||||
import { Observable } from 'rxjs';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
|
||||
import { BaseRepository } from '../base-repository';
|
||||
import { CollectionStringMapperService } from '../../core-services/collection-string-mapper.service';
|
||||
import { ConfigService } from 'app/core/ui-services/config.service';
|
||||
import { DataSendService } from 'app/core/core-services/data-send.service';
|
||||
import { DataStoreService } from '../../core-services/data-store.service';
|
||||
import { HttpService } from 'app/core/core-services/http.service';
|
||||
import { Item } from 'app/shared/models/agenda/item';
|
||||
import { ViewItem } from 'app/site/agenda/models/view-item';
|
||||
import { TreeIdNode } from 'app/core/ui-services/tree.service';
|
||||
import { BaseAgendaViewModel } from 'app/site/base/base-agenda-view-model';
|
||||
import { ViewItem, ItemTitleInformation } from 'app/site/agenda/models/view-item';
|
||||
import {
|
||||
BaseViewModelWithAgendaItem,
|
||||
isBaseViewModelWithAgendaItem
|
||||
} from 'app/site/base/base-view-model-with-agenda-item';
|
||||
import { ViewModelStoreService } from 'app/core/core-services/view-model-store.service';
|
||||
import { BaseViewModel } from 'app/site/base/base-view-model';
|
||||
import { BaseAgendaContentObjectRepository } from '../base-agenda-content-object-repository';
|
||||
import { Motion } from 'app/shared/models/motions/motion';
|
||||
import { MotionBlock } from 'app/shared/models/motions/motion-block';
|
||||
import { Topic } from 'app/shared/models/topics/topic';
|
||||
import { Assignment } from 'app/shared/models/assignments/assignment';
|
||||
import { BaseIsAgendaItemContentObjectRepository } from '../base-is-agenda-item-content-object-repository';
|
||||
import { BaseHasContentObjectRepository } from '../base-has-content-object-repository';
|
||||
|
||||
/**
|
||||
* Repository service for users
|
||||
* Repository service for items
|
||||
*
|
||||
* Documentation partially provided in {@link BaseRepository}
|
||||
*/
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class ItemRepositoryService extends BaseRepository<ViewItem, Item> {
|
||||
export class ItemRepositoryService extends BaseHasContentObjectRepository<
|
||||
ViewItem,
|
||||
Item,
|
||||
BaseViewModelWithAgendaItem,
|
||||
ItemTitleInformation
|
||||
> {
|
||||
/**
|
||||
* Contructor for agenda repository.
|
||||
*
|
||||
@ -57,21 +65,24 @@ export class ItemRepositoryService extends BaseRepository<ViewItem, Item> {
|
||||
MotionBlock
|
||||
]);
|
||||
|
||||
this.setSortFunction((a, b) => {
|
||||
// TODO: In some occasions weight will be undefined, if the user has not the correct set of permission.
|
||||
// That should not be the case here.
|
||||
if (a.weight && b.weight) {
|
||||
return a.weight - b.weight;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
});
|
||||
this.setSortFunction((a, b) => a.weight - b.weight);
|
||||
}
|
||||
|
||||
public getVerboseName = (plural: boolean = false) => {
|
||||
return this.translate.instant(plural ? 'Items' : 'Item');
|
||||
};
|
||||
|
||||
public getTitle = (titleInformation: ItemTitleInformation) => {
|
||||
if (titleInformation.contentObject) {
|
||||
return titleInformation.contentObject.getAgendaListTitle();
|
||||
} else {
|
||||
const repo = this.collectionStringMapperService.getRepository(
|
||||
titleInformation.contentObjectData.collection
|
||||
) as BaseIsAgendaItemContentObjectRepository<any, any, any>;
|
||||
return repo.getAgendaListTitle(titleInformation.title_information);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates the viewItem out of a given item
|
||||
*
|
||||
@ -80,31 +91,16 @@ export class ItemRepositoryService extends BaseRepository<ViewItem, Item> {
|
||||
*/
|
||||
public createViewModel(item: Item): ViewItem {
|
||||
const contentObject = this.getContentObject(item);
|
||||
const viewItem = new ViewItem(item, contentObject);
|
||||
viewItem.getVerboseName = this.getVerboseName;
|
||||
viewItem.getTitle = () => {
|
||||
const numberPrefix = viewItem.itemNumber ? `${viewItem.itemNumber} · ` : '';
|
||||
|
||||
if (viewItem.contentObject) {
|
||||
return numberPrefix + viewItem.contentObject.getAgendaTitleWithType();
|
||||
} else {
|
||||
const repo = this.collectionStringMapperService.getRepository(
|
||||
viewItem.item.content_object.collection
|
||||
) as BaseAgendaContentObjectRepository<any, any>;
|
||||
return numberPrefix + repo.getAgendaTitleWithType(viewItem.title_information);
|
||||
}
|
||||
};
|
||||
viewItem.getListTitle = viewItem.getTitle;
|
||||
return viewItem;
|
||||
return new ViewItem(item, contentObject);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the corresponding content object to a given {@link Item} as an {@link AgendaBaseViewModel}
|
||||
* Returns the corresponding content object to a given {@link Item} as an {@link BaseAgendaItemViewModel}
|
||||
*
|
||||
* @param agendaItem the target agenda Item
|
||||
* @returns the content object of the given item. Might be null if it was not found.
|
||||
*/
|
||||
public getContentObject(agendaItem: Item): BaseAgendaViewModel {
|
||||
public getContentObject(agendaItem: Item): BaseViewModelWithAgendaItem {
|
||||
const contentObject = this.viewModelStoreService.get<BaseViewModel>(
|
||||
agendaItem.content_object.collection,
|
||||
agendaItem.content_object.id
|
||||
@ -112,13 +108,13 @@ export class ItemRepositoryService extends BaseRepository<ViewItem, Item> {
|
||||
if (!contentObject) {
|
||||
return null;
|
||||
}
|
||||
if (contentObject instanceof BaseAgendaViewModel) {
|
||||
return contentObject as BaseAgendaViewModel;
|
||||
if (isBaseViewModelWithAgendaItem(contentObject)) {
|
||||
return contentObject;
|
||||
} else {
|
||||
throw new Error(
|
||||
`The content object (${agendaItem.content_object.collection}, ${
|
||||
agendaItem.content_object.id
|
||||
}) of item ${agendaItem.id} is not a AgendaBaseViewModel.`
|
||||
}) of item ${agendaItem.id} is not a BaseAgendaItemViewModel.`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,17 @@
|
||||
import { TestBed, inject } from '@angular/core/testing';
|
||||
|
||||
import { E2EImportsModule } from 'e2e-imports.module';
|
||||
import { ListOfSpeakersRepositoryService } from './list-of-speakers-repository.service';
|
||||
|
||||
describe('ListOfSpeakersRepositoryService', () => {
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [E2EImportsModule],
|
||||
providers: [ListOfSpeakersRepositoryService]
|
||||
});
|
||||
});
|
||||
|
||||
it('should be created', inject([ListOfSpeakersRepositoryService], (service: ListOfSpeakersRepositoryService) => {
|
||||
expect(service).toBeTruthy();
|
||||
}));
|
||||
});
|
@ -0,0 +1,227 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
|
||||
import { CollectionStringMapperService } from '../../core-services/collection-string-mapper.service';
|
||||
import { DataSendService } from 'app/core/core-services/data-send.service';
|
||||
import { DataStoreService } from '../../core-services/data-store.service';
|
||||
import { ViewModelStoreService } from 'app/core/core-services/view-model-store.service';
|
||||
import { ViewListOfSpeakers, ListOfSpeakersTitleInformation } from 'app/site/agenda/models/view-list-of-speakers';
|
||||
import { ListOfSpeakers } from 'app/shared/models/agenda/list-of-speakers';
|
||||
import {
|
||||
BaseViewModelWithListOfSpeakers,
|
||||
isBaseViewModelWithListOfSpeakers
|
||||
} from 'app/site/base/base-view-model-with-list-of-speakers';
|
||||
import { BaseViewModel } from 'app/site/base/base-view-model';
|
||||
import { ViewSpeaker } from 'app/site/agenda/models/view-speaker';
|
||||
import { ViewUser } from 'app/site/users/models/view-user';
|
||||
import { Identifiable } from 'app/shared/models/base/identifiable';
|
||||
import { HttpService } from 'app/core/core-services/http.service';
|
||||
import { BaseIsListOfSpeakersContentObjectRepository } from '../base-is-list-of-speakers-content-object-repository';
|
||||
import { BaseHasContentObjectRepository } from '../base-has-content-object-repository';
|
||||
import { Topic } from 'app/shared/models/topics/topic';
|
||||
import { Assignment } from 'app/shared/models/assignments/assignment';
|
||||
import { Motion } from 'app/shared/models/motions/motion';
|
||||
import { MotionBlock } from 'app/shared/models/motions/motion-block';
|
||||
import { Mediafile } from 'app/shared/models/mediafiles/mediafile';
|
||||
import { ItemRepositoryService } from './item-repository.service';
|
||||
import { User } from 'app/shared/models/users/user';
|
||||
|
||||
/**
|
||||
* Repository service for lists of speakers
|
||||
*
|
||||
* Documentation partially provided in {@link BaseRepository}
|
||||
*/
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class ListOfSpeakersRepositoryService extends BaseHasContentObjectRepository<
|
||||
ViewListOfSpeakers,
|
||||
ListOfSpeakers,
|
||||
BaseViewModelWithListOfSpeakers,
|
||||
ListOfSpeakersTitleInformation
|
||||
> {
|
||||
/**
|
||||
* Contructor for agenda repository.
|
||||
*
|
||||
* @param DS The DataStore
|
||||
* @param httpService OpenSlides own HttpService
|
||||
* @param mapperService OpenSlides mapping service for collection strings
|
||||
* @param config Read config variables
|
||||
* @param dataSend send models to the server
|
||||
* @param treeService sort the data according to weight and parents
|
||||
*/
|
||||
public constructor(
|
||||
DS: DataStoreService,
|
||||
dataSend: DataSendService,
|
||||
mapperService: CollectionStringMapperService,
|
||||
viewModelStoreService: ViewModelStoreService,
|
||||
translate: TranslateService,
|
||||
private httpService: HttpService,
|
||||
private itemRepo: ItemRepositoryService
|
||||
) {
|
||||
super(DS, dataSend, mapperService, viewModelStoreService, translate, ListOfSpeakers, [
|
||||
Topic,
|
||||
Assignment,
|
||||
Motion,
|
||||
MotionBlock,
|
||||
Mediafile,
|
||||
User
|
||||
]);
|
||||
}
|
||||
|
||||
public getVerboseName = (plural: boolean = false) => {
|
||||
return this.translate.instant(plural ? 'Lists of speakers' : 'List of speakers');
|
||||
};
|
||||
|
||||
public getTitle = (titleInformation: ListOfSpeakersTitleInformation) => {
|
||||
if (titleInformation.contentObject) {
|
||||
return titleInformation.contentObject.getListOfSpeakersTitle();
|
||||
} else {
|
||||
const repo = this.collectionStringMapperService.getRepository(
|
||||
titleInformation.contentObjectData.collection
|
||||
) as BaseIsListOfSpeakersContentObjectRepository<any, any, any>;
|
||||
|
||||
// Try to get the agenda item for this to get the item number
|
||||
// TODO: This can be resolved with #4738
|
||||
const item = this.itemRepo.findByContentObject(titleInformation.contentObjectData);
|
||||
if (item) {
|
||||
(<any>titleInformation.title_information).agenda_item_number = item.itemNumber;
|
||||
}
|
||||
|
||||
return repo.getListOfSpeakersTitle(titleInformation.title_information);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates the viewListOfSpeakers out of a given list of speakers
|
||||
*
|
||||
* @param listOfSpeakers the list fo speakers that should be converted to view item
|
||||
* @returns a new view list fo speakers
|
||||
*/
|
||||
public createViewModel(listOfSpeakers: ListOfSpeakers): ViewListOfSpeakers {
|
||||
const contentObject = this.getContentObject(listOfSpeakers);
|
||||
const speakers = this.getSpeakers(listOfSpeakers);
|
||||
return new ViewListOfSpeakers(listOfSpeakers, speakers, contentObject);
|
||||
}
|
||||
|
||||
private getSpeakers(listOfSpeakers: ListOfSpeakers): ViewSpeaker[] {
|
||||
return listOfSpeakers.speakers.map(speaker => {
|
||||
const user = this.viewModelStoreService.get(ViewUser, speaker.user_id);
|
||||
return new ViewSpeaker(speaker, user);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the corresponding content object to a given {@link ListOfSpeakers} as an {@link BaseListOfSpeakersViewModel}
|
||||
*
|
||||
* @param listOfSpeakers the target list fo speakers
|
||||
* @returns the content object of the given list of sepakers. Might be null if it was not found.
|
||||
*/
|
||||
public getContentObject(listOfSpeakers: ListOfSpeakers): BaseViewModelWithListOfSpeakers {
|
||||
const contentObject = this.viewModelStoreService.get<BaseViewModel>(
|
||||
listOfSpeakers.content_object.collection,
|
||||
listOfSpeakers.content_object.id
|
||||
);
|
||||
if (!contentObject) {
|
||||
return null;
|
||||
}
|
||||
if (isBaseViewModelWithListOfSpeakers(contentObject)) {
|
||||
return contentObject;
|
||||
} else {
|
||||
throw new Error(
|
||||
`The content object (${listOfSpeakers.content_object.collection}, ${
|
||||
listOfSpeakers.content_object.id
|
||||
}) of list of speakers ${listOfSpeakers.id} is not a BaseListOfSpeakersViewModel.`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new speaker to a list of speakers.
|
||||
* Sends the users id to the server
|
||||
*
|
||||
* @param userId {@link User} id of the new speaker
|
||||
* @param listOfSpeakers the target agenda item
|
||||
*/
|
||||
public async createSpeaker(listOfSpeakers: ViewListOfSpeakers, userId: number): Promise<Identifiable> {
|
||||
const restUrl = this.getRestUrl(listOfSpeakers.id, 'manage_speaker');
|
||||
return await this.httpService.post<Identifiable>(restUrl, { user: userId });
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the given speaker for the list of speakers
|
||||
*
|
||||
* @param listOfSpeakers the target list of speakers
|
||||
* @param speakerId (otional) the speakers id. If no id is given, the speaker with the
|
||||
* current operator is removed.
|
||||
*/
|
||||
public async delete(listOfSpeakers: ViewListOfSpeakers, speakerId?: number): Promise<void> {
|
||||
const restUrl = this.getRestUrl(listOfSpeakers.id, 'manage_speaker');
|
||||
await this.httpService.delete(restUrl, speakerId ? { speaker: speakerId } : null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes all speakers of the given list of speakers.
|
||||
*
|
||||
* @param listOfSpeakers the target list of speakers
|
||||
*/
|
||||
public async deleteAllSpeakers(listOfSpeakers: ViewListOfSpeakers): Promise<void> {
|
||||
const restUrl = this.getRestUrl(listOfSpeakers.id, 'manage_speaker');
|
||||
await this.httpService.delete(restUrl, { speaker: listOfSpeakers.speakers.map(speaker => speaker.id) });
|
||||
}
|
||||
|
||||
/**
|
||||
* Posts an (manually) sorted speaker list to the server
|
||||
*
|
||||
* @param speakerIds array of speaker id numbers
|
||||
* @param Item the target agenda item
|
||||
*/
|
||||
public async sortSpeakers(listOfSpeakers: ViewListOfSpeakers, speakerIds: number[]): Promise<void> {
|
||||
const restUrl = this.getRestUrl(listOfSpeakers.id, 'sort_speakers');
|
||||
await this.httpService.post(restUrl, { speakers: speakerIds });
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks all speakers for a given user
|
||||
*
|
||||
* @param userId {@link User} id of the user
|
||||
* @param marked determine if the user should be marked or not
|
||||
* @param listOfSpeakers the target list of speakers
|
||||
*/
|
||||
public async markSpeaker(listOfSpeakers: ViewListOfSpeakers, speaker: ViewSpeaker, marked: boolean): Promise<void> {
|
||||
const restUrl = this.getRestUrl(listOfSpeakers.id, 'manage_speaker');
|
||||
await this.httpService.patch(restUrl, { user: speaker.user.id, marked: marked });
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops the current speaker
|
||||
*
|
||||
* @param listOfSpeakers the target list of speakers
|
||||
*/
|
||||
public async stopCurrentSpeaker(listOfSpeakers: ViewListOfSpeakers): Promise<void> {
|
||||
const restUrl = this.getRestUrl(listOfSpeakers.id, 'speak');
|
||||
await this.httpService.delete(restUrl);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the given speaker id to speak
|
||||
*
|
||||
* @param speakerId the speakers id
|
||||
* @param listOfSpeakers the target list of speakers
|
||||
*/
|
||||
public async startSpeaker(listOfSpeakers: ViewListOfSpeakers, speaker: ViewSpeaker): Promise<void> {
|
||||
const restUrl = this.getRestUrl(listOfSpeakers.id, 'speak');
|
||||
await this.httpService.put(restUrl, { speaker: speaker.id });
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function get the url to the speaker rest address
|
||||
*
|
||||
* @param listOfSpeakersId id of the list of speakers
|
||||
* @param method the desired speaker action
|
||||
*/
|
||||
private getRestUrl(listOfSpeakersId: number, method: 'manage_speaker' | 'sort_speakers' | 'speak'): string {
|
||||
return `/rest/agenda/list-of-speakers/${listOfSpeakersId}/${method}/`;
|
||||
}
|
||||
}
|
@ -1,17 +0,0 @@
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
|
||||
import { SpeakerRepositoryService } from './speaker-repository.service';
|
||||
import { E2EImportsModule } from 'e2e-imports.module';
|
||||
|
||||
describe('SpeakerRepositoryService', () => {
|
||||
beforeEach(() =>
|
||||
TestBed.configureTestingModule({
|
||||
imports: [E2EImportsModule]
|
||||
})
|
||||
);
|
||||
|
||||
it('should be created', () => {
|
||||
const service: SpeakerRepositoryService = TestBed.get(SpeakerRepositoryService);
|
||||
expect(service).toBeTruthy();
|
||||
});
|
||||
});
|
@ -1,163 +0,0 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
|
||||
import { HttpService } from 'app/core/core-services/http.service';
|
||||
import { Identifiable } from 'app/shared/models/base/identifiable';
|
||||
import { Item } from 'app/shared/models/agenda/item';
|
||||
import { Speaker } from 'app/shared/models/agenda/speaker';
|
||||
import { ViewSpeaker } from 'app/site/agenda/models/view-speaker';
|
||||
import { ViewModelStoreService } from 'app/core/core-services/view-model-store.service';
|
||||
import { ViewItem } from 'app/site/agenda/models/view-item';
|
||||
import { ViewUser } from 'app/site/users/models/view-user';
|
||||
|
||||
/**
|
||||
* Define what actions might occur on speaker lists
|
||||
*/
|
||||
type SpeakerAction = 'manage_speaker' | 'sort_speakers' | 'speak';
|
||||
|
||||
/**
|
||||
* Repository for speakers.
|
||||
*
|
||||
* Since speakers are no base models, normal repository methods do not apply here.
|
||||
*/
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class SpeakerRepositoryService {
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param viewModelStoreService To get the view users
|
||||
* @param httpService make custom requests
|
||||
* @param translate translate
|
||||
*/
|
||||
public constructor(
|
||||
private viewModelStoreService: ViewModelStoreService,
|
||||
private httpService: HttpService,
|
||||
private translate: TranslateService
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Add a new speaker to an agenda item.
|
||||
* Sends the users ID to the server
|
||||
* Might need another repo
|
||||
*
|
||||
* @param speakerId {@link User} id of the new speaker
|
||||
* @param item the target agenda item
|
||||
*/
|
||||
public async create(speakerId: number, item: ViewItem): Promise<void> {
|
||||
const restUrl = this.getRestUrl(item.id, 'manage_speaker');
|
||||
await this.httpService.post<Identifiable>(restUrl, { user: speakerId });
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the given speaker for the agenda item
|
||||
*
|
||||
* @param item the target agenda item
|
||||
* @param speakerId (otional) the speakers id. If no id is given, the current operator
|
||||
* is removed.
|
||||
*/
|
||||
public async delete(item: ViewItem, speakerId?: number): Promise<void> {
|
||||
const restUrl = this.getRestUrl(item.id, 'manage_speaker');
|
||||
await this.httpService.delete(restUrl, speakerId ? { speaker: speakerId } : null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates and returns a new ViewSpeaker out of a speaker model
|
||||
*
|
||||
* @param speaker speaker to transform
|
||||
* @return a new ViewSpeaker
|
||||
*/
|
||||
private createViewModel(speaker: Speaker): ViewSpeaker {
|
||||
const user = this.viewModelStoreService.get(ViewUser, speaker.user_id);
|
||||
const viewSpeaker = new ViewSpeaker(speaker, user);
|
||||
viewSpeaker.getVerboseName = (plural: boolean = false) => {
|
||||
return this.translate.instant(plural ? 'Speakers' : 'Speaker');
|
||||
};
|
||||
return viewSpeaker;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate viewSpeaker objects from a given agenda Item
|
||||
*
|
||||
* @param item agenda Item holding speakers
|
||||
* @returns the list of view speakers corresponding to the given item
|
||||
*/
|
||||
public createSpeakerList(item: Item): ViewSpeaker[] {
|
||||
let viewSpeakers = [];
|
||||
const speakers = item.speakers;
|
||||
if (speakers && speakers.length > 0) {
|
||||
viewSpeakers = speakers.map(speaker => {
|
||||
return this.createViewModel(speaker);
|
||||
});
|
||||
}
|
||||
// sort speakers by their weight
|
||||
viewSpeakers = viewSpeakers.sort((a, b) => a.weight - b.weight);
|
||||
return viewSpeakers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes all speakers of the given agenda item.
|
||||
*
|
||||
* @param item the target agenda item
|
||||
*/
|
||||
public async deleteAllSpeakers(item: ViewItem): Promise<void> {
|
||||
const restUrl = this.getRestUrl(item.id, 'manage_speaker');
|
||||
await this.httpService.delete(restUrl, { speaker: item.speakers.map(speaker => speaker.id) });
|
||||
}
|
||||
|
||||
/**
|
||||
* Posts an (manually) sorted speaker list to the server
|
||||
*
|
||||
* @param speakerIds array of speaker id numbers
|
||||
* @param Item the target agenda item
|
||||
*/
|
||||
public async sortSpeakers(speakerIds: number[], item: Item): Promise<void> {
|
||||
const restUrl = this.getRestUrl(item.id, 'sort_speakers');
|
||||
await this.httpService.post(restUrl, { speakers: speakerIds });
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks the current speaker
|
||||
*
|
||||
* @param speakerId {@link User} id of the new speaker
|
||||
* @param mark determine if the user was marked or not
|
||||
* @param item the target agenda item
|
||||
*/
|
||||
public async markSpeaker(speakerId: number, mark: boolean, item: ViewItem): Promise<void> {
|
||||
const restUrl = this.getRestUrl(item.id, 'manage_speaker');
|
||||
await this.httpService.patch(restUrl, { user: speakerId, marked: mark });
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops the current speaker
|
||||
*
|
||||
* @param item the target agenda item
|
||||
*/
|
||||
public async stopCurrentSpeaker(item: ViewItem): Promise<void> {
|
||||
const restUrl = this.getRestUrl(item.id, 'speak');
|
||||
await this.httpService.delete(restUrl);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the given speaker ID to Speak
|
||||
*
|
||||
* @param speakerId the speakers id
|
||||
* @param item the target agenda item
|
||||
*/
|
||||
public async startSpeaker(speakerId: number, item: ViewItem): Promise<void> {
|
||||
const restUrl = this.getRestUrl(item.id, 'speak');
|
||||
await this.httpService.put(restUrl, { speaker: speakerId });
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function get the url to the speaker rest address
|
||||
*
|
||||
* @param itemId id of the agenda item
|
||||
* @param suffix the desired speaker action
|
||||
*/
|
||||
private getRestUrl(itemId: number, suffix: SpeakerAction): string {
|
||||
return `/rest/agenda/item/${itemId}/${suffix}/`;
|
||||
}
|
||||
}
|
@ -4,16 +4,14 @@ import { TranslateService } from '@ngx-translate/core';
|
||||
|
||||
import { Assignment } from 'app/shared/models/assignments/assignment';
|
||||
import { AssignmentRelatedUser } from 'app/shared/models/assignments/assignment-related-user';
|
||||
import { BaseAgendaContentObjectRepository } from '../base-agenda-content-object-repository';
|
||||
import { CollectionStringMapperService } from '../../core-services/collection-string-mapper.service';
|
||||
import { DataSendService } from 'app/core/core-services/data-send.service';
|
||||
import { DataStoreService } from '../../core-services/data-store.service';
|
||||
import { HttpService } from 'app/core/core-services/http.service';
|
||||
import { Item } from 'app/shared/models/agenda/item';
|
||||
import { AssignmentPoll } from 'app/shared/models/assignments/assignment-poll';
|
||||
import { Tag } from 'app/shared/models/core/tag';
|
||||
import { User } from 'app/shared/models/users/user';
|
||||
import { ViewAssignment } from 'app/site/assignments/models/view-assignment';
|
||||
import { ViewAssignment, AssignmentTitleInformation } from 'app/site/assignments/models/view-assignment';
|
||||
import { ViewItem } from 'app/site/agenda/models/view-item';
|
||||
import { ViewModelStoreService } from 'app/core/core-services/view-model-store.service';
|
||||
import { ViewTag } from 'app/site/tags/models/view-tag';
|
||||
@ -23,6 +21,8 @@ import { ViewAssignmentPoll } from 'app/site/assignments/models/view-assignment-
|
||||
import { ViewAssignmentPollOption } from 'app/site/assignments/models/view-assignment-poll-option';
|
||||
import { ViewMediafile } from 'app/site/mediafiles/models/view-mediafile';
|
||||
import { Mediafile } from 'app/shared/models/mediafiles/mediafile';
|
||||
import { ViewListOfSpeakers } from 'app/site/agenda/models/view-list-of-speakers';
|
||||
import { BaseIsAgendaItemAndListOfSpeakersContentObjectRepository } from '../base-is-agenda-item-and-list-of-speakers-content-object-repository';
|
||||
|
||||
/**
|
||||
* Repository Service for Assignments.
|
||||
@ -32,7 +32,11 @@ import { Mediafile } from 'app/shared/models/mediafiles/mediafile';
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class AssignmentRepositoryService extends BaseAgendaContentObjectRepository<ViewAssignment, Assignment> {
|
||||
export class AssignmentRepositoryService extends BaseIsAgendaItemAndListOfSpeakersContentObjectRepository<
|
||||
ViewAssignment,
|
||||
Assignment,
|
||||
AssignmentTitleInformation
|
||||
> {
|
||||
private readonly restPath = '/rest/assignments/assignment/';
|
||||
private readonly restPollPath = '/rest/assignments/poll/';
|
||||
private readonly candidatureOtherPath = '/candidature_other/';
|
||||
@ -58,15 +62,11 @@ export class AssignmentRepositoryService extends BaseAgendaContentObjectReposito
|
||||
protected translate: TranslateService,
|
||||
private httpService: HttpService
|
||||
) {
|
||||
super(DS, dataSend, mapperService, viewModelStoreService, translate, Assignment, [User, Item, Tag, Mediafile]);
|
||||
super(DS, dataSend, mapperService, viewModelStoreService, translate, Assignment, [User, Tag, Mediafile]);
|
||||
}
|
||||
|
||||
public getAgendaTitle = (assignment: Partial<Assignment> | Partial<ViewAssignment>) => {
|
||||
return assignment.title;
|
||||
};
|
||||
|
||||
public getAgendaTitleWithType = (assignment: Partial<Assignment> | Partial<ViewAssignment>) => {
|
||||
return assignment.title + ' (' + this.getVerboseName() + ')';
|
||||
public getTitle = (titleInformation: AssignmentTitleInformation) => {
|
||||
return titleInformation.title;
|
||||
};
|
||||
|
||||
public getVerboseName = (plural: boolean = false) => {
|
||||
@ -74,24 +74,22 @@ export class AssignmentRepositoryService extends BaseAgendaContentObjectReposito
|
||||
};
|
||||
|
||||
public createViewModel(assignment: Assignment): ViewAssignment {
|
||||
const agendaItem = this.viewModelStoreService.get(ViewItem, assignment.agenda_item_id);
|
||||
const item = this.viewModelStoreService.get(ViewItem, assignment.agenda_item_id);
|
||||
const listOfSpeakers = this.viewModelStoreService.get(ViewListOfSpeakers, assignment.list_of_speakers_id);
|
||||
const tags = this.viewModelStoreService.getMany(ViewTag, assignment.tags_id);
|
||||
const attachments = this.viewModelStoreService.getMany(ViewMediafile, assignment.attachments_id);
|
||||
const assignmentRelatedUsers = this.createViewAssignmentRelatedUsers(assignment.assignment_related_users);
|
||||
const assignmentPolls = this.createViewAssignmentPolls(assignment.polls);
|
||||
|
||||
const viewAssignment = new ViewAssignment(
|
||||
return new ViewAssignment(
|
||||
assignment,
|
||||
assignmentRelatedUsers,
|
||||
assignmentPolls,
|
||||
agendaItem,
|
||||
item,
|
||||
listOfSpeakers,
|
||||
tags,
|
||||
attachments
|
||||
);
|
||||
viewAssignment.getVerboseName = this.getVerboseName;
|
||||
viewAssignment.getAgendaTitle = () => this.getAgendaTitle(viewAssignment);
|
||||
viewAssignment.getAgendaTitleWithType = () => this.getAgendaTitleWithType(viewAssignment);
|
||||
return viewAssignment;
|
||||
}
|
||||
|
||||
private createViewAssignmentRelatedUsers(
|
||||
|
@ -1,44 +0,0 @@
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
|
||||
import { BaseViewModel } from '../../site/base/base-view-model';
|
||||
import { BaseModel, ModelConstructor } from '../../shared/models/base/base-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 { ViewModelStoreService } from '../core-services/view-model-store.service';
|
||||
import { BaseRepository } from './base-repository';
|
||||
|
||||
export function isBaseAgendaContentObjectRepository(obj: any): obj is BaseAgendaContentObjectRepository<any, any> {
|
||||
const repo = obj as BaseAgendaContentObjectRepository<any, any>;
|
||||
return !!obj && repo.getAgendaTitle !== undefined && repo.getAgendaTitleWithType !== undefined;
|
||||
}
|
||||
|
||||
export abstract class BaseAgendaContentObjectRepository<
|
||||
V extends BaseViewModel,
|
||||
M extends BaseModel
|
||||
> extends BaseRepository<V, M> {
|
||||
public abstract getAgendaTitle: (model: Partial<M> | Partial<V>) => string;
|
||||
public abstract getAgendaTitleWithType: (model: Partial<M> | Partial<V>) => string;
|
||||
|
||||
/**
|
||||
*/
|
||||
public constructor(
|
||||
DS: DataStoreService,
|
||||
dataSend: DataSendService,
|
||||
collectionStringMapperService: CollectionStringMapperService,
|
||||
viewModelStoreService: ViewModelStoreService,
|
||||
translate: TranslateService,
|
||||
baseModelCtor: ModelConstructor<M>,
|
||||
depsModelCtors?: ModelConstructor<BaseModel>[]
|
||||
) {
|
||||
super(
|
||||
DS,
|
||||
dataSend,
|
||||
collectionStringMapperService,
|
||||
viewModelStoreService,
|
||||
translate,
|
||||
baseModelCtor,
|
||||
depsModelCtors
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,75 @@
|
||||
import { BaseRepository } from './base-repository';
|
||||
import { BaseModelWithContentObject } from 'app/shared/models/base/base-model-with-content-object';
|
||||
import { BaseViewModelWithContentObject } from 'app/site/base/base-view-model-with-content-object';
|
||||
import { ContentObject } from 'app/shared/models/base/content-object';
|
||||
import { BaseViewModel, TitleInformation } from 'app/site/base/base-view-model';
|
||||
|
||||
/**
|
||||
* A base repository for objects that *have* content objects, e.g. items and lists of speakers.
|
||||
* Ensures that these objects must have a content objects via generics and adds a way of
|
||||
* efficient querying objects by their content objects:
|
||||
* If one wants to have the object for "motions/motion:1", call `findByContentObject` with
|
||||
* these information represented as a {@link ContentObject}.
|
||||
*/
|
||||
export abstract class BaseHasContentObjectRepository<
|
||||
V extends BaseViewModelWithContentObject<M, C> & T,
|
||||
M extends BaseModelWithContentObject,
|
||||
C extends BaseViewModel,
|
||||
T extends TitleInformation
|
||||
> extends BaseRepository<V, M, T> {
|
||||
protected contentObjectMapping: {
|
||||
[collection: string]: {
|
||||
[id: number]: V;
|
||||
};
|
||||
} = {};
|
||||
|
||||
/**
|
||||
* Returns the object with has the given content object as the content object.
|
||||
*
|
||||
* @param contentObject The content object to query.
|
||||
*/
|
||||
public findByContentObject(contentObject: ContentObject): V | null {
|
||||
if (
|
||||
this.contentObjectMapping[contentObject.collection] &&
|
||||
this.contentObjectMapping[contentObject.collection][contentObject.id]
|
||||
) {
|
||||
return this.contentObjectMapping[contentObject.collection][contentObject.id];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public changedModels(ids: number[]): void {
|
||||
ids.forEach(id => {
|
||||
const v = this.createViewModelWithTitles(this.DS.get(this.collectionString, id));
|
||||
this.viewModelStore[id] = v;
|
||||
|
||||
const contentObject = v.contentObjectData;
|
||||
if (!this.contentObjectMapping[contentObject.collection]) {
|
||||
this.contentObjectMapping[contentObject.collection] = {};
|
||||
}
|
||||
this.contentObjectMapping[contentObject.collection][contentObject.id] = v;
|
||||
this.updateViewModelObservable(id);
|
||||
});
|
||||
this.updateViewModelListObservable();
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public deleteModels(ids: number[]): void {
|
||||
ids.forEach(id => {
|
||||
const v = this.viewModelStore[id];
|
||||
if (v) {
|
||||
const contentObject = v.contentObjectData;
|
||||
if (this.contentObjectMapping[contentObject.collection]) {
|
||||
delete this.contentObjectMapping[contentObject.collection][contentObject.id];
|
||||
}
|
||||
}
|
||||
delete this.viewModelStore[id];
|
||||
this.updateViewModelObservable(id);
|
||||
});
|
||||
this.updateViewModelListObservable();
|
||||
}
|
||||
}
|
@ -0,0 +1,98 @@
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
|
||||
import { BaseModel, ModelConstructor } from '../../shared/models/base/base-model';
|
||||
import { BaseRepository } from './base-repository';
|
||||
import {
|
||||
isBaseIsAgendaItemContentObjectRepository,
|
||||
IBaseIsAgendaItemContentObjectRepository
|
||||
} from './base-is-agenda-item-content-object-repository';
|
||||
import {
|
||||
isBaseIsListOfSpeakersContentObjectRepository,
|
||||
IBaseIsListOfSpeakersContentObjectRepository
|
||||
} from './base-is-list-of-speakers-content-object-repository';
|
||||
import { DataStoreService } from '../core-services/data-store.service';
|
||||
import { DataSendService } from '../core-services/data-send.service';
|
||||
import { ViewModelStoreService } from '../core-services/view-model-store.service';
|
||||
import { Item } from 'app/shared/models/agenda/item';
|
||||
import { ListOfSpeakers } from 'app/shared/models/agenda/list-of-speakers';
|
||||
import { CollectionStringMapperService } from '../core-services/collection-string-mapper.service';
|
||||
import {
|
||||
TitleInformationWithAgendaItem,
|
||||
IBaseViewModelWithAgendaItem
|
||||
} from 'app/site/base/base-view-model-with-agenda-item';
|
||||
import { BaseProjectableViewModel } from 'app/site/base/base-projectable-view-model';
|
||||
import { IBaseViewModelWithListOfSpeakers } from 'app/site/base/base-view-model-with-list-of-speakers';
|
||||
|
||||
export function isBaseIsAgendaItemAndListOfSpeakersContentObjectRepository(
|
||||
obj: any
|
||||
): obj is BaseIsAgendaItemAndListOfSpeakersContentObjectRepository<any, any, any> {
|
||||
return (
|
||||
!!obj && isBaseIsAgendaItemContentObjectRepository(obj) && isBaseIsListOfSpeakersContentObjectRepository(obj)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* The base repository for objects with an agenda item and a list of speakers. This is some kind of
|
||||
* multi-inheritance by implementing both inherit classes again...
|
||||
*/
|
||||
export abstract class BaseIsAgendaItemAndListOfSpeakersContentObjectRepository<
|
||||
V extends BaseProjectableViewModel & IBaseViewModelWithAgendaItem & IBaseViewModelWithListOfSpeakers & T,
|
||||
M extends BaseModel,
|
||||
T extends TitleInformationWithAgendaItem
|
||||
> extends BaseRepository<V, M, T>
|
||||
implements
|
||||
IBaseIsAgendaItemContentObjectRepository<V, M, T>,
|
||||
IBaseIsListOfSpeakersContentObjectRepository<V, M, T> {
|
||||
public constructor(
|
||||
DS: DataStoreService,
|
||||
dataSend: DataSendService,
|
||||
collectionStringMapperService: CollectionStringMapperService,
|
||||
viewModelStoreService: ViewModelStoreService,
|
||||
translate: TranslateService,
|
||||
baseModelCtor: ModelConstructor<M>,
|
||||
depsModelCtors?: ModelConstructor<BaseModel>[]
|
||||
) {
|
||||
super(
|
||||
DS,
|
||||
dataSend,
|
||||
collectionStringMapperService,
|
||||
viewModelStoreService,
|
||||
translate,
|
||||
baseModelCtor,
|
||||
depsModelCtors
|
||||
);
|
||||
if (!this.depsModelCtors) {
|
||||
this.depsModelCtors = [];
|
||||
}
|
||||
this.depsModelCtors.push(Item);
|
||||
this.depsModelCtors.push(ListOfSpeakers);
|
||||
}
|
||||
|
||||
public getAgendaListTitle(titleInformation: T): string {
|
||||
// Return the agenda title with the model's verbose name appended
|
||||
const numberPrefix = titleInformation.agenda_item_number ? `${titleInformation.agenda_item_number} · ` : '';
|
||||
return numberPrefix + this.getTitle(titleInformation) + ' (' + this.getVerboseName() + ')';
|
||||
}
|
||||
|
||||
public getAgendaSlideTitle(titleInformation: T): string {
|
||||
const numberPrefix = titleInformation.agenda_item_number ? `${titleInformation.agenda_item_number} · ` : '';
|
||||
return numberPrefix + this.getTitle(titleInformation);
|
||||
}
|
||||
|
||||
public getListOfSpeakersTitle = (titleInformation: T) => {
|
||||
return this.getAgendaListTitle(titleInformation);
|
||||
};
|
||||
|
||||
public getListOfSpeakersSlideTitle = (titleInformation: T) => {
|
||||
return this.getAgendaSlideTitle(titleInformation);
|
||||
};
|
||||
|
||||
protected createViewModelWithTitles(model: M): V {
|
||||
const viewModel = super.createViewModelWithTitles(model);
|
||||
viewModel.getAgendaListTitle = () => this.getAgendaListTitle(viewModel);
|
||||
viewModel.getAgendaSlideTitle = () => this.getAgendaSlideTitle(viewModel);
|
||||
viewModel.getListOfSpeakersTitle = () => this.getListOfSpeakersTitle(viewModel);
|
||||
viewModel.getListOfSpeakersSlideTitle = () => this.getListOfSpeakersSlideTitle(viewModel);
|
||||
return viewModel;
|
||||
}
|
||||
}
|
@ -0,0 +1,92 @@
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
|
||||
import { BaseModel, ModelConstructor } from '../../shared/models/base/base-model';
|
||||
import { CollectionStringMapperService } from '../core-services/collection-string-mapper.service';
|
||||
import { DataSendService } from '../core-services/data-send.service';
|
||||
import { BaseRepository } from './base-repository';
|
||||
import { Item } from 'app/shared/models/agenda/item';
|
||||
import { DataStoreService } from '../core-services/data-store.service';
|
||||
import { ViewModelStoreService } from '../core-services/view-model-store.service';
|
||||
import {
|
||||
TitleInformationWithAgendaItem,
|
||||
BaseViewModelWithAgendaItem
|
||||
} from 'app/site/base/base-view-model-with-agenda-item';
|
||||
|
||||
export function isBaseIsAgendaItemContentObjectRepository(
|
||||
obj: any
|
||||
): obj is BaseIsAgendaItemContentObjectRepository<any, any, any> {
|
||||
const repo = obj as BaseIsAgendaItemContentObjectRepository<any, any, any>;
|
||||
return !!obj && repo.getAgendaSlideTitle !== undefined && repo.getAgendaListTitle !== undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Describes a base repository which objects do have an assigned agenda item.
|
||||
*/
|
||||
export interface IBaseIsAgendaItemContentObjectRepository<
|
||||
V extends BaseViewModelWithAgendaItem & T,
|
||||
M extends BaseModel,
|
||||
T extends TitleInformationWithAgendaItem
|
||||
> extends BaseRepository<V, M, T> {
|
||||
getAgendaListTitle: (titleInformation: T) => string;
|
||||
getAgendaSlideTitle: (titleInformation: T) => string;
|
||||
}
|
||||
|
||||
/**
|
||||
* The base repository for objects with an agenda item.
|
||||
*/
|
||||
export abstract class BaseIsAgendaItemContentObjectRepository<
|
||||
V extends BaseViewModelWithAgendaItem & T,
|
||||
M extends BaseModel,
|
||||
T extends TitleInformationWithAgendaItem
|
||||
> extends BaseRepository<V, M, T> implements IBaseIsAgendaItemContentObjectRepository<V, M, T> {
|
||||
public constructor(
|
||||
DS: DataStoreService,
|
||||
dataSend: DataSendService,
|
||||
collectionStringMapperService: CollectionStringMapperService,
|
||||
viewModelStoreService: ViewModelStoreService,
|
||||
translate: TranslateService,
|
||||
baseModelCtor: ModelConstructor<M>,
|
||||
depsModelCtors?: ModelConstructor<BaseModel>[]
|
||||
) {
|
||||
super(
|
||||
DS,
|
||||
dataSend,
|
||||
collectionStringMapperService,
|
||||
viewModelStoreService,
|
||||
translate,
|
||||
baseModelCtor,
|
||||
depsModelCtors
|
||||
);
|
||||
if (!this.depsModelCtors) {
|
||||
this.depsModelCtors = [];
|
||||
}
|
||||
this.depsModelCtors.push(Item);
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns the agenda title for the agenda item list. Should
|
||||
* be `<item number> · <title> (<type>)`. E.g. `7 · the is an election (Election)`.
|
||||
*/
|
||||
public getAgendaListTitle(titleInformation: T): string {
|
||||
// Return the agenda title with the model's verbose name appended
|
||||
const numberPrefix = titleInformation.agenda_item_number ? `${titleInformation.agenda_item_number} · ` : '';
|
||||
return numberPrefix + this.getTitle(titleInformation) + ' (' + this.getVerboseName() + ')';
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns the agenda title for the item slides
|
||||
*/
|
||||
public getAgendaSlideTitle(titleInformation: T): string {
|
||||
return this.getTitle(titleInformation);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the agenda titles to the viewmodel.
|
||||
*/
|
||||
protected createViewModelWithTitles(model: M): V {
|
||||
const viewModel = super.createViewModelWithTitles(model);
|
||||
viewModel.getAgendaListTitle = () => this.getAgendaListTitle(viewModel);
|
||||
viewModel.getAgendaSlideTitle = () => this.getAgendaSlideTitle(viewModel);
|
||||
return viewModel;
|
||||
}
|
||||
}
|
@ -0,0 +1,81 @@
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
|
||||
import { TitleInformation } from '../../site/base/base-view-model';
|
||||
import { BaseModel, ModelConstructor } from '../../shared/models/base/base-model';
|
||||
import { BaseRepository } from './base-repository';
|
||||
import { DataStoreService } from '../core-services/data-store.service';
|
||||
import { DataSendService } from '../core-services/data-send.service';
|
||||
import { CollectionStringMapperService } from '../core-services/collection-string-mapper.service';
|
||||
import { ViewModelStoreService } from '../core-services/view-model-store.service';
|
||||
import { ListOfSpeakers } from 'app/shared/models/agenda/list-of-speakers';
|
||||
import { BaseViewModelWithListOfSpeakers } from 'app/site/base/base-view-model-with-list-of-speakers';
|
||||
|
||||
export function isBaseIsListOfSpeakersContentObjectRepository(
|
||||
obj: any
|
||||
): obj is BaseIsListOfSpeakersContentObjectRepository<any, any, any> {
|
||||
const repo = obj as BaseIsListOfSpeakersContentObjectRepository<any, any, any>;
|
||||
return !!obj && repo.getListOfSpeakersTitle !== undefined && repo.getListOfSpeakersSlideTitle !== undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Describes a base repository which objects have a list of speakers assigned.
|
||||
*/
|
||||
export interface IBaseIsListOfSpeakersContentObjectRepository<
|
||||
V extends BaseViewModelWithListOfSpeakers & T,
|
||||
M extends BaseModel,
|
||||
T extends TitleInformation
|
||||
> extends BaseRepository<V, M, T> {
|
||||
getListOfSpeakersTitle: (titleInformation: T) => string;
|
||||
getListOfSpeakersSlideTitle: (titleInformation: T) => string;
|
||||
}
|
||||
|
||||
/**
|
||||
* The base repository for objects with a list of speakers.
|
||||
*/
|
||||
export abstract class BaseIsListOfSpeakersContentObjectRepository<
|
||||
V extends BaseViewModelWithListOfSpeakers & T,
|
||||
M extends BaseModel,
|
||||
T extends TitleInformation
|
||||
> extends BaseRepository<V, M, T> implements IBaseIsListOfSpeakersContentObjectRepository<V, M, T> {
|
||||
public constructor(
|
||||
DS: DataStoreService,
|
||||
dataSend: DataSendService,
|
||||
collectionStringMapperService: CollectionStringMapperService,
|
||||
viewModelStoreService: ViewModelStoreService,
|
||||
translate: TranslateService,
|
||||
baseModelCtor: ModelConstructor<M>,
|
||||
depsModelCtors?: ModelConstructor<BaseModel>[]
|
||||
) {
|
||||
super(
|
||||
DS,
|
||||
dataSend,
|
||||
collectionStringMapperService,
|
||||
viewModelStoreService,
|
||||
translate,
|
||||
baseModelCtor,
|
||||
depsModelCtors
|
||||
);
|
||||
if (!this.depsModelCtors) {
|
||||
this.depsModelCtors = [];
|
||||
}
|
||||
this.depsModelCtors.push(ListOfSpeakers);
|
||||
}
|
||||
|
||||
public getListOfSpeakersTitle(titleInformation: T): string {
|
||||
return this.getTitle(titleInformation) + ' (' + this.getVerboseName() + ')';
|
||||
}
|
||||
|
||||
public getListOfSpeakersSlideTitle(titleInformation: T): string {
|
||||
return this.getTitle(titleInformation);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the list of speakers titles to the view model
|
||||
*/
|
||||
protected createViewModelWithTitles(model: M): V {
|
||||
const viewModel = super.createViewModelWithTitles(model);
|
||||
viewModel.getListOfSpeakersTitle = () => this.getListOfSpeakersTitle(viewModel);
|
||||
viewModel.getListOfSpeakersSlideTitle = () => this.getListOfSpeakersSlideTitle(viewModel);
|
||||
return viewModel;
|
||||
}
|
||||
}
|
@ -1,19 +1,19 @@
|
||||
import { BehaviorSubject, Observable, Subject } from 'rxjs';
|
||||
import { auditTime } from 'rxjs/operators';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
|
||||
import { BaseViewModel } from '../../site/base/base-view-model';
|
||||
import { BaseViewModel, TitleInformation } from '../../site/base/base-view-model';
|
||||
import { BaseModel, ModelConstructor } from '../../shared/models/base/base-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 { Identifiable } from '../../shared/models/base/identifiable';
|
||||
import { auditTime } from 'rxjs/operators';
|
||||
import { ViewModelStoreService } from '../core-services/view-model-store.service';
|
||||
import { OnAfterAppsLoaded } from '../onAfterAppsLoaded';
|
||||
import { Collection } from 'app/shared/models/base/collection';
|
||||
import { CollectionIds } from '../core-services/data-store-update-manager.service';
|
||||
|
||||
export abstract class BaseRepository<V extends BaseViewModel, M extends BaseModel>
|
||||
export abstract class BaseRepository<V extends BaseViewModel & T, M extends BaseModel, T extends TitleInformation>
|
||||
implements OnAfterAppsLoaded, Collection {
|
||||
/**
|
||||
* Stores all the viewModel in an object
|
||||
@ -70,6 +70,7 @@ export abstract class BaseRepository<V extends BaseViewModel, M extends BaseMode
|
||||
}
|
||||
|
||||
public abstract getVerboseName: (plural?: boolean) => string;
|
||||
public abstract getTitle: (titleInformation: T) => string;
|
||||
|
||||
/**
|
||||
* Construction routine for the base repository
|
||||
@ -114,7 +115,7 @@ export abstract class BaseRepository<V extends BaseViewModel, M extends BaseMode
|
||||
protected loadInitialFromDS(): void {
|
||||
// Populate the local viewModelStore with ViewModel Objects.
|
||||
this.DS.getAll(this.baseModelCtor).forEach((model: M) => {
|
||||
this.viewModelStore[model.id] = this.createViewModel(model);
|
||||
this.viewModelStore[model.id] = this.createViewModelWithTitles(model);
|
||||
});
|
||||
|
||||
// Update the list and then all models on their own
|
||||
@ -145,7 +146,7 @@ export abstract class BaseRepository<V extends BaseViewModel, M extends BaseMode
|
||||
*/
|
||||
public changedModels(ids: number[]): void {
|
||||
ids.forEach(id => {
|
||||
this.viewModelStore[id] = this.createViewModel(this.DS.get(this.collectionString, id));
|
||||
this.viewModelStore[id] = this.createViewModelWithTitles(this.DS.get(this.collectionString, id));
|
||||
this.updateViewModelObservable(id);
|
||||
});
|
||||
this.updateViewModelListObservable();
|
||||
@ -188,6 +189,10 @@ export abstract class BaseRepository<V extends BaseViewModel, M extends BaseMode
|
||||
}
|
||||
}
|
||||
|
||||
public getListTitle: (titleInformation: T) => string = (titleInformation: T) => {
|
||||
return this.getTitle(titleInformation);
|
||||
};
|
||||
|
||||
/**
|
||||
* Saves the (full) update to an existing model. So called "update"-function
|
||||
* Provides a default procedure, but can be overwritten if required
|
||||
@ -258,6 +263,18 @@ export abstract class BaseRepository<V extends BaseViewModel, M extends BaseMode
|
||||
*/
|
||||
protected abstract createViewModel(model: M): V;
|
||||
|
||||
/**
|
||||
* After creating a view model, all functions for models form the repo
|
||||
* are assigned to the new view model.
|
||||
*/
|
||||
protected createViewModelWithTitles(model: M): V {
|
||||
const viewModel = this.createViewModel(model);
|
||||
viewModel.getTitle = () => this.getTitle(viewModel);
|
||||
viewModel.getListTitle = () => this.getListTitle(viewModel);
|
||||
viewModel.getVerboseName = this.getVerboseName;
|
||||
return viewModel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the repository.
|
||||
*/
|
||||
|
@ -12,7 +12,7 @@ import { HttpService } from 'app/core/core-services/http.service';
|
||||
import { Identifiable } from 'app/shared/models/base/identifiable';
|
||||
import { CollectionStringMapperService } from 'app/core/core-services/collection-string-mapper.service';
|
||||
import { ViewModelStoreService } from 'app/core/core-services/view-model-store.service';
|
||||
import { ViewConfig } from 'app/site/config/models/view-config';
|
||||
import { ViewConfig, ConfigTitleInformation } from 'app/site/config/models/view-config';
|
||||
|
||||
/**
|
||||
* Holds a single config item.
|
||||
@ -77,7 +77,7 @@ export interface ConfigGroup {
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class ConfigRepositoryService extends BaseRepository<ViewConfig, Config> {
|
||||
export class ConfigRepositoryService extends BaseRepository<ViewConfig, Config, ConfigTitleInformation> {
|
||||
/**
|
||||
* Own store for config groups.
|
||||
*/
|
||||
@ -130,14 +130,16 @@ export class ConfigRepositoryService extends BaseRepository<ViewConfig, Config>
|
||||
return this.translate.instant(plural ? 'Configs' : 'Config');
|
||||
};
|
||||
|
||||
public getTitle = (titleInformation: ConfigTitleInformation) => {
|
||||
return titleInformation.key;
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a new ViewConfig of a given Config object
|
||||
* @param config
|
||||
*/
|
||||
public createViewModel(config: Config): ViewConfig {
|
||||
const viewConfig = new ViewConfig(config);
|
||||
viewConfig.getVerboseName = this.getVerboseName;
|
||||
return viewConfig;
|
||||
return new ViewConfig(config);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1,16 +1,17 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
|
||||
import { CollectionStringMapperService } from 'app/core/core-services/collection-string-mapper.service';
|
||||
import { DataStoreService } from 'app/core/core-services/data-store.service';
|
||||
import { BaseRepository } from 'app/core/repositories/base-repository';
|
||||
import { History } from 'app/shared/models/core/history';
|
||||
import { Identifiable } from 'app/shared/models/base/identifiable';
|
||||
import { HttpService } from 'app/core/core-services/http.service';
|
||||
import { ViewHistory, ProxyHistory } from 'app/site/history/models/view-history';
|
||||
import { ViewHistory, ProxyHistory, HistoryTitleInformation } from 'app/site/history/models/view-history';
|
||||
import { TimeTravelService } from 'app/core/core-services/time-travel.service';
|
||||
import { ViewModelStoreService } from 'app/core/core-services/view-model-store.service';
|
||||
import { ViewUser } from 'app/site/users/models/view-user';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { DataSendService } from 'app/core/core-services/data-send.service';
|
||||
|
||||
/**
|
||||
@ -21,7 +22,7 @@ import { DataSendService } from 'app/core/core-services/data-send.service';
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class HistoryRepositoryService extends BaseRepository<ViewHistory, History> {
|
||||
export class HistoryRepositoryService extends BaseRepository<ViewHistory, History, HistoryTitleInformation> {
|
||||
/**
|
||||
* Constructs the history repository
|
||||
*
|
||||
@ -46,6 +47,10 @@ export class HistoryRepositoryService extends BaseRepository<ViewHistory, Histor
|
||||
return this.translate.instant(plural ? 'Histories' : 'History');
|
||||
};
|
||||
|
||||
public getTitle = (titleInformation: HistoryTitleInformation) => {
|
||||
return titleInformation.element_id;
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a new ViewHistory objects out of a historyObject
|
||||
*
|
||||
@ -53,9 +58,7 @@ export class HistoryRepositoryService extends BaseRepository<ViewHistory, Histor
|
||||
* @return a new ViewHistory object
|
||||
*/
|
||||
public createViewModel(history: History): ViewHistory {
|
||||
const viewHistory = new ViewHistory(this.createProxyHistory(history));
|
||||
viewHistory.getVerboseName = this.getVerboseName;
|
||||
return viewHistory;
|
||||
return new ViewHistory(this.createProxyHistory(history));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1,7 +1,9 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { HttpHeaders } from '@angular/common/http';
|
||||
|
||||
import { BaseRepository } from '../base-repository';
|
||||
import { ViewMediafile } from 'app/site/mediafiles/models/view-mediafile';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
|
||||
import { ViewMediafile, MediafileTitleInformation } from 'app/site/mediafiles/models/view-mediafile';
|
||||
import { Mediafile } from 'app/shared/models/mediafiles/mediafile';
|
||||
import { User } from 'app/shared/models/users/user';
|
||||
import { DataStoreService } from '../../core-services/data-store.service';
|
||||
@ -9,10 +11,10 @@ import { Identifiable } from 'app/shared/models/base/identifiable';
|
||||
import { CollectionStringMapperService } from '../../core-services/collection-string-mapper.service';
|
||||
import { DataSendService } from 'app/core/core-services/data-send.service';
|
||||
import { HttpService } from 'app/core/core-services/http.service';
|
||||
import { HttpHeaders } from '@angular/common/http';
|
||||
import { ViewModelStoreService } from 'app/core/core-services/view-model-store.service';
|
||||
import { ViewUser } from 'app/site/users/models/view-user';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { BaseIsListOfSpeakersContentObjectRepository } from '../base-is-list-of-speakers-content-object-repository';
|
||||
import { ViewListOfSpeakers } from 'app/site/agenda/models/view-list-of-speakers';
|
||||
|
||||
/**
|
||||
* Repository for MediaFiles
|
||||
@ -20,7 +22,11 @@ import { TranslateService } from '@ngx-translate/core';
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class MediafileRepositoryService extends BaseRepository<ViewMediafile, Mediafile> {
|
||||
export class MediafileRepositoryService extends BaseIsListOfSpeakersContentObjectRepository<
|
||||
ViewMediafile,
|
||||
Mediafile,
|
||||
MediafileTitleInformation
|
||||
> {
|
||||
/**
|
||||
* Constructor for the mediafile repository
|
||||
* @param DS Data store
|
||||
@ -40,6 +46,10 @@ export class MediafileRepositoryService extends BaseRepository<ViewMediafile, Me
|
||||
this.initSorting();
|
||||
}
|
||||
|
||||
public getTitle = (titleInformation: MediafileTitleInformation) => {
|
||||
return titleInformation.title;
|
||||
};
|
||||
|
||||
public getVerboseName = (plural: boolean = false) => {
|
||||
return this.translate.instant(plural ? 'Files' : 'File');
|
||||
};
|
||||
@ -51,10 +61,9 @@ export class MediafileRepositoryService extends BaseRepository<ViewMediafile, Me
|
||||
* @returns a new mediafile ViewModel
|
||||
*/
|
||||
public createViewModel(file: Mediafile): ViewMediafile {
|
||||
const listOfSpeakers = this.viewModelStoreService.get(ViewListOfSpeakers, file.list_of_speakers_id);
|
||||
const uploader = this.viewModelStoreService.get(ViewUser, file.uploader_id);
|
||||
const viewMediafile = new ViewMediafile(file, uploader);
|
||||
viewMediafile.getVerboseName = this.getVerboseName;
|
||||
return viewMediafile;
|
||||
return new ViewMediafile(file, listOfSpeakers, uploader);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -9,7 +9,7 @@ import { ConfigService } from 'app/core/ui-services/config.service';
|
||||
import { DataSendService } from '../../core-services/data-send.service';
|
||||
import { DataStoreService } from '../../core-services/data-store.service';
|
||||
import { HttpService } from '../../core-services/http.service';
|
||||
import { ViewCategory } from 'app/site/motions/models/view-category';
|
||||
import { ViewCategory, CategoryTitleInformation } from 'app/site/motions/models/view-category';
|
||||
import { ViewModelStoreService } from 'app/core/core-services/view-model-store.service';
|
||||
|
||||
type SortProperty = 'prefix' | 'name';
|
||||
@ -27,7 +27,7 @@ type SortProperty = 'prefix' | 'name';
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class CategoryRepositoryService extends BaseRepository<ViewCategory, Category> {
|
||||
export class CategoryRepositoryService extends BaseRepository<ViewCategory, Category, CategoryTitleInformation> {
|
||||
private sortProperty: SortProperty;
|
||||
|
||||
/**
|
||||
@ -60,14 +60,18 @@ export class CategoryRepositoryService extends BaseRepository<ViewCategory, Cate
|
||||
});
|
||||
}
|
||||
|
||||
public getTitle = (titleInformation: CategoryTitleInformation) => {
|
||||
return titleInformation.prefix
|
||||
? titleInformation.prefix + ' - ' + titleInformation.name
|
||||
: titleInformation.name;
|
||||
};
|
||||
|
||||
public getVerboseName = (plural: boolean = false) => {
|
||||
return this.translate.instant(plural ? 'Categories' : 'Category');
|
||||
};
|
||||
|
||||
protected createViewModel(category: Category): ViewCategory {
|
||||
const viewCategory = new ViewCategory(category);
|
||||
viewCategory.getVerboseName = this.getVerboseName;
|
||||
return viewCategory;
|
||||
return new ViewCategory(category);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -11,7 +11,10 @@ import { Workflow } from 'app/shared/models/motions/workflow';
|
||||
import { BaseRepository } from '../base-repository';
|
||||
import { DataStoreService } from 'app/core/core-services/data-store.service';
|
||||
import { MotionChangeRecommendation } from 'app/shared/models/motions/motion-change-reco';
|
||||
import { ViewMotionChangeRecommendation } from 'app/site/motions/models/view-change-recommendation';
|
||||
import {
|
||||
ViewMotionChangeRecommendation,
|
||||
MotionChangeRecommendationTitleInformation
|
||||
} from 'app/site/motions/models/view-motion-change-recommendation';
|
||||
import { Identifiable } from 'app/shared/models/base/identifiable';
|
||||
import { CollectionStringMapperService } from 'app/core/core-services/collection-string-mapper.service';
|
||||
import { ViewModelStoreService } from 'app/core/core-services/view-model-store.service';
|
||||
@ -31,7 +34,8 @@ import { ViewModelStoreService } from 'app/core/core-services/view-model-store.s
|
||||
})
|
||||
export class ChangeRecommendationRepositoryService extends BaseRepository<
|
||||
ViewMotionChangeRecommendation,
|
||||
MotionChangeRecommendation
|
||||
MotionChangeRecommendation,
|
||||
MotionChangeRecommendationTitleInformation
|
||||
> {
|
||||
/**
|
||||
* Creates a MotionRepository
|
||||
@ -57,6 +61,10 @@ export class ChangeRecommendationRepositoryService extends BaseRepository<
|
||||
]);
|
||||
}
|
||||
|
||||
public getTitle = (titleInformation: MotionChangeRecommendationTitleInformation) => {
|
||||
return this.getVerboseName();
|
||||
};
|
||||
|
||||
public getVerboseName = (plural: boolean = false) => {
|
||||
return this.translate.instant(plural ? 'Change recommendations' : 'Change recommendation');
|
||||
};
|
||||
@ -67,9 +75,7 @@ export class ChangeRecommendationRepositoryService extends BaseRepository<
|
||||
* @param {MotionChangeRecommendation} model
|
||||
*/
|
||||
protected createViewModel(model: MotionChangeRecommendation): ViewMotionChangeRecommendation {
|
||||
const viewMotionChangeRecommendation = new ViewMotionChangeRecommendation(model);
|
||||
viewMotionChangeRecommendation.getVerboseName = this.getVerboseName;
|
||||
return viewMotionChangeRecommendation;
|
||||
return new ViewMotionChangeRecommendation(model);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -12,11 +12,11 @@ import { Motion } from 'app/shared/models/motions/motion';
|
||||
import { MotionBlock } from 'app/shared/models/motions/motion-block';
|
||||
import { MotionRepositoryService } from './motion-repository.service';
|
||||
import { ViewMotion } from 'app/site/motions/models/view-motion';
|
||||
import { ViewMotionBlock } from 'app/site/motions/models/view-motion-block';
|
||||
import { ViewMotionBlock, MotionBlockTitleInformation } from 'app/site/motions/models/view-motion-block';
|
||||
import { ViewModelStoreService } from 'app/core/core-services/view-model-store.service';
|
||||
import { Item } from 'app/shared/models/agenda/item';
|
||||
import { ViewItem } from 'app/site/agenda/models/view-item';
|
||||
import { BaseAgendaContentObjectRepository } from '../base-agenda-content-object-repository';
|
||||
import { ViewListOfSpeakers } from 'app/site/agenda/models/view-list-of-speakers';
|
||||
import { BaseIsAgendaItemAndListOfSpeakersContentObjectRepository } from '../base-is-agenda-item-and-list-of-speakers-content-object-repository';
|
||||
|
||||
/**
|
||||
* Repository service for motion blocks
|
||||
@ -24,7 +24,11 @@ import { BaseAgendaContentObjectRepository } from '../base-agenda-content-object
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class MotionBlockRepositoryService extends BaseAgendaContentObjectRepository<ViewMotionBlock, MotionBlock> {
|
||||
export class MotionBlockRepositoryService extends BaseIsAgendaItemAndListOfSpeakersContentObjectRepository<
|
||||
ViewMotionBlock,
|
||||
MotionBlock,
|
||||
MotionBlockTitleInformation
|
||||
> {
|
||||
/**
|
||||
* Constructor for the motion block repository
|
||||
*
|
||||
@ -43,16 +47,12 @@ export class MotionBlockRepositoryService extends BaseAgendaContentObjectReposit
|
||||
private motionRepo: MotionRepositoryService,
|
||||
private httpService: HttpService
|
||||
) {
|
||||
super(DS, dataSend, mapperService, viewModelStoreService, translate, MotionBlock, [Item]);
|
||||
super(DS, dataSend, mapperService, viewModelStoreService, translate, MotionBlock);
|
||||
this.initSorting();
|
||||
}
|
||||
|
||||
public getAgendaTitle = (motionBlock: Partial<MotionBlock> | Partial<ViewMotionBlock>) => {
|
||||
return motionBlock.title;
|
||||
};
|
||||
|
||||
public getAgendaTitleWithType = (motionBlock: Partial<MotionBlock> | Partial<ViewMotionBlock>) => {
|
||||
return motionBlock.title + ' (' + this.getVerboseName() + ')';
|
||||
public getTitle = (titleInformation: MotionBlockTitleInformation) => {
|
||||
return titleInformation.title;
|
||||
};
|
||||
|
||||
public getVerboseName = (plural: boolean = false) => {
|
||||
@ -67,11 +67,8 @@ export class MotionBlockRepositoryService extends BaseAgendaContentObjectReposit
|
||||
*/
|
||||
protected createViewModel(block: MotionBlock): ViewMotionBlock {
|
||||
const item = this.viewModelStoreService.get(ViewItem, block.agenda_item_id);
|
||||
const viewMotionBlock = new ViewMotionBlock(block, item);
|
||||
viewMotionBlock.getVerboseName = this.getVerboseName;
|
||||
viewMotionBlock.getAgendaTitle = () => this.getAgendaTitle(viewMotionBlock);
|
||||
viewMotionBlock.getAgendaTitleWithType = () => this.getAgendaTitleWithType(viewMotionBlock);
|
||||
return viewMotionBlock;
|
||||
const listOfSpeakers = this.viewModelStoreService.get(ViewListOfSpeakers, block.list_of_speakers_id);
|
||||
return new ViewMotionBlock(block, item, listOfSpeakers);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1,16 +1,20 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
|
||||
import { DataSendService } from '../../core-services/data-send.service';
|
||||
import { DataStoreService } from '../../core-services/data-store.service';
|
||||
import { BaseRepository } from '../base-repository';
|
||||
import { ViewMotionCommentSection } from 'app/site/motions/models/view-motion-comment-section';
|
||||
import {
|
||||
ViewMotionCommentSection,
|
||||
MotionCommentSectionTitleInformation
|
||||
} from 'app/site/motions/models/view-motion-comment-section';
|
||||
import { MotionCommentSection } from 'app/shared/models/motions/motion-comment-section';
|
||||
import { Group } from 'app/shared/models/users/group';
|
||||
import { CollectionStringMapperService } from '../../core-services/collection-string-mapper.service';
|
||||
import { HttpService } from 'app/core/core-services/http.service';
|
||||
import { ViewModelStoreService } from 'app/core/core-services/view-model-store.service';
|
||||
import { ViewGroup } from 'app/site/users/models/view-group';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { ViewMotion } from 'app/site/motions/models/view-motion';
|
||||
|
||||
/**
|
||||
@ -28,7 +32,8 @@ import { ViewMotion } from 'app/site/motions/models/view-motion';
|
||||
})
|
||||
export class MotionCommentSectionRepositoryService extends BaseRepository<
|
||||
ViewMotionCommentSection,
|
||||
MotionCommentSection
|
||||
MotionCommentSection,
|
||||
MotionCommentSectionTitleInformation
|
||||
> {
|
||||
/**
|
||||
* Creates a CategoryRepository
|
||||
@ -51,6 +56,10 @@ export class MotionCommentSectionRepositoryService extends BaseRepository<
|
||||
super(DS, dataSend, mapperService, viewModelStoreService, translate, MotionCommentSection, [Group]);
|
||||
}
|
||||
|
||||
public getTitle = (titleInformation: MotionCommentSectionTitleInformation) => {
|
||||
return titleInformation.name;
|
||||
};
|
||||
|
||||
public getVerboseName = (plural: boolean = false) => {
|
||||
return this.translate.instant(plural ? 'Comment sections' : 'Comment section');
|
||||
};
|
||||
@ -64,9 +73,7 @@ export class MotionCommentSectionRepositoryService extends BaseRepository<
|
||||
protected createViewModel(section: MotionCommentSection): ViewMotionCommentSection {
|
||||
const readGroups = this.viewModelStoreService.getMany(ViewGroup, section.read_groups_id);
|
||||
const writeGroups = this.viewModelStoreService.getMany(ViewGroup, section.write_groups_id);
|
||||
const viewMotionCommentSection = new ViewMotionCommentSection(section, readGroups, writeGroups);
|
||||
viewMotionCommentSection.getVerboseName = this.getVerboseName;
|
||||
return viewMotionCommentSection;
|
||||
return new ViewMotionCommentSection(section, readGroups, writeGroups);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -6,14 +6,13 @@ import { Observable } from 'rxjs';
|
||||
import { map } from 'rxjs/operators';
|
||||
|
||||
import { Category } from 'app/shared/models/motions/category';
|
||||
import { ChangeRecoMode, ViewMotion } from 'app/site/motions/models/view-motion';
|
||||
import { ChangeRecoMode, ViewMotion, MotionTitleInformation } from 'app/site/motions/models/view-motion';
|
||||
import { CollectionStringMapperService } from '../../core-services/collection-string-mapper.service';
|
||||
import { ConfigService } from 'app/core/ui-services/config.service';
|
||||
import { DataSendService } from '../../core-services/data-send.service';
|
||||
import { DataStoreService } from '../../core-services/data-store.service';
|
||||
import { DiffLinesInParagraph, DiffService, LineRange, ModificationType } from '../../ui-services/diff.service';
|
||||
import { HttpService } from 'app/core/core-services/http.service';
|
||||
import { Item } from 'app/shared/models/agenda/item';
|
||||
import { LinenumberingService, LineNumberRange } from '../../ui-services/linenumbering.service';
|
||||
import { Mediafile } from 'app/shared/models/mediafiles/mediafile';
|
||||
import { Motion } from 'app/shared/models/motions/motion';
|
||||
@ -22,7 +21,7 @@ import { MotionChangeRecommendation } from 'app/shared/models/motions/motion-cha
|
||||
import { MotionPoll } from 'app/shared/models/motions/motion-poll';
|
||||
import { TreeIdNode } from 'app/core/ui-services/tree.service';
|
||||
import { User } from 'app/shared/models/users/user';
|
||||
import { ViewMotionChangeRecommendation } from 'app/site/motions/models/view-change-recommendation';
|
||||
import { ViewMotionChangeRecommendation } from 'app/site/motions/models/view-motion-change-recommendation';
|
||||
import { ViewMotionAmendedParagraph } from 'app/site/motions/models/view-motion-amended-paragraph';
|
||||
import { ViewUnifiedChange } from 'app/shared/models/motions/view-unified-change';
|
||||
import { ViewStatuteParagraph } from 'app/site/motions/models/view-statute-paragraph';
|
||||
@ -37,11 +36,12 @@ import { ViewItem } from 'app/site/agenda/models/view-item';
|
||||
import { ViewMotionBlock } from 'app/site/motions/models/view-motion-block';
|
||||
import { ViewMediafile } from 'app/site/mediafiles/models/view-mediafile';
|
||||
import { ViewTag } from 'app/site/tags/models/view-tag';
|
||||
import { BaseAgendaContentObjectRepository } from '../base-agenda-content-object-repository';
|
||||
import { PersonalNote, PersonalNoteContent } from 'app/shared/models/users/personal-note';
|
||||
import { ViewPersonalNote } from 'app/site/users/models/view-personal-note';
|
||||
import { OperatorService } from 'app/core/core-services/operator.service';
|
||||
import { CollectionIds } from 'app/core/core-services/data-store-update-manager.service';
|
||||
import { PersonalNote, PersonalNoteContent } from 'app/shared/models/users/personal-note';
|
||||
import { ViewListOfSpeakers } from 'app/site/agenda/models/view-list-of-speakers';
|
||||
import { BaseIsAgendaItemAndListOfSpeakersContentObjectRepository } from '../base-is-agenda-item-and-list-of-speakers-content-object-repository';
|
||||
|
||||
type SortProperty = 'weight' | 'identifier';
|
||||
|
||||
@ -88,7 +88,11 @@ export interface ParagraphToChoose {
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class MotionRepositoryService extends BaseAgendaContentObjectRepository<ViewMotion, Motion> {
|
||||
export class MotionRepositoryService extends BaseIsAgendaItemAndListOfSpeakersContentObjectRepository<
|
||||
ViewMotion,
|
||||
Motion,
|
||||
MotionTitleInformation
|
||||
> {
|
||||
/**
|
||||
* The property the incoming data is sorted by
|
||||
*/
|
||||
@ -127,7 +131,6 @@ export class MotionRepositoryService extends BaseAgendaContentObjectRepository<V
|
||||
Category,
|
||||
User,
|
||||
Workflow,
|
||||
Item,
|
||||
MotionBlock,
|
||||
Mediafile,
|
||||
Tag,
|
||||
@ -141,37 +144,39 @@ export class MotionRepositoryService extends BaseAgendaContentObjectRepository<V
|
||||
});
|
||||
}
|
||||
|
||||
public getTitle = (motion: Partial<Motion> | Partial<ViewMotion>) => {
|
||||
if (motion.identifier) {
|
||||
return motion.identifier + ': ' + motion.title;
|
||||
public getTitle = (titleInformation: MotionTitleInformation) => {
|
||||
if (titleInformation.identifier) {
|
||||
return titleInformation.identifier + ': ' + titleInformation.title;
|
||||
} else {
|
||||
return motion.title;
|
||||
return titleInformation.title;
|
||||
}
|
||||
};
|
||||
|
||||
public getIdentifierOrTitle = (motion: Partial<Motion> | Partial<ViewMotion>) => {
|
||||
if (motion.identifier) {
|
||||
return motion.identifier;
|
||||
public getIdentifierOrTitle = (titleInformation: MotionTitleInformation) => {
|
||||
if (titleInformation.identifier) {
|
||||
return titleInformation.identifier;
|
||||
} else {
|
||||
return motion.title;
|
||||
return titleInformation.title;
|
||||
}
|
||||
};
|
||||
|
||||
public getAgendaTitle = (motion: Partial<Motion> | Partial<ViewMotion>) => {
|
||||
public getAgendaSlideTitle = (titleInformation: MotionTitleInformation) => {
|
||||
const numberPrefix = titleInformation.agenda_item_number ? `${titleInformation.agenda_item_number} · ` : '';
|
||||
// if the identifier is set, the title will be 'Motion <identifier>'.
|
||||
if (motion.identifier) {
|
||||
return this.translate.instant('Motion') + ' ' + motion.identifier;
|
||||
if (titleInformation.identifier) {
|
||||
return numberPrefix + this.translate.instant('Motion') + ' ' + titleInformation.identifier;
|
||||
} else {
|
||||
return motion.title;
|
||||
return numberPrefix + titleInformation.title;
|
||||
}
|
||||
};
|
||||
|
||||
public getAgendaTitleWithType = (motion: Partial<Motion> | Partial<ViewMotion>) => {
|
||||
public getAgendaListTitle = (titleInformation: MotionTitleInformation) => {
|
||||
const numberPrefix = titleInformation.agenda_item_number ? `${titleInformation.agenda_item_number} · ` : '';
|
||||
// Append the verbose name only, if not the special format 'Motion <identifier>' is used.
|
||||
if (motion.identifier) {
|
||||
return this.translate.instant('Motion') + ' ' + motion.identifier;
|
||||
if (titleInformation.identifier) {
|
||||
return numberPrefix + this.translate.instant('Motion') + ' ' + titleInformation.identifier;
|
||||
} else {
|
||||
return motion.title + ' (' + this.getVerboseName() + ')';
|
||||
return numberPrefix + titleInformation.title + ' (' + this.getVerboseName() + ')';
|
||||
}
|
||||
};
|
||||
|
||||
@ -193,6 +198,7 @@ export class MotionRepositoryService extends BaseAgendaContentObjectRepository<V
|
||||
const supporters = this.viewModelStoreService.getMany(ViewUser, motion.supporters_id);
|
||||
const workflow = this.viewModelStoreService.get(ViewWorkflow, motion.workflow_id);
|
||||
const item = this.viewModelStoreService.get(ViewItem, motion.agenda_item_id);
|
||||
const listOfSpeakers = this.viewModelStoreService.get(ViewListOfSpeakers, motion.list_of_speakers_id);
|
||||
const block = this.viewModelStoreService.get(ViewMotionBlock, motion.motion_block_id);
|
||||
const attachments = this.viewModelStoreService.getMany(ViewMediafile, motion.attachments_id);
|
||||
const tags = this.viewModelStoreService.getMany(ViewTag, motion.tags_id);
|
||||
@ -215,6 +221,7 @@ export class MotionRepositoryService extends BaseAgendaContentObjectRepository<V
|
||||
workflow,
|
||||
state,
|
||||
item,
|
||||
listOfSpeakers,
|
||||
block,
|
||||
attachments,
|
||||
tags,
|
||||
@ -224,11 +231,7 @@ export class MotionRepositoryService extends BaseAgendaContentObjectRepository<V
|
||||
personalNote
|
||||
);
|
||||
viewMotion.getIdentifierOrTitle = () => this.getIdentifierOrTitle(viewMotion);
|
||||
viewMotion.getTitle = () => this.getTitle(viewMotion);
|
||||
viewMotion.getVerboseName = this.getVerboseName;
|
||||
viewMotion.getAgendaTitle = () => this.getAgendaTitle(viewMotion);
|
||||
viewMotion.getProjectorTitle = viewMotion.getAgendaTitle;
|
||||
viewMotion.getAgendaTitleWithType = () => this.getAgendaTitleWithType(viewMotion);
|
||||
viewMotion.getProjectorTitle = () => this.getAgendaSlideTitle(viewMotion);
|
||||
return viewMotion;
|
||||
}
|
||||
|
||||
|
@ -1,13 +1,14 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
|
||||
import { DataSendService } from '../../core-services/data-send.service';
|
||||
import { DataStoreService } from '../../core-services/data-store.service';
|
||||
import { BaseRepository } from '../base-repository';
|
||||
import { ViewStatuteParagraph } from 'app/site/motions/models/view-statute-paragraph';
|
||||
import { ViewStatuteParagraph, StatuteParagraphTitleInformation } from 'app/site/motions/models/view-statute-paragraph';
|
||||
import { StatuteParagraph } from 'app/shared/models/motions/statute-paragraph';
|
||||
import { CollectionStringMapperService } from '../../core-services/collection-string-mapper.service';
|
||||
import { ViewModelStoreService } from 'app/core/core-services/view-model-store.service';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
|
||||
/**
|
||||
* Repository Services for statute paragraphs
|
||||
@ -19,7 +20,11 @@ import { TranslateService } from '@ngx-translate/core';
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class StatuteParagraphRepositoryService extends BaseRepository<ViewStatuteParagraph, StatuteParagraph> {
|
||||
export class StatuteParagraphRepositoryService extends BaseRepository<
|
||||
ViewStatuteParagraph,
|
||||
StatuteParagraph,
|
||||
StatuteParagraphTitleInformation
|
||||
> {
|
||||
/**
|
||||
* Creates a StatuteParagraphRepository
|
||||
* Converts existing and incoming statute paragraphs to ViewStatuteParagraphs
|
||||
@ -39,13 +44,15 @@ export class StatuteParagraphRepositoryService extends BaseRepository<ViewStatut
|
||||
super(DS, dataSend, mapperService, viewModelStoreService, translate, StatuteParagraph);
|
||||
}
|
||||
|
||||
public getTitle = (titleInformation: StatuteParagraphTitleInformation) => {
|
||||
return titleInformation.title;
|
||||
};
|
||||
|
||||
public getVerboseName = (plural: boolean = false) => {
|
||||
return this.translate.instant(plural ? 'Statute paragraphs' : 'Statute paragraph');
|
||||
};
|
||||
|
||||
protected createViewModel(statuteParagraph: StatuteParagraph): ViewStatuteParagraph {
|
||||
const viewStatuteParagraph = new ViewStatuteParagraph(statuteParagraph);
|
||||
viewStatuteParagraph.getVerboseName = this.getVerboseName;
|
||||
return viewStatuteParagraph;
|
||||
return new ViewStatuteParagraph(statuteParagraph);
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,9 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
|
||||
import { Workflow } from 'app/shared/models/motions/workflow';
|
||||
import { ViewWorkflow } from 'app/site/motions/models/view-workflow';
|
||||
import { ViewWorkflow, WorkflowTitleInformation } from 'app/site/motions/models/view-workflow';
|
||||
import { DataSendService } from '../../core-services/data-send.service';
|
||||
import { DataStoreService } from '../../core-services/data-store.service';
|
||||
import { BaseRepository } from '../base-repository';
|
||||
@ -10,7 +12,6 @@ import { WorkflowState } from 'app/shared/models/motions/workflow-state';
|
||||
import { ViewMotion } from 'app/site/motions/models/view-motion';
|
||||
import { HttpService } from 'app/core/core-services/http.service';
|
||||
import { ViewModelStoreService } from 'app/core/core-services/view-model-store.service';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
|
||||
/**
|
||||
* Repository Services for Categories
|
||||
@ -25,7 +26,7 @@ import { TranslateService } from '@ngx-translate/core';
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class WorkflowRepositoryService extends BaseRepository<ViewWorkflow, Workflow> {
|
||||
export class WorkflowRepositoryService extends BaseRepository<ViewWorkflow, Workflow, WorkflowTitleInformation> {
|
||||
/**
|
||||
* The url to state on rest
|
||||
*/
|
||||
@ -57,6 +58,10 @@ export class WorkflowRepositoryService extends BaseRepository<ViewWorkflow, Work
|
||||
});
|
||||
}
|
||||
|
||||
public getTitle = (titleInformation: WorkflowTitleInformation) => {
|
||||
return titleInformation.name;
|
||||
};
|
||||
|
||||
public getVerboseName = (plural: boolean = false) => {
|
||||
return this.translate.instant(plural ? 'Workflows' : 'Workflow');
|
||||
};
|
||||
@ -81,9 +86,7 @@ export class WorkflowRepositoryService extends BaseRepository<ViewWorkflow, Work
|
||||
* @param workflow the Workflow to convert
|
||||
*/
|
||||
protected createViewModel(workflow: Workflow): ViewWorkflow {
|
||||
const viewWorkflow = new ViewWorkflow(workflow);
|
||||
viewWorkflow.getVerboseName = this.getVerboseName;
|
||||
return viewWorkflow;
|
||||
return new ViewWorkflow(workflow);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -6,7 +6,7 @@ import { DataSendService } from '../../core-services/data-send.service';
|
||||
import { DataStoreService } from '../../core-services/data-store.service';
|
||||
import { BaseRepository } from '../base-repository';
|
||||
import { CollectionStringMapperService } from '../../core-services/collection-string-mapper.service';
|
||||
import { ViewCountdown } from 'app/site/projector/models/view-countdown';
|
||||
import { ViewCountdown, CountdownTitleInformation } from 'app/site/projector/models/view-countdown';
|
||||
import { Countdown } from 'app/shared/models/core/countdown';
|
||||
import { ViewModelStoreService } from 'app/core/core-services/view-model-store.service';
|
||||
import { ServertimeService } from 'app/core/core-services/servertime.service';
|
||||
@ -14,7 +14,7 @@ import { ServertimeService } from 'app/core/core-services/servertime.service';
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class CountdownRepositoryService extends BaseRepository<ViewCountdown, Countdown> {
|
||||
export class CountdownRepositoryService extends BaseRepository<ViewCountdown, Countdown, CountdownTitleInformation> {
|
||||
public constructor(
|
||||
DS: DataStoreService,
|
||||
dataSend: DataSendService,
|
||||
@ -26,14 +26,18 @@ export class CountdownRepositoryService extends BaseRepository<ViewCountdown, Co
|
||||
super(DS, dataSend, mapperService, viewModelStoreService, translate, Countdown);
|
||||
}
|
||||
|
||||
public getTitle = (titleInformation: CountdownTitleInformation) => {
|
||||
return titleInformation.description
|
||||
? `${titleInformation.title} (${titleInformation.description})`
|
||||
: titleInformation.title;
|
||||
};
|
||||
|
||||
public getVerboseName = (plural: boolean = false) => {
|
||||
return this.translate.instant(plural ? 'Countdowns' : 'Countdown');
|
||||
};
|
||||
|
||||
protected createViewModel(countdown: Countdown): ViewCountdown {
|
||||
const viewCountdown = new ViewCountdown(countdown);
|
||||
viewCountdown.getVerboseName = this.getVerboseName;
|
||||
return viewCountdown;
|
||||
return new ViewCountdown(countdown);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -9,7 +9,10 @@ import { DataStoreService } from '../../core-services/data-store.service';
|
||||
import { Identifiable } from 'app/shared/models/base/identifiable';
|
||||
import { ViewModelStoreService } from 'app/core/core-services/view-model-store.service';
|
||||
import { ProjectionDefault } from 'app/shared/models/core/projection-default';
|
||||
import { ViewProjectionDefault } from 'app/site/projector/models/view-projection-default';
|
||||
import {
|
||||
ViewProjectionDefault,
|
||||
ProjectionDefaultTitleInformation
|
||||
} from 'app/site/projector/models/view-projection-default';
|
||||
|
||||
/**
|
||||
* Manages all projection default instances.
|
||||
@ -17,7 +20,11 @@ import { ViewProjectionDefault } from 'app/site/projector/models/view-projection
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class ProjectionDefaultRepositoryService extends BaseRepository<ViewProjectionDefault, ProjectionDefault> {
|
||||
export class ProjectionDefaultRepositoryService extends BaseRepository<
|
||||
ViewProjectionDefault,
|
||||
ProjectionDefault,
|
||||
ProjectionDefaultTitleInformation
|
||||
> {
|
||||
/**
|
||||
* Constructor calls the parent constructor
|
||||
*
|
||||
@ -41,15 +48,12 @@ export class ProjectionDefaultRepositoryService extends BaseRepository<ViewProje
|
||||
return this.translate.instant(plural ? 'Projectiondefaults' : 'Projectiondefault');
|
||||
};
|
||||
|
||||
public getTitle = (projectionDefault: Partial<ProjectionDefault> | Partial<ViewProjectionDefault>) => {
|
||||
return this.translate.instant(projectionDefault.display_name);
|
||||
public getTitle = (titleInformation: ProjectionDefaultTitleInformation) => {
|
||||
return this.translate.instant(titleInformation.display_name);
|
||||
};
|
||||
|
||||
public createViewModel(projectionDefault: ProjectionDefault): ViewProjectionDefault {
|
||||
const viewProjectionDefault = new ViewProjectionDefault(projectionDefault);
|
||||
viewProjectionDefault.getVerboseName = this.getVerboseName;
|
||||
viewProjectionDefault.getTitle = () => this.getTitle(viewProjectionDefault);
|
||||
return viewProjectionDefault;
|
||||
return new ViewProjectionDefault(projectionDefault);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1,17 +1,26 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
|
||||
import { DataStoreService } from '../../core-services/data-store.service';
|
||||
import { BaseRepository } from '../base-repository';
|
||||
import { CollectionStringMapperService } from '../../core-services/collection-string-mapper.service';
|
||||
import { ProjectorMessage } from 'app/shared/models/core/projector-message';
|
||||
import { ViewProjectorMessage } from 'app/site/projector/models/view-projector-message';
|
||||
import {
|
||||
ViewProjectorMessage,
|
||||
ProjectorMessageTitleInformation
|
||||
} from 'app/site/projector/models/view-projector-message';
|
||||
import { ViewModelStoreService } from 'app/core/core-services/view-model-store.service';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { DataSendService } from 'app/core/core-services/data-send.service';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class ProjectorMessageRepositoryService extends BaseRepository<ViewProjectorMessage, ProjectorMessage> {
|
||||
export class ProjectorMessageRepositoryService extends BaseRepository<
|
||||
ViewProjectorMessage,
|
||||
ProjectorMessage,
|
||||
ProjectorMessageTitleInformation
|
||||
> {
|
||||
public constructor(
|
||||
DS: DataStoreService,
|
||||
dataSend: DataSendService,
|
||||
@ -22,13 +31,15 @@ export class ProjectorMessageRepositoryService extends BaseRepository<ViewProjec
|
||||
super(DS, dataSend, mapperService, viewModelStoreService, translate, ProjectorMessage);
|
||||
}
|
||||
|
||||
public getTitle = (titleInformation: ProjectorMessageTitleInformation) => {
|
||||
return this.getVerboseName();
|
||||
};
|
||||
|
||||
public getVerboseName = (plural: boolean = false) => {
|
||||
return this.translate.instant(plural ? 'Messages' : 'Message');
|
||||
};
|
||||
|
||||
protected createViewModel(message: ProjectorMessage): ViewProjectorMessage {
|
||||
const viewProjectorMessage = new ViewProjectorMessage(message);
|
||||
viewProjectorMessage.getVerboseName = this.getVerboseName;
|
||||
return viewProjectorMessage;
|
||||
return new ViewProjectorMessage(message);
|
||||
}
|
||||
}
|
||||
|
@ -1,15 +1,16 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
|
||||
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 { Identifiable } from 'app/shared/models/base/identifiable';
|
||||
import { ViewProjector } from 'app/site/projector/models/view-projector';
|
||||
import { ViewProjector, ProjectorTitleInformation } from 'app/site/projector/models/view-projector';
|
||||
import { Projector } from 'app/shared/models/core/projector';
|
||||
import { HttpService } from 'app/core/core-services/http.service';
|
||||
import { ViewModelStoreService } from 'app/core/core-services/view-model-store.service';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
|
||||
/**
|
||||
* Directions for scale and scroll requests.
|
||||
@ -26,7 +27,7 @@ export enum ScrollScaleDirection {
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class ProjectorRepositoryService extends BaseRepository<ViewProjector, Projector> {
|
||||
export class ProjectorRepositoryService extends BaseRepository<ViewProjector, Projector, ProjectorTitleInformation> {
|
||||
/**
|
||||
* Constructor calls the parent constructor
|
||||
*
|
||||
@ -46,14 +47,16 @@ export class ProjectorRepositoryService extends BaseRepository<ViewProjector, Pr
|
||||
super(DS, dataSend, mapperService, viewModelStoreService, translate, Projector, [Projector]);
|
||||
}
|
||||
|
||||
public getTitle = (titleInformation: ProjectorTitleInformation) => {
|
||||
return titleInformation.name;
|
||||
};
|
||||
|
||||
public getVerboseName = (plural: boolean = false) => {
|
||||
return this.translate.instant(plural ? 'Projectors' : 'Projector');
|
||||
};
|
||||
|
||||
public createViewModel(projector: Projector): ViewProjector {
|
||||
const viewProjector = new ViewProjector(projector);
|
||||
viewProjector.getVerboseName = this.getVerboseName;
|
||||
return viewProjector;
|
||||
return new ViewProjector(projector);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1,13 +1,14 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
|
||||
import { Tag } from 'app/shared/models/core/tag';
|
||||
import { ViewTag } from 'app/site/tags/models/view-tag';
|
||||
import { ViewTag, TagTitleInformation } from 'app/site/tags/models/view-tag';
|
||||
import { DataSendService } from '../../core-services/data-send.service';
|
||||
import { DataStoreService } from '../../core-services/data-store.service';
|
||||
import { BaseRepository } from '../base-repository';
|
||||
import { CollectionStringMapperService } from '../../core-services/collection-string-mapper.service';
|
||||
import { ViewModelStoreService } from 'app/core/core-services/view-model-store.service';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
|
||||
/**
|
||||
* Repository Services for Tags
|
||||
@ -22,7 +23,7 @@ import { TranslateService } from '@ngx-translate/core';
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class TagRepositoryService extends BaseRepository<ViewTag, Tag> {
|
||||
export class TagRepositoryService extends BaseRepository<ViewTag, Tag, TagTitleInformation> {
|
||||
/**
|
||||
* Creates a TagRepository
|
||||
* Converts existing and incoming Tags to ViewTags
|
||||
@ -43,14 +44,16 @@ export class TagRepositoryService extends BaseRepository<ViewTag, Tag> {
|
||||
this.initSorting();
|
||||
}
|
||||
|
||||
public getTitle = (titleInformation: TagTitleInformation) => {
|
||||
return titleInformation.name;
|
||||
};
|
||||
|
||||
public getVerboseName = (plural: boolean = false) => {
|
||||
return this.translate.instant(plural ? 'Tags' : 'Tag');
|
||||
};
|
||||
|
||||
protected createViewModel(tag: Tag): ViewTag {
|
||||
const viewTag = new ViewTag(tag);
|
||||
viewTag.getVerboseName = this.getVerboseName;
|
||||
return viewTag;
|
||||
return new ViewTag(tag);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -2,17 +2,17 @@ import { Injectable } from '@angular/core';
|
||||
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
|
||||
import { BaseAgendaContentObjectRepository } from '../base-agenda-content-object-repository';
|
||||
import { CollectionStringMapperService } from 'app/core/core-services/collection-string-mapper.service';
|
||||
import { DataStoreService } from 'app/core/core-services/data-store.service';
|
||||
import { DataSendService } from 'app/core/core-services/data-send.service';
|
||||
import { Item } from 'app/shared/models/agenda/item';
|
||||
import { Mediafile } from 'app/shared/models/mediafiles/mediafile';
|
||||
import { Topic } from 'app/shared/models/topics/topic';
|
||||
import { ViewTopic } from 'app/site/agenda/models/view-topic';
|
||||
import { ViewTopic, TopicTitleInformation } from 'app/site/agenda/models/view-topic';
|
||||
import { ViewModelStoreService } from 'app/core/core-services/view-model-store.service';
|
||||
import { ViewMediafile } from 'app/site/mediafiles/models/view-mediafile';
|
||||
import { ViewItem } from 'app/site/agenda/models/view-item';
|
||||
import { ViewListOfSpeakers } from 'app/site/agenda/models/view-list-of-speakers';
|
||||
import { BaseIsAgendaItemAndListOfSpeakersContentObjectRepository } from '../base-is-agenda-item-and-list-of-speakers-content-object-repository';
|
||||
|
||||
/**
|
||||
* Repository for topics
|
||||
@ -20,7 +20,11 @@ import { ViewItem } from 'app/site/agenda/models/view-item';
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class TopicRepositoryService extends BaseAgendaContentObjectRepository<ViewTopic, Topic> {
|
||||
export class TopicRepositoryService extends BaseIsAgendaItemAndListOfSpeakersContentObjectRepository<
|
||||
ViewTopic,
|
||||
Topic,
|
||||
TopicTitleInformation
|
||||
> {
|
||||
/**
|
||||
* Constructor calls the parent constructor
|
||||
*
|
||||
@ -35,16 +39,25 @@ export class TopicRepositoryService extends BaseAgendaContentObjectRepository<Vi
|
||||
viewModelStoreService: ViewModelStoreService,
|
||||
translate: TranslateService
|
||||
) {
|
||||
super(DS, dataSend, mapperService, viewModelStoreService, translate, Topic, [Mediafile, Item]);
|
||||
super(DS, dataSend, mapperService, viewModelStoreService, translate, Topic, [Mediafile]);
|
||||
}
|
||||
|
||||
public getAgendaTitle = (topic: Partial<Topic> | Partial<ViewTopic>) => {
|
||||
return topic.title;
|
||||
public getTitle = (titleInformation: TopicTitleInformation) => {
|
||||
if (titleInformation.agenda_item_number) {
|
||||
return titleInformation.agenda_item_number + ' · ' + titleInformation.title;
|
||||
} else {
|
||||
return titleInformation.title;
|
||||
}
|
||||
};
|
||||
|
||||
public getAgendaTitleWithType = (topic: Partial<Topic> | Partial<ViewTopic>) => {
|
||||
public getAgendaListTitle = (titleInformation: TopicTitleInformation) => {
|
||||
// Do not append ' (Topic)' to the title.
|
||||
return topic.title;
|
||||
return this.getTitle(titleInformation);
|
||||
};
|
||||
|
||||
public getAgendaSlideTitle = (titleInformation: TopicTitleInformation) => {
|
||||
// Do not append ' (Topic)' to the title.
|
||||
return this.getTitle(titleInformation);
|
||||
};
|
||||
|
||||
public getVerboseName = (plural: boolean = false) => {
|
||||
@ -60,11 +73,8 @@ export class TopicRepositoryService extends BaseAgendaContentObjectRepository<Vi
|
||||
public createViewModel(topic: Topic): ViewTopic {
|
||||
const attachments = this.viewModelStoreService.getMany(ViewMediafile, topic.attachments_id);
|
||||
const item = this.viewModelStoreService.get(ViewItem, topic.agenda_item_id);
|
||||
const viewTopic = new ViewTopic(topic, attachments, item);
|
||||
viewTopic.getVerboseName = this.getVerboseName;
|
||||
viewTopic.getAgendaTitle = () => this.getAgendaTitle(viewTopic);
|
||||
viewTopic.getAgendaTitleWithType = () => this.getAgendaTitle(viewTopic);
|
||||
return viewTopic;
|
||||
const listOfSpeakers = this.viewModelStoreService.get(ViewListOfSpeakers, topic.list_of_speakers_id);
|
||||
return new ViewTopic(topic, attachments, item, listOfSpeakers);
|
||||
}
|
||||
|
||||
/**
|
@ -1,14 +1,15 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
|
||||
import { BaseRepository } from '../base-repository';
|
||||
import { CollectionStringMapperService } from '../../core-services/collection-string-mapper.service';
|
||||
import { ConstantsService } from '../../core-services/constants.service';
|
||||
import { DataSendService } from '../../core-services/data-send.service';
|
||||
import { DataStoreService } from '../../core-services/data-store.service';
|
||||
import { Group } from 'app/shared/models/users/group';
|
||||
import { ViewGroup } from 'app/site/users/models/view-group';
|
||||
import { ViewGroup, GroupTitleInformation } from 'app/site/users/models/view-group';
|
||||
import { ViewModelStoreService } from 'app/core/core-services/view-model-store.service';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { HttpService } from 'app/core/core-services/http.service';
|
||||
|
||||
/**
|
||||
@ -35,7 +36,7 @@ export interface AppPermissions {
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class GroupRepositoryService extends BaseRepository<ViewGroup, Group> {
|
||||
export class GroupRepositoryService extends BaseRepository<ViewGroup, Group, GroupTitleInformation> {
|
||||
/**
|
||||
* holds sorted permissions per app.
|
||||
*/
|
||||
@ -61,14 +62,16 @@ export class GroupRepositoryService extends BaseRepository<ViewGroup, Group> {
|
||||
this.sortPermsPerApp();
|
||||
}
|
||||
|
||||
public getTitle = (titleInformation: GroupTitleInformation) => {
|
||||
return titleInformation.name;
|
||||
};
|
||||
|
||||
public getVerboseName = (plural: boolean = false) => {
|
||||
return this.translate.instant(plural ? 'Groups' : 'Group');
|
||||
};
|
||||
|
||||
public createViewModel(group: Group): ViewGroup {
|
||||
const viewGroup = new ViewGroup(group);
|
||||
viewGroup.getVerboseName = this.getVerboseName;
|
||||
return viewGroup;
|
||||
return new ViewGroup(group);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -8,7 +8,7 @@ import { BaseRepository } from '../base-repository';
|
||||
import { CollectionStringMapperService } from '../../core-services/collection-string-mapper.service';
|
||||
import { Identifiable } from 'app/shared/models/base/identifiable';
|
||||
import { PersonalNote } from 'app/shared/models/users/personal-note';
|
||||
import { ViewPersonalNote } from 'app/site/users/models/view-personal-note';
|
||||
import { ViewPersonalNote, PersonalNoteTitleInformation } from 'app/site/users/models/view-personal-note';
|
||||
import { ViewModelStoreService } from 'app/core/core-services/view-model-store.service';
|
||||
|
||||
/**
|
||||
@ -16,7 +16,11 @@ import { ViewModelStoreService } from 'app/core/core-services/view-model-store.s
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class PersonalNoteRepositoryService extends BaseRepository<ViewPersonalNote, PersonalNote> {
|
||||
export class PersonalNoteRepositoryService extends BaseRepository<
|
||||
ViewPersonalNote,
|
||||
PersonalNote,
|
||||
PersonalNoteTitleInformation
|
||||
> {
|
||||
/**
|
||||
* @param DS The DataStore
|
||||
* @param mapperService Maps collection strings to classes
|
||||
@ -31,14 +35,16 @@ export class PersonalNoteRepositoryService extends BaseRepository<ViewPersonalNo
|
||||
super(DS, dataSend, mapperService, viewModelStoreService, translate, PersonalNote);
|
||||
}
|
||||
|
||||
public getTitle = (titleInformation: PersonalNoteTitleInformation) => {
|
||||
return this.getVerboseName();
|
||||
};
|
||||
|
||||
public getVerboseName = (plural: boolean = false) => {
|
||||
return this.translate.instant(plural ? 'Personal notes' : 'Personal note');
|
||||
};
|
||||
|
||||
protected createViewModel(personalNote: PersonalNote): ViewPersonalNote {
|
||||
const viewPersonalNote = new ViewPersonalNote(personalNote);
|
||||
viewPersonalNote.getVerboseName = this.getVerboseName;
|
||||
return viewPersonalNote;
|
||||
return new ViewPersonalNote(personalNote);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -12,7 +12,7 @@ import { Group } from 'app/shared/models/users/group';
|
||||
import { HttpService } from 'app/core/core-services/http.service';
|
||||
import { NewEntry } from 'app/core/ui-services/base-import.service';
|
||||
import { User } from 'app/shared/models/users/user';
|
||||
import { ViewUser } from 'app/site/users/models/view-user';
|
||||
import { ViewUser, UserTitleInformation } from 'app/site/users/models/view-user';
|
||||
import { ViewGroup } from 'app/site/users/models/view-group';
|
||||
import { ViewModelStoreService } from 'app/core/core-services/view-model-store.service';
|
||||
|
||||
@ -32,7 +32,7 @@ type SortProperty = 'first_name' | 'last_name' | 'number';
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class UserRepositoryService extends BaseRepository<ViewUser, User> {
|
||||
export class UserRepositoryService extends BaseRepository<ViewUser, User, UserTitleInformation> {
|
||||
/**
|
||||
* The property the incoming data is sorted by
|
||||
*/
|
||||
@ -65,6 +65,55 @@ export class UserRepositoryService extends BaseRepository<ViewUser, User> {
|
||||
});
|
||||
}
|
||||
|
||||
public getTitle = (titleInformation: UserTitleInformation) => {
|
||||
return this.getFullName(titleInformation);
|
||||
};
|
||||
|
||||
/**
|
||||
* Getter for the short name (Title, given name, surname)
|
||||
*
|
||||
* @returns a non-empty string
|
||||
*/
|
||||
public getShortName(titleInformation: UserTitleInformation): string {
|
||||
const title = titleInformation.title ? titleInformation.title.trim() : '';
|
||||
const firstName = titleInformation.first_name ? titleInformation.first_name.trim() : '';
|
||||
const lastName = titleInformation.last_name ? titleInformation.last_name.trim() : '';
|
||||
let shortName = `${firstName} ${lastName}`;
|
||||
|
||||
if (shortName.length <= 1) {
|
||||
// We have at least one space from the concatination of
|
||||
// first- and lastname.
|
||||
shortName = titleInformation.username;
|
||||
}
|
||||
|
||||
if (title) {
|
||||
shortName = `${title} ${shortName}`;
|
||||
}
|
||||
|
||||
return shortName;
|
||||
}
|
||||
|
||||
public getFullName(titleInformation: UserTitleInformation): string {
|
||||
let name = this.getShortName(titleInformation);
|
||||
const additions: string[] = [];
|
||||
|
||||
// addition: add number and structure level
|
||||
const structure_level = titleInformation.structure_level ? titleInformation.structure_level.trim() : '';
|
||||
if (structure_level) {
|
||||
additions.push(structure_level);
|
||||
}
|
||||
|
||||
const number = titleInformation.number ? titleInformation.number.trim() : '';
|
||||
if (number) {
|
||||
additions.push(`${this.translate.instant('No.')} ${number}`);
|
||||
}
|
||||
|
||||
if (additions.length > 0) {
|
||||
name += ' (' + additions.join(' · ') + ')';
|
||||
}
|
||||
return name.trim();
|
||||
}
|
||||
|
||||
public getVerboseName = (plural: boolean = false) => {
|
||||
return this.translate.instant(plural ? 'Participants' : 'Participant');
|
||||
};
|
||||
@ -72,10 +121,8 @@ export class UserRepositoryService extends BaseRepository<ViewUser, User> {
|
||||
public createViewModel(user: User): ViewUser {
|
||||
const groups = this.viewModelStoreService.getMany(ViewGroup, user.groups_id);
|
||||
const viewUser = new ViewUser(user, groups);
|
||||
viewUser.getVerboseName = this.getVerboseName;
|
||||
viewUser.getNumberForName = (nr: number) => {
|
||||
return `${this.translate.instant('No.')} ${nr}`;
|
||||
};
|
||||
viewUser.getFullName = () => this.getFullName(viewUser);
|
||||
viewUser.getShortName = () => this.getShortName(viewUser);
|
||||
return viewUser;
|
||||
}
|
||||
|
||||
|
@ -2,7 +2,7 @@ import { BehaviorSubject, Observable, Subscription } from 'rxjs';
|
||||
|
||||
import { BaseModel } from 'app/shared/models/base/base-model';
|
||||
import { BaseRepository } from '../repositories/base-repository';
|
||||
import { BaseViewModel } from '../../site/base/base-view-model';
|
||||
import { BaseViewModel, TitleInformation } from '../../site/base/base-view-model';
|
||||
import { StorageService } from '../core-services/storage.service';
|
||||
|
||||
/**
|
||||
@ -208,7 +208,7 @@ export abstract class BaseFilterListService<V extends BaseViewModel> {
|
||||
* @param exexcludeIds Set if certain ID's should be excluded from filtering
|
||||
*/
|
||||
protected updateFilterForRepo(
|
||||
repo: BaseRepository<BaseViewModel, BaseModel>,
|
||||
repo: BaseRepository<BaseViewModel, BaseModel, TitleInformation>,
|
||||
filter: OsFilter,
|
||||
noneOptionLabel?: string,
|
||||
excludeIds?: number[]
|
||||
|
@ -92,7 +92,7 @@ export class SearchService {
|
||||
*/
|
||||
public registerModel(
|
||||
collectionString: string,
|
||||
repo: BaseRepository<any, any>,
|
||||
repo: BaseRepository<any, any, any>,
|
||||
displayOrder: number,
|
||||
openInNewTab: boolean = false
|
||||
): void {
|
||||
|
@ -0,0 +1,16 @@
|
||||
<ng-container *osPerms="'agenda.can_see_list_of_speakers'">
|
||||
<ng-container *ngIf="listOfSpeakers">
|
||||
<button type="button" *ngIf="!menuItem" mat-icon-button [routerLink]="listOfSpeakers.listOfSpeakersUrl" [disabled]="disabled">
|
||||
<mat-icon
|
||||
[matBadge]="listOfSpeakers.waitingSpeakerAmount > 0 ? listOfSpeakers.waitingSpeakerAmount : null"
|
||||
matBadgeColor="accent"
|
||||
>
|
||||
mic
|
||||
</mat-icon>
|
||||
</button>
|
||||
<button type="button" *ngIf="menuItem" mat-menu-item [routerLink]="listOfSpeakers.listOfSpeakersUrl">
|
||||
<mat-icon>mic</mat-icon>
|
||||
<span translate>List of speakers</span>
|
||||
</button>
|
||||
</ng-container>
|
||||
</ng-container>
|
@ -0,0 +1,25 @@
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { E2EImportsModule } from 'e2e-imports.module';
|
||||
import { SpeakerButtonComponent } from './speaker-button.component';
|
||||
|
||||
describe('SpeakerButtonComponent', () => {
|
||||
let component: SpeakerButtonComponent;
|
||||
let fixture: ComponentFixture<SpeakerButtonComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [E2EImportsModule]
|
||||
}).compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(SpeakerButtonComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
@ -0,0 +1,45 @@
|
||||
import { Component, Input } from '@angular/core';
|
||||
|
||||
import { ContentObject, isContentObject } from 'app/shared/models/base/content-object';
|
||||
import { ViewListOfSpeakers } from 'app/site/agenda/models/view-list-of-speakers';
|
||||
import {
|
||||
BaseViewModelWithListOfSpeakers,
|
||||
isBaseViewModelWithListOfSpeakers
|
||||
} from 'app/site/base/base-view-model-with-list-of-speakers';
|
||||
import { ListOfSpeakersRepositoryService } from 'app/core/repositories/agenda/list-of-speakers-repository.service';
|
||||
|
||||
/**
|
||||
* A generic button to go to the list of speakers. Give the content object with
|
||||
* [object]=object, which can be a ContentObject or a ViewModelWithListOfSpeakers.
|
||||
* - Usage as a mini-fab (like in the agenda) with [menuItem]=false (default)
|
||||
* - Usage in a dropdown (=list) with [menuItem]=true
|
||||
*/
|
||||
@Component({
|
||||
selector: 'os-speaker-button',
|
||||
templateUrl: './speaker-button.component.html'
|
||||
})
|
||||
export class SpeakerButtonComponent {
|
||||
@Input()
|
||||
public set object(obj: BaseViewModelWithListOfSpeakers | ContentObject | null) {
|
||||
if (isBaseViewModelWithListOfSpeakers(obj)) {
|
||||
this.listOfSpeakers = obj.listOfSpeakers;
|
||||
} else if (isContentObject(obj)) {
|
||||
this.listOfSpeakers = this.listOfSpeakersRepo.findByContentObject(obj);
|
||||
} else {
|
||||
this.listOfSpeakers = null;
|
||||
}
|
||||
}
|
||||
|
||||
public listOfSpeakers: ViewListOfSpeakers | null;
|
||||
|
||||
@Input()
|
||||
public disabled: boolean;
|
||||
|
||||
@Input()
|
||||
public menuItem = false;
|
||||
|
||||
/**
|
||||
* The constructor
|
||||
*/
|
||||
public constructor(private listOfSpeakersRepo: ListOfSpeakersRepositoryService) {}
|
||||
}
|
@ -1,14 +1,5 @@
|
||||
import { Speaker } from './speaker';
|
||||
import { BaseModel } from '../base/base-model';
|
||||
|
||||
/**
|
||||
* The representation of the content object for agenda items. The unique combination
|
||||
* of the collection and id is given.
|
||||
*/
|
||||
interface ContentObject {
|
||||
id: number;
|
||||
collection: string;
|
||||
}
|
||||
import { ContentObject } from '../base/content-object';
|
||||
import { BaseModelWithContentObject } from '../base/base-model-with-content-object';
|
||||
|
||||
/**
|
||||
* Determine visibility states for agenda items
|
||||
@ -24,19 +15,36 @@ export const itemVisibilityChoices = [
|
||||
* Representations of agenda Item
|
||||
* @ignore
|
||||
*/
|
||||
export class Item extends BaseModel<Item> {
|
||||
export class Item extends BaseModelWithContentObject<Item> {
|
||||
public static COLLECTIONSTRING = 'agenda/item';
|
||||
|
||||
// TODO: remove this, if the server can properly include the agenda item number
|
||||
// in the title information. See issue #4738
|
||||
private _itemNumber: string;
|
||||
private _titleInformation: any;
|
||||
|
||||
public id: number;
|
||||
public item_number: string;
|
||||
public title_information: object;
|
||||
public get item_number(): string {
|
||||
return this._itemNumber;
|
||||
}
|
||||
public set item_number(val: string) {
|
||||
this._itemNumber = val;
|
||||
if (this._titleInformation) {
|
||||
this._titleInformation.agenda_item_number = this.item_number;
|
||||
}
|
||||
}
|
||||
public get title_information(): object {
|
||||
return this._titleInformation;
|
||||
}
|
||||
public set title_information(val: object) {
|
||||
this._titleInformation = val;
|
||||
this._titleInformation.agenda_item_number = this.item_number;
|
||||
}
|
||||
public comment: string;
|
||||
public closed: boolean;
|
||||
public type: number;
|
||||
public is_hidden: boolean;
|
||||
public duration: number; // minutes
|
||||
public speakers: Speaker[];
|
||||
public speaker_list_closed: boolean;
|
||||
public content_object: ContentObject;
|
||||
public weight: number;
|
||||
public parent_id: number;
|
||||
@ -45,14 +53,4 @@ export class Item extends BaseModel<Item> {
|
||||
public constructor(input?: any) {
|
||||
super(Item.COLLECTIONSTRING, input);
|
||||
}
|
||||
|
||||
public deserialize(input: any): void {
|
||||
Object.assign(this, input);
|
||||
|
||||
if (input.speakers instanceof Array) {
|
||||
this.speakers = input.speakers.map(speakerData => {
|
||||
return new Speaker(speakerData);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
21
client/src/app/shared/models/agenda/list-of-speakers.ts
Normal file
21
client/src/app/shared/models/agenda/list-of-speakers.ts
Normal file
@ -0,0 +1,21 @@
|
||||
import { Speaker } from './speaker';
|
||||
import { ContentObject } from '../base/content-object';
|
||||
import { BaseModelWithContentObject } from '../base/base-model-with-content-object';
|
||||
|
||||
/**
|
||||
* Representations of agenda Item
|
||||
* @ignore
|
||||
*/
|
||||
export class ListOfSpeakers extends BaseModelWithContentObject<ListOfSpeakers> {
|
||||
public static COLLECTIONSTRING = 'agenda/list-of-speakers';
|
||||
|
||||
public id: number;
|
||||
public title_information: object;
|
||||
public speakers: Speaker[];
|
||||
public closed: boolean;
|
||||
public content_object: ContentObject;
|
||||
|
||||
public constructor(input?: any) {
|
||||
super(ListOfSpeakers.COLLECTIONSTRING, input);
|
||||
}
|
||||
}
|
@ -1,14 +1,3 @@
|
||||
import { Deserializer } from '../base/deserializer';
|
||||
|
||||
/**
|
||||
* Determine the state of the speaker
|
||||
*/
|
||||
export enum SpeakerState {
|
||||
WAITING,
|
||||
CURRENT,
|
||||
FINISHED
|
||||
}
|
||||
|
||||
/**
|
||||
* Representation of a speaker in an agenda item.
|
||||
*
|
||||
@ -16,49 +5,23 @@ export enum SpeakerState {
|
||||
* Part of the 'speakers' list.
|
||||
* @ignore
|
||||
*/
|
||||
export class Speaker extends Deserializer {
|
||||
public static COLLECTIONSTRING = 'agenda/item/speakers';
|
||||
|
||||
public id: number;
|
||||
public user_id: number;
|
||||
export interface Speaker {
|
||||
id: number;
|
||||
user_id: number;
|
||||
|
||||
/**
|
||||
* ISO datetime string to indicate the begin time of the speech. Empty if
|
||||
* the speaker has not started
|
||||
*/
|
||||
public begin_time: string;
|
||||
begin_time: string;
|
||||
|
||||
/**
|
||||
* ISO datetime string to indicate the end time of the speech. Empty if the
|
||||
* speech has not ended
|
||||
*/
|
||||
public end_time: string;
|
||||
end_time: string;
|
||||
|
||||
public weight: number;
|
||||
public marked: boolean;
|
||||
public item_id: number;
|
||||
|
||||
/**
|
||||
* Needs to be completely optional because agenda has (yet) the optional parameter 'speaker'
|
||||
* @param input
|
||||
*/
|
||||
public constructor(input?: any) {
|
||||
super(input);
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns
|
||||
* - waiting if there is no begin nor end time
|
||||
* - current if there is a begin time and not end time
|
||||
* - finished if there are both begin and end time
|
||||
*/
|
||||
public get state(): SpeakerState {
|
||||
if (!this.begin_time && !this.end_time) {
|
||||
return SpeakerState.WAITING;
|
||||
} else if (this.begin_time && !this.end_time) {
|
||||
return SpeakerState.CURRENT;
|
||||
} else {
|
||||
return SpeakerState.FINISHED;
|
||||
}
|
||||
}
|
||||
weight: number;
|
||||
marked: boolean;
|
||||
item_id: number;
|
||||
}
|
||||
|
@ -1,12 +1,12 @@
|
||||
import { BaseModel } from '../base/base-model';
|
||||
import { AssignmentRelatedUser } from './assignment-related-user';
|
||||
import { AssignmentPoll } from './assignment-poll';
|
||||
import { BaseModelWithAgendaItemAndListOfSpeakers } from '../base/base-model-with-agenda-item-and-list-of-speakers';
|
||||
|
||||
/**
|
||||
* Representation of an assignment.
|
||||
* @ignore
|
||||
*/
|
||||
export class Assignment extends BaseModel<Assignment> {
|
||||
export class Assignment extends BaseModelWithAgendaItemAndListOfSpeakers<Assignment> {
|
||||
public static COLLECTIONSTRING = 'assignments/assignment';
|
||||
|
||||
public id: number;
|
||||
@ -17,7 +17,6 @@ export class Assignment extends BaseModel<Assignment> {
|
||||
public assignment_related_users: AssignmentRelatedUser[];
|
||||
public poll_description_default: number;
|
||||
public polls: AssignmentPoll[];
|
||||
public agenda_item_id: number;
|
||||
public tags_id: number[];
|
||||
public attachments_id: number[];
|
||||
|
||||
|
@ -0,0 +1,16 @@
|
||||
import { BaseModel } from './base-model';
|
||||
import { BaseModelWithAgendaItem, isBaseModelWithAgendaItem } from './base-model-with-agenda-item';
|
||||
import { BaseModelWithListOfSpeakers, isBaseModelWithListOfSpeakers } from './base-model-with-list-of-speakers';
|
||||
|
||||
export function isBaseModelWithAgendaItemAndListOfSpeakers(obj: any): obj is BaseModelWithAgendaItemAndListOfSpeakers {
|
||||
return !!obj && isBaseModelWithAgendaItem(obj) && isBaseModelWithListOfSpeakers(obj);
|
||||
}
|
||||
|
||||
/**
|
||||
* A base model with an agenda item and a list of speakers.
|
||||
*/
|
||||
export abstract class BaseModelWithAgendaItemAndListOfSpeakers<T = object> extends BaseModel<T>
|
||||
implements BaseModelWithAgendaItem<T>, BaseModelWithListOfSpeakers<T> {
|
||||
public agenda_item_id: number;
|
||||
public list_of_speakers_id: number;
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
import { BaseModel } from './base-model';
|
||||
|
||||
export function isBaseModelWithAgendaItem(obj: any): obj is BaseModelWithAgendaItem {
|
||||
return !!obj && (<BaseModelWithAgendaItem>obj).agenda_item_id !== undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* A base model which has an agenda item. These models have a `agenda_item_id` in any case.
|
||||
*/
|
||||
export abstract class BaseModelWithAgendaItem<T = object> extends BaseModel<T> {
|
||||
public agenda_item_id: number;
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
import { ContentObject } from './content-object';
|
||||
import { BaseModel } from './base-model';
|
||||
|
||||
/**
|
||||
* A base model which has a content object, like items of list of speakers.
|
||||
*/
|
||||
export abstract class BaseModelWithContentObject<T = object> extends BaseModel<T> {
|
||||
public abstract content_object: ContentObject;
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
import { BaseModel } from './base-model';
|
||||
|
||||
export function isBaseModelWithListOfSpeakers(obj: any): obj is BaseModelWithListOfSpeakers {
|
||||
return !!obj && (<BaseModelWithListOfSpeakers>obj).list_of_speakers_id !== undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* A base model with a list of speakers. The id is always given by the server.
|
||||
*/
|
||||
export abstract class BaseModelWithListOfSpeakers<T = object> extends BaseModel<T> {
|
||||
public list_of_speakers_id: number;
|
||||
}
|
12
client/src/app/shared/models/base/content-object.ts
Normal file
12
client/src/app/shared/models/base/content-object.ts
Normal file
@ -0,0 +1,12 @@
|
||||
export function isContentObject(obj: any): obj is ContentObject {
|
||||
return !!obj && obj.id !== undefined && obj.collection !== undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* The representation of content objects. Holds the unique combination
|
||||
* of the collection and the id.
|
||||
*/
|
||||
export interface ContentObject {
|
||||
id: number;
|
||||
collection: string;
|
||||
}
|
@ -1,19 +0,0 @@
|
||||
import { Deserializer } from '../base/deserializer';
|
||||
|
||||
/**
|
||||
* The name and the type of a mediaFile.
|
||||
* @ignore
|
||||
*/
|
||||
export class File extends Deserializer {
|
||||
public name: string;
|
||||
public type: string;
|
||||
|
||||
/**
|
||||
* Needs to be fully optional, because the 'mediafile'-property in the mediaFile class is optional as well
|
||||
* @param name The name of the file
|
||||
* @param type The tape (jpg, png, pdf)
|
||||
*/
|
||||
public constructor(input?: any) {
|
||||
super(input);
|
||||
}
|
||||
}
|
@ -1,15 +1,23 @@
|
||||
import { File } from './file';
|
||||
import { BaseModel } from '../base/base-model';
|
||||
import { BaseModelWithListOfSpeakers } from '../base/base-model-with-list-of-speakers';
|
||||
|
||||
interface FileMetadata {
|
||||
name: string;
|
||||
type: string;
|
||||
|
||||
// Only for PDFs
|
||||
pages: number;
|
||||
encrypted?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Representation of MediaFile. Has the nested property "File"
|
||||
* @ignore
|
||||
*/
|
||||
export class Mediafile extends BaseModel<Mediafile> {
|
||||
export class Mediafile extends BaseModelWithListOfSpeakers<Mediafile> {
|
||||
public static COLLECTIONSTRING = 'mediafiles/mediafile';
|
||||
public id: number;
|
||||
public title: string;
|
||||
public mediafile: File;
|
||||
public mediafile: FileMetadata;
|
||||
public media_url_prefix: string;
|
||||
public uploader_id: number;
|
||||
public filesize: string;
|
||||
@ -20,11 +28,6 @@ export class Mediafile extends BaseModel<Mediafile> {
|
||||
super(Mediafile.COLLECTIONSTRING, input);
|
||||
}
|
||||
|
||||
public deserialize(input: any): void {
|
||||
Object.assign(this, input);
|
||||
this.mediafile = new File(input.mediafile);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the downloadURL
|
||||
*
|
||||
|
@ -1,15 +1,14 @@
|
||||
import { BaseModel } from '../base/base-model';
|
||||
import { BaseModelWithAgendaItemAndListOfSpeakers } from '../base/base-model-with-agenda-item-and-list-of-speakers';
|
||||
|
||||
/**
|
||||
* Representation of a motion block.
|
||||
* @ignore
|
||||
*/
|
||||
export class MotionBlock extends BaseModel {
|
||||
export class MotionBlock extends BaseModelWithAgendaItemAndListOfSpeakers<MotionBlock> {
|
||||
public static COLLECTIONSTRING = 'motions/motion-block';
|
||||
|
||||
public id: number;
|
||||
public title: string;
|
||||
public agenda_item_id: number;
|
||||
|
||||
public constructor(input?: any) {
|
||||
super(MotionBlock.COLLECTIONSTRING, input);
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { MotionSubmitter } from './motion-submitter';
|
||||
import { MotionPoll } from './motion-poll';
|
||||
import { BaseModel } from '../base/base-model';
|
||||
import { BaseModelWithAgendaItemAndListOfSpeakers } from '../base/base-model-with-agenda-item-and-list-of-speakers';
|
||||
|
||||
export interface MotionComment {
|
||||
id: number;
|
||||
@ -12,11 +12,11 @@ export interface MotionComment {
|
||||
/**
|
||||
* Representation of Motion.
|
||||
*
|
||||
* Slightly Defined cause heavy maintaince on server side.
|
||||
* Slightly defined cause heavy maintenance on server side.
|
||||
*
|
||||
* @ignore
|
||||
*/
|
||||
export class Motion extends BaseModel {
|
||||
export class Motion extends BaseModelWithAgendaItemAndListOfSpeakers<Motion> {
|
||||
public static COLLECTIONSTRING = 'motions/motion';
|
||||
|
||||
public id: number;
|
||||
@ -44,7 +44,6 @@ export class Motion extends BaseModel {
|
||||
public tags_id: number[];
|
||||
public attachments_id: number[];
|
||||
public polls: MotionPoll[];
|
||||
public agenda_item_id: number;
|
||||
public weight: number;
|
||||
public sort_parent_id: number;
|
||||
public created: string;
|
||||
|
@ -1,17 +1,16 @@
|
||||
import { BaseModel } from '../base/base-model';
|
||||
import { BaseModelWithAgendaItemAndListOfSpeakers } from '../base/base-model-with-agenda-item-and-list-of-speakers';
|
||||
|
||||
/**
|
||||
* Representation of a topic.
|
||||
* @ignore
|
||||
*/
|
||||
export class Topic extends BaseModel<Topic> {
|
||||
export class Topic extends BaseModelWithAgendaItemAndListOfSpeakers<Topic> {
|
||||
public static COLLECTIONSTRING = 'topics/topic';
|
||||
|
||||
public id: number;
|
||||
public title: string;
|
||||
public text: string;
|
||||
public attachments_id: number[];
|
||||
public agenda_item_id: number;
|
||||
|
||||
public constructor(input?: any) {
|
||||
super(Topic.COLLECTIONSTRING, input);
|
||||
|
@ -83,6 +83,7 @@ import { SlideContainerComponent } from './components/slide-container/slide-cont
|
||||
import { CountdownTimeComponent } from './components/contdown-time/countdown-time.component';
|
||||
import { MediaUploadContentComponent } from './components/media-upload-content/media-upload-content.component';
|
||||
import { PrecisionPipe } from './pipes/precision.pipe';
|
||||
import { SpeakerButtonComponent } from './components/speaker-button/speaker-button.component';
|
||||
|
||||
/**
|
||||
* Share Module for all "dumb" components and pipes.
|
||||
@ -206,7 +207,8 @@ import { PrecisionPipe } from './pipes/precision.pipe';
|
||||
CountdownTimeComponent,
|
||||
MediaUploadContentComponent,
|
||||
PrecisionPipe,
|
||||
ScrollingModule
|
||||
ScrollingModule,
|
||||
SpeakerButtonComponent
|
||||
],
|
||||
declarations: [
|
||||
PermsDirective,
|
||||
@ -234,7 +236,8 @@ import { PrecisionPipe } from './pipes/precision.pipe';
|
||||
SlideContainerComponent,
|
||||
CountdownTimeComponent,
|
||||
MediaUploadContentComponent,
|
||||
PrecisionPipe
|
||||
PrecisionPipe,
|
||||
SpeakerButtonComponent
|
||||
],
|
||||
providers: [
|
||||
{ provide: DateAdapter, useClass: OpenSlidesDateAdapter },
|
||||
|
@ -7,7 +7,7 @@ import { BaseImportService, NewEntry } from 'app/core/ui-services/base-import.se
|
||||
import { CreateTopic } from './models/create-topic';
|
||||
import { DurationService } from 'app/core/ui-services/duration.service';
|
||||
import { itemVisibilityChoices } from 'app/shared/models/agenda/item';
|
||||
import { TopicRepositoryService } from '../../core/repositories/agenda/topic-repository.service';
|
||||
import { TopicRepositoryService } from '../../core/repositories/topics/topic-repository.service';
|
||||
import { ViewCreateTopic } from './models/view-create-topic';
|
||||
|
||||
@Injectable({
|
||||
|
@ -18,9 +18,9 @@ const routes: Routes = [
|
||||
canDeactivate: [WatchSortingTreeGuard],
|
||||
data: { basePerm: 'agenda.can_manage' }
|
||||
},
|
||||
{ path: 'speakers', component: ListOfSpeakersComponent, data: { basePerm: 'agenda.can_see' } },
|
||||
{ path: 'topics/:id', component: TopicDetailComponent, data: { basePerm: 'agenda.can_see' } },
|
||||
{ path: ':id/speakers', component: ListOfSpeakersComponent, data: { basePerm: 'agenda.can_see' } }
|
||||
{ path: 'speakers', component: ListOfSpeakersComponent, data: { basePerm: 'agenda.can_see_list_of_speakers' } },
|
||||
{ path: 'speakers/:id', component: ListOfSpeakersComponent, data: { basePerm: 'agenda.can_see_list_of_speakers' } },
|
||||
{ path: 'topics/:id', component: TopicDetailComponent, data: { basePerm: 'agenda.can_see' } }
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
|
@ -2,14 +2,23 @@ import { AppConfig } from '../../core/app-config';
|
||||
import { Item } from '../../shared/models/agenda/item';
|
||||
import { Topic } from '../../shared/models/topics/topic';
|
||||
import { ItemRepositoryService } from 'app/core/repositories/agenda/item-repository.service';
|
||||
import { TopicRepositoryService } from 'app/core/repositories/agenda/topic-repository.service';
|
||||
import { TopicRepositoryService } from 'app/core/repositories/topics/topic-repository.service';
|
||||
import { ViewTopic } from './models/view-topic';
|
||||
import { ViewItem } from './models/view-item';
|
||||
import { ListOfSpeakers } from 'app/shared/models/agenda/list-of-speakers';
|
||||
import { ViewListOfSpeakers } from './models/view-list-of-speakers';
|
||||
import { ListOfSpeakersRepositoryService } from 'app/core/repositories/agenda/list-of-speakers-repository.service';
|
||||
|
||||
export const AgendaAppConfig: AppConfig = {
|
||||
name: 'agenda',
|
||||
models: [
|
||||
{ collectionString: 'agenda/item', model: Item, viewModel: ViewItem, repository: ItemRepositoryService },
|
||||
{
|
||||
collectionString: 'agenda/list-of-speakers',
|
||||
model: ListOfSpeakers,
|
||||
viewModel: ViewListOfSpeakers,
|
||||
repository: ListOfSpeakersRepositoryService
|
||||
},
|
||||
{
|
||||
collectionString: 'topics/topic',
|
||||
model: Topic,
|
||||
|
@ -77,14 +77,7 @@
|
||||
<ng-container matColumnDef="speakers">
|
||||
<mat-header-cell *matHeaderCellDef mat-sort-header>Speakers</mat-header-cell>
|
||||
<mat-cell *matCellDef="let item">
|
||||
<button mat-icon-button (click)="onSpeakerIcon(item, $event)" [disabled]="isMultiSelect">
|
||||
<mat-icon
|
||||
[matBadge]="item.waitingSpeakerAmount > 0 ? item.waitingSpeakerAmount : null"
|
||||
matBadgeColor="accent"
|
||||
>
|
||||
mic
|
||||
</mat-icon>
|
||||
</button>
|
||||
<os-speaker-button [object]="item.contentObjectData" [disabled]="isMultiSelect"></os-speaker-button>
|
||||
</mat-cell>
|
||||
</ng-container>
|
||||
|
||||
|
@ -21,6 +21,8 @@ import { ViewItem } from '../../models/view-item';
|
||||
import { ProjectorElementBuildDeskriptor } from 'app/site/base/projectable';
|
||||
import { _ } from 'app/core/translate/translation-marker';
|
||||
import { StorageService } from 'app/core/core-services/storage.service';
|
||||
import { ListOfSpeakersRepositoryService } from 'app/core/repositories/agenda/list-of-speakers-repository.service';
|
||||
import { ViewListOfSpeakers } from '../../models/view-list-of-speakers';
|
||||
|
||||
/**
|
||||
* List view for the agenda.
|
||||
@ -35,12 +37,12 @@ export class AgendaListComponent extends ListViewBaseComponent<ViewItem, Item, I
|
||||
/**
|
||||
* Determine the display columns in desktop view
|
||||
*/
|
||||
public displayedColumnsDesktop: string[] = ['title', 'info', 'speakers'];
|
||||
public displayedColumnsDesktop: string[] = ['title', 'info'];
|
||||
|
||||
/**
|
||||
* Determine the display columns in mobile view
|
||||
*/
|
||||
public displayedColumnsMobile: string[] = ['title', 'speakers'];
|
||||
public displayedColumnsMobile: string[] = ['title'];
|
||||
|
||||
public isNumberingAllowed: boolean;
|
||||
|
||||
@ -105,7 +107,8 @@ export class AgendaListComponent extends ListViewBaseComponent<ViewItem, Item, I
|
||||
private csvExport: AgendaCsvExportService,
|
||||
public filterService: AgendaFilterListService,
|
||||
private agendaPdfService: AgendaPdfService,
|
||||
private pdfService: PdfDocumentService
|
||||
private pdfService: PdfDocumentService,
|
||||
private listOfSpeakersRepo: ListOfSpeakersRepositoryService
|
||||
) {
|
||||
super(titleService, translate, matSnackBar, repo, route, storage, filterService);
|
||||
|
||||
@ -126,6 +129,16 @@ export class AgendaListComponent extends ListViewBaseComponent<ViewItem, Item, I
|
||||
this.setFulltextFilter();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the list of speakers for an agenda item. Might be null, if the items content
|
||||
* object does not have a list of speakers.
|
||||
*
|
||||
* @param item The item to get the list of speakers from
|
||||
*/
|
||||
public getListOfSpeakers(item: ViewItem): ViewListOfSpeakers | null {
|
||||
return this.listOfSpeakersRepo.findByContentObject(item.item.content_object);
|
||||
}
|
||||
|
||||
/**
|
||||
* Links to the content object.
|
||||
*
|
||||
@ -183,16 +196,6 @@ export class AgendaListComponent extends ListViewBaseComponent<ViewItem, Item, I
|
||||
await this.repo.update({ closed: !item.closed }, item).then(null, this.raiseError);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler for the speakers button
|
||||
*
|
||||
* @param item indicates the row that was clicked on
|
||||
*/
|
||||
public onSpeakerIcon(item: ViewItem, event: MouseEvent): void {
|
||||
event.stopPropagation();
|
||||
this.router.navigate([`${item.id}/speakers`], { relativeTo: this.route });
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler for the plus button.
|
||||
* Comes from the HeadBar Component
|
||||
@ -258,6 +261,9 @@ export class AgendaListComponent extends ListViewBaseComponent<ViewItem, Item, I
|
||||
*/
|
||||
public getColumnDefinition(): string[] {
|
||||
let columns = this.vp.isMobile ? this.displayedColumnsMobile : this.displayedColumnsDesktop;
|
||||
if (this.operator.hasPerms('agenda.can_see_list_of_speakers')) {
|
||||
columns = columns.concat(['speakers']);
|
||||
}
|
||||
if (this.operator.hasPerms('agenda.can_manage')) {
|
||||
columns = columns.concat(['menu']);
|
||||
}
|
||||
|
@ -2,8 +2,8 @@
|
||||
<!-- Title -->
|
||||
<div class="title-slot">
|
||||
<h2>
|
||||
<span *ngIf="!currentListOfSpeakers" translate>List of speakers</span>
|
||||
<span *ngIf="currentListOfSpeakers" translate>Current list of speakers</span>
|
||||
<span *ngIf="!isCurrentListOfSpeakers" translate>List of speakers</span>
|
||||
<span *ngIf="isCurrentListOfSpeakers" translate>Current list of speakers</span>
|
||||
</h2>
|
||||
</div>
|
||||
<div class="menu-slot" *osPerms="['agenda.can_manage_list_of_speakers', 'core.can_manage_projector']">
|
||||
@ -11,9 +11,9 @@
|
||||
</div>
|
||||
</os-head-bar>
|
||||
|
||||
<mat-card class="os-card speaker-card" *ngIf="viewItem">
|
||||
<mat-card class="os-card speaker-card" *ngIf="viewListOfSpeakers">
|
||||
<!-- Title -->
|
||||
<h1 class="los-title on-transition-fade" *ngIf="viewItem">{{ viewItem.getTitle() }}</h1>
|
||||
<h1 class="los-title on-transition-fade">{{ viewListOfSpeakers.getTitle() }}</h1>
|
||||
|
||||
<!-- List of finished speakers -->
|
||||
<mat-expansion-panel *ngIf="finishedSpeakers && finishedSpeakers.length > 0" class="finished-list">
|
||||
@ -24,7 +24,7 @@
|
||||
<mat-list-item *ngFor="let speaker of finishedSpeakers; let number = index">
|
||||
<div class="finished-speaker-grid">
|
||||
<div class="number">{{ number + 1 }}.</div>
|
||||
<div class="name">{{ speaker }}</div>
|
||||
<div class="name">{{ speaker.getTitle() }}</div>
|
||||
<div class="time">
|
||||
{{ durationString(speaker) }} ({{ 'Start time' | translate }}: {{ startTimeToString(speaker) }})
|
||||
</div>
|
||||
@ -53,7 +53,7 @@
|
||||
<mat-icon>mic</mat-icon>
|
||||
</span>
|
||||
|
||||
<span class="name">{{ activeSpeaker }}</span>
|
||||
<span class="name">{{ activeSpeaker.getTitle() }}</span>
|
||||
|
||||
<span class="suffix">
|
||||
<!-- Stop speaker button -->
|
||||
@ -77,29 +77,29 @@
|
||||
[enable]="opCanManage()"
|
||||
(sortEvent)="onSortingChange($event)"
|
||||
>
|
||||
<!-- implicit item references into the component using ng-template slot -->
|
||||
<ng-template let-item>
|
||||
<!-- implicit speaker references into the component using ng-template slot -->
|
||||
<ng-template let-speaker>
|
||||
<span *osPerms="'agenda.can_manage_list_of_speakers'">
|
||||
<span *ngIf="hasSpokenCount(item)" class="red-warning-text speaker-warning">
|
||||
{{ hasSpokenCount(item) + 1 }}. <span translate>contribution</span>
|
||||
<span *ngIf="hasSpokenCount(speaker)" class="red-warning-text speaker-warning">
|
||||
{{ hasSpokenCount(speaker) + 1 }}. <span translate>contribution</span>
|
||||
</span>
|
||||
<span *ngIf="item.gender">({{ item.gender | translate }})</span>
|
||||
<span *ngIf="speaker.gender">({{ speaker.gender | translate }})</span>
|
||||
</span>
|
||||
|
||||
<!-- Start, start and delete buttons -->
|
||||
<span *osPerms="'agenda.can_manage_list_of_speakers'">
|
||||
<!-- start button -->
|
||||
<button mat-icon-button matTooltip="{{ 'Begin speech' | translate }}" (click)="onStartButton(item)">
|
||||
<button mat-icon-button matTooltip="{{ 'Begin speech' | translate }}" (click)="onStartButton(speaker)">
|
||||
<mat-icon>play_arrow</mat-icon>
|
||||
</button>
|
||||
|
||||
<!-- star button -->
|
||||
<button mat-icon-button matTooltip="{{ 'Mark speaker' | translate }}" (click)="onMarkButton(item)">
|
||||
<mat-icon>{{ item.marked ? 'star' : 'star_border' }}</mat-icon>
|
||||
<button mat-icon-button matTooltip="{{ 'Mark speaker' | translate }}" (click)="onMarkButton(speaker)">
|
||||
<mat-icon>{{ speaker.marked ? 'star' : 'star_border' }}</mat-icon>
|
||||
</button>
|
||||
|
||||
<!-- delete button -->
|
||||
<button mat-icon-button matTooltip="{{ 'Remove' | translate }}" (click)="onDeleteButton(item)">
|
||||
<button mat-icon-button matTooltip="{{ 'Remove' | translate }}" (click)="onDeleteButton(speaker)">
|
||||
<mat-icon>close</mat-icon>
|
||||
</button>
|
||||
</span>
|
||||
@ -139,39 +139,39 @@
|
||||
|
||||
<mat-menu #speakerMenu="matMenu">
|
||||
<os-projector-button
|
||||
*ngIf="viewItem && projectors && projectors.length > 1"
|
||||
*ngIf="viewListOfSpeakers && projectors && projectors.length > 1"
|
||||
[object]="getClosSlide()"
|
||||
[menuItem]="true"
|
||||
text="Current list of speakers (as slide)"
|
||||
></os-projector-button>
|
||||
|
||||
<os-projector-button
|
||||
*ngIf="viewItem"
|
||||
[object]="viewItem.listOfSpeakersSlide"
|
||||
*ngIf="viewListOfSpeakers"
|
||||
[object]="viewListOfSpeakers"
|
||||
[menuItem]="true"
|
||||
text="List of speakers"
|
||||
></os-projector-button>
|
||||
|
||||
<os-projector-button
|
||||
*ngIf="viewItem"
|
||||
[object]="viewItem.contentObject"
|
||||
*ngIf="viewListOfSpeakers"
|
||||
[object]="viewListOfSpeakers.contentObject"
|
||||
[menuItem]="true"
|
||||
[text]="getContentObjectProjectorButtonText()"
|
||||
></os-projector-button>
|
||||
|
||||
<button mat-menu-item *ngIf="closedList" (click)="openSpeakerList()">
|
||||
<button mat-menu-item *ngIf="isListOfSpeakersClosed" (click)="openSpeakerList()">
|
||||
<mat-icon>mic</mat-icon>
|
||||
<span translate>Open list of speakers</span>
|
||||
</button>
|
||||
|
||||
<button mat-menu-item *ngIf="!closedList" (click)="closeSpeakerList()">
|
||||
<button mat-menu-item *ngIf="!isListOfSpeakersClosed" (click)="closeSpeakerList()">
|
||||
<mat-icon>mic_off</mat-icon>
|
||||
<span translate>Close list of speakers</span>
|
||||
</button>
|
||||
|
||||
<mat-divider *ngIf="!emptyList"></mat-divider>
|
||||
<mat-divider *ngIf="!isListOfSpeakersEmpty"></mat-divider>
|
||||
|
||||
<button mat-menu-item (click)="clearSpeakerList()" *ngIf="!emptyList" class="red-warning-text">
|
||||
<button mat-menu-item (click)="clearSpeakerList()" *ngIf="!isListOfSpeakersEmpty" class="red-warning-text">
|
||||
<mat-icon>delete</mat-icon>
|
||||
<span translate>Remove all speakers</span>
|
||||
</button>
|
||||
|
@ -11,19 +11,17 @@ import { BaseViewComponent } from 'app/site/base/base-view';
|
||||
import { OperatorService } from 'app/core/core-services/operator.service';
|
||||
import { ProjectorRepositoryService } from 'app/core/repositories/projector/projector-repository.service';
|
||||
import { PromptService } from 'app/core/ui-services/prompt.service';
|
||||
import { SpeakerState } from 'app/shared/models/agenda/speaker';
|
||||
import { SpeakerRepositoryService } from 'app/core/repositories/agenda/speaker-repository.service';
|
||||
import { ViewItem } from '../../models/view-item';
|
||||
import { ViewSpeaker } from '../../models/view-speaker';
|
||||
import { ViewSpeaker, SpeakerState } from '../../models/view-speaker';
|
||||
import { ViewProjector } from 'app/site/projector/models/view-projector';
|
||||
import { ViewUser } from 'app/site/users/models/view-user';
|
||||
import { UserRepositoryService } from 'app/core/repositories/users/user-repository.service';
|
||||
import { DurationService } from 'app/core/ui-services/duration.service';
|
||||
import { CurrentAgendaItemService } from 'app/site/projector/services/current-agenda-item.service';
|
||||
import { ItemRepositoryService } from 'app/core/repositories/agenda/item-repository.service';
|
||||
import { CollectionStringMapperService } from 'app/core/core-services/collection-string-mapper.service';
|
||||
import { CurrentListOfSpeakersService } from 'app/site/projector/services/current-agenda-item.service';
|
||||
import { CurrentListOfSpeakersSlideService } from 'app/site/projector/services/current-list-of-of-speakers-slide.service';
|
||||
import { ProjectorElementBuildDeskriptor } from 'app/site/base/projectable';
|
||||
import { ListOfSpeakersRepositoryService } from 'app/core/repositories/agenda/list-of-speakers-repository.service';
|
||||
import { ViewListOfSpeakers } from '../../models/view-list-of-speakers';
|
||||
|
||||
/**
|
||||
* The list of speakers for agenda items.
|
||||
@ -37,12 +35,12 @@ export class ListOfSpeakersComponent extends BaseViewComponent implements OnInit
|
||||
/**
|
||||
* Determine if the user is viewing the current list if speakers
|
||||
*/
|
||||
public currentListOfSpeakers = false;
|
||||
public isCurrentListOfSpeakers = false;
|
||||
|
||||
/**
|
||||
* Holds the view item to the given topic
|
||||
*/
|
||||
public viewItem: ViewItem;
|
||||
public viewListOfSpeakers: ViewListOfSpeakers;
|
||||
|
||||
/**
|
||||
* Holds the speakers
|
||||
@ -80,19 +78,19 @@ export class ListOfSpeakersComponent extends BaseViewComponent implements OnInit
|
||||
public addSpeakerForm: FormGroup;
|
||||
|
||||
/**
|
||||
* @returns true if the items' speaker list is currently not open
|
||||
* @returns true if the list of speakers list is currently closed
|
||||
*/
|
||||
public get closedList(): boolean {
|
||||
return this.viewItem && this.viewItem.item.speaker_list_closed;
|
||||
public get isListOfSpeakersClosed(): boolean {
|
||||
return this.viewListOfSpeakers && this.viewListOfSpeakers.closed;
|
||||
}
|
||||
|
||||
public get emptyList(): boolean {
|
||||
public get isListOfSpeakersEmpty(): boolean {
|
||||
if (this.speakers && this.speakers.length) {
|
||||
return false;
|
||||
} else if (this.finishedSpeakers && this.finishedSpeakers.length) {
|
||||
return false;
|
||||
}
|
||||
return this.activeSpeaker ? false : true;
|
||||
return !this.activeSpeaker;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -100,11 +98,10 @@ export class ListOfSpeakersComponent extends BaseViewComponent implements OnInit
|
||||
*/
|
||||
private closReferenceProjectorId: number | null;
|
||||
|
||||
private closItemSubscription: Subscription | null;
|
||||
private closSubscription: Subscription | null;
|
||||
|
||||
/**
|
||||
* Constructor for speaker list component. Generates the forms and subscribes
|
||||
* to the {@link currentListOfSpeakers}
|
||||
* Constructor for speaker list component. Generates the forms.
|
||||
*
|
||||
* @param title
|
||||
* @param translate
|
||||
@ -112,43 +109,29 @@ export class ListOfSpeakersComponent extends BaseViewComponent implements OnInit
|
||||
* @param projectorRepo
|
||||
* @param route Angulars ActivatedRoute
|
||||
* @param DS the DataStore
|
||||
* @param repo Repository for speakers
|
||||
* @param itemRepo Repository for agendaItems
|
||||
* @param op the current operator
|
||||
* @param listOfSpeakersRepo Repository for list of speakers
|
||||
* @param operator the current operator
|
||||
* @param promptService
|
||||
* @param currentAgendaItemService
|
||||
* @param currentListOfSpeakersService
|
||||
* @param durationService helper for speech duration display
|
||||
*/
|
||||
public constructor(
|
||||
title: Title,
|
||||
protected translate: TranslateService, // protected required for ng-translate-extract
|
||||
snackBar: MatSnackBar,
|
||||
projectorRepo: ProjectorRepositoryService,
|
||||
private projectorRepo: ProjectorRepositoryService,
|
||||
private route: ActivatedRoute,
|
||||
private repo: SpeakerRepositoryService,
|
||||
private itemRepo: ItemRepositoryService,
|
||||
private op: OperatorService,
|
||||
private listOfSpeakersRepo: ListOfSpeakersRepositoryService,
|
||||
private operator: OperatorService,
|
||||
private promptService: PromptService,
|
||||
private currentAgendaItemService: CurrentAgendaItemService,
|
||||
private currentListOfSpeakersService: CurrentListOfSpeakersService,
|
||||
private durationService: DurationService,
|
||||
private userRepository: UserRepositoryService,
|
||||
private collectionStringMapper: CollectionStringMapperService,
|
||||
private currentListOfSpeakersSlideService: CurrentListOfSpeakersSlideService
|
||||
) {
|
||||
super(title, translate, snackBar);
|
||||
this.isCurrentListOfSpeakers();
|
||||
this.addSpeakerForm = new FormGroup({ user_id: new FormControl([]) });
|
||||
|
||||
if (this.currentListOfSpeakers) {
|
||||
this.projectors = projectorRepo.getSortedViewModelList();
|
||||
this.updateClosProjector();
|
||||
projectorRepo.getViewModelListObservable().subscribe(newProjectors => {
|
||||
this.projectors = newProjectors;
|
||||
this.updateClosProjector();
|
||||
});
|
||||
} else {
|
||||
this.getItemByUrl();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -158,6 +141,24 @@ export class ListOfSpeakersComponent extends BaseViewComponent implements OnInit
|
||||
* React to form changes
|
||||
*/
|
||||
public ngOnInit(): void {
|
||||
// Check, if we are on the current list of speakers.
|
||||
this.isCurrentListOfSpeakers =
|
||||
this.route.snapshot.url.length > 0
|
||||
? this.route.snapshot.url[this.route.snapshot.url.length - 1].path === 'speakers'
|
||||
: true;
|
||||
|
||||
if (this.isCurrentListOfSpeakers) {
|
||||
this.projectors = this.projectorRepo.getSortedViewModelList();
|
||||
this.updateClosProjector();
|
||||
this.projectorRepo.getViewModelListObservable().subscribe(newProjectors => {
|
||||
this.projectors = newProjectors;
|
||||
this.updateClosProjector();
|
||||
});
|
||||
} else {
|
||||
const id = +this.route.snapshot.url[this.route.snapshot.url.length - 1].path;
|
||||
this.setListOfSpeakersId(id);
|
||||
}
|
||||
|
||||
// load and observe users
|
||||
this.users = this.userRepository.getViewModelListBehaviorSubject();
|
||||
|
||||
@ -171,16 +172,7 @@ export class ListOfSpeakersComponent extends BaseViewComponent implements OnInit
|
||||
}
|
||||
|
||||
public opCanManage(): boolean {
|
||||
return this.op.hasPerms('agenda.can_manage_list_of_speakers');
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the URL to determine a current list of Speakers
|
||||
*/
|
||||
private isCurrentListOfSpeakers(): void {
|
||||
if (this.route.snapshot.url[0]) {
|
||||
this.currentListOfSpeakers = this.route.snapshot.url[0].path === 'speakers';
|
||||
}
|
||||
return this.operator.hasPerms('agenda.can_manage_list_of_speakers');
|
||||
}
|
||||
|
||||
/**
|
||||
@ -198,14 +190,14 @@ export class ListOfSpeakersComponent extends BaseViewComponent implements OnInit
|
||||
|
||||
if (this.projectorSubscription) {
|
||||
this.projectorSubscription.unsubscribe();
|
||||
this.viewItem = null;
|
||||
this.viewListOfSpeakers = null;
|
||||
}
|
||||
|
||||
this.projectorSubscription = this.currentAgendaItemService
|
||||
.getAgendaItemObservable(referenceProjector)
|
||||
.subscribe(item => {
|
||||
if (item) {
|
||||
this.setSpeakerList(item.id);
|
||||
this.projectorSubscription = this.currentListOfSpeakersService
|
||||
.getListOfSpeakersObservable(referenceProjector)
|
||||
.subscribe(listOfSpeakers => {
|
||||
if (listOfSpeakers) {
|
||||
this.setListOfSpeakersId(listOfSpeakers.id);
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -218,31 +210,20 @@ export class ListOfSpeakersComponent extends BaseViewComponent implements OnInit
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract the ID from the url
|
||||
* Determine whether the speaker list belongs to a motion or a topic
|
||||
*/
|
||||
private getItemByUrl(): void {
|
||||
const id = +this.route.snapshot.url[0];
|
||||
this.setSpeakerList(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the current item as list of speakers
|
||||
* Sets the current list of speakers id to show
|
||||
*
|
||||
* @param item the item to use as List of Speakers
|
||||
* @param id the list of speakers id
|
||||
*/
|
||||
private setSpeakerList(id: number): void {
|
||||
if (this.closItemSubscription) {
|
||||
this.closItemSubscription.unsubscribe();
|
||||
private setListOfSpeakersId(id: number): void {
|
||||
if (this.closSubscription) {
|
||||
this.closSubscription.unsubscribe();
|
||||
}
|
||||
|
||||
this.closItemSubscription = this.itemRepo.getViewModelObservable(id).subscribe(newAgendaItem => {
|
||||
if (newAgendaItem) {
|
||||
this.viewItem = newAgendaItem;
|
||||
const allSpeakers = this.repo.createSpeakerList(newAgendaItem.item);
|
||||
this.closSubscription = this.listOfSpeakersRepo.getViewModelObservable(id).subscribe(listOfSpeakers => {
|
||||
if (listOfSpeakers) {
|
||||
this.viewListOfSpeakers = listOfSpeakers;
|
||||
const allSpeakers = this.viewListOfSpeakers.speakers.sort((a, b) => a.weight - b.weight);
|
||||
this.speakers = allSpeakers.filter(speaker => speaker.state === SpeakerState.WAITING);
|
||||
// Since the speaker repository is not a normal repository, sorting cannot be handled there
|
||||
this.speakers.sort((a: ViewSpeaker, b: ViewSpeaker) => a.weight - b.weight);
|
||||
this.finishedSpeakers = allSpeakers.filter(speaker => speaker.state === SpeakerState.FINISHED);
|
||||
|
||||
// convert begin time to date and sort
|
||||
@ -259,11 +240,11 @@ export class ListOfSpeakersComponent extends BaseViewComponent implements OnInit
|
||||
|
||||
/**
|
||||
* @returns the verbose name of the model of the content object from viewItem.
|
||||
* If a motion is the current content object, "Motion" will be the returned value.
|
||||
* E.g. if a motion is the current content object, "Motion" will be the returned value.
|
||||
*/
|
||||
public getContentObjectProjectorButtonText(): string {
|
||||
const verboseName = this.collectionStringMapper
|
||||
.getRepository(this.viewItem.item.content_object.collection)
|
||||
.getRepository(this.viewListOfSpeakers.listOfSpeakers.content_object.collection)
|
||||
.getVerboseName();
|
||||
return verboseName;
|
||||
}
|
||||
@ -274,7 +255,9 @@ export class ListOfSpeakersComponent extends BaseViewComponent implements OnInit
|
||||
* @param userId the user id to add to the list. No parameter adds the operators user as speaker.
|
||||
*/
|
||||
public addNewSpeaker(userId?: number): void {
|
||||
this.repo.create(userId, this.viewItem).then(() => this.addSpeakerForm.reset(), this.raiseError);
|
||||
this.listOfSpeakersRepo
|
||||
.createSpeaker(this.viewListOfSpeakers, userId)
|
||||
.then(() => this.addSpeakerForm.reset(), this.raiseError);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -285,32 +268,34 @@ export class ListOfSpeakersComponent extends BaseViewComponent implements OnInit
|
||||
public onSortingChange(listInNewOrder: ViewSpeaker[]): void {
|
||||
// extract the ids from the ViewSpeaker array
|
||||
const userIds = listInNewOrder.map(speaker => speaker.id);
|
||||
this.repo.sortSpeakers(userIds, this.viewItem.item).then(null, this.raiseError);
|
||||
this.listOfSpeakersRepo.sortSpeakers(this.viewListOfSpeakers, userIds).then(null, this.raiseError);
|
||||
}
|
||||
|
||||
/**
|
||||
* Click on the mic button to mark a speaker as speaking
|
||||
*
|
||||
* @param item the speaker marked in the list
|
||||
* @param speaker the speaker marked in the list
|
||||
*/
|
||||
public onStartButton(item: ViewSpeaker): void {
|
||||
this.repo.startSpeaker(item.id, this.viewItem).then(null, this.raiseError);
|
||||
public onStartButton(speaker: ViewSpeaker): void {
|
||||
this.listOfSpeakersRepo.startSpeaker(this.viewListOfSpeakers, speaker).then(null, this.raiseError);
|
||||
}
|
||||
|
||||
/**
|
||||
* Click on the mic-cross button
|
||||
*/
|
||||
public onStopButton(): void {
|
||||
this.repo.stopCurrentSpeaker(this.viewItem).then(null, this.raiseError);
|
||||
this.listOfSpeakersRepo.stopCurrentSpeaker(this.viewListOfSpeakers).then(null, this.raiseError);
|
||||
}
|
||||
|
||||
/**
|
||||
* Click on the star button
|
||||
* Click on the star button. Toggles the marked attribute.
|
||||
*
|
||||
* @param item
|
||||
* @param speaker The speaker clicked on.
|
||||
*/
|
||||
public onMarkButton(item: ViewSpeaker): void {
|
||||
this.repo.markSpeaker(item.user.id, !item.marked, this.viewItem).then(null, this.raiseError);
|
||||
public onMarkButton(speaker: ViewSpeaker): void {
|
||||
this.listOfSpeakersRepo
|
||||
.markSpeaker(this.viewListOfSpeakers, speaker, !speaker.marked)
|
||||
.then(null, this.raiseError);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -319,7 +304,9 @@ export class ListOfSpeakersComponent extends BaseViewComponent implements OnInit
|
||||
* @param speaker
|
||||
*/
|
||||
public onDeleteButton(speaker?: ViewSpeaker): void {
|
||||
this.repo.delete(this.viewItem, speaker ? speaker.id : null).then(null, this.raiseError);
|
||||
this.listOfSpeakersRepo
|
||||
.delete(this.viewListOfSpeakers, speaker ? speaker.id : null)
|
||||
.then(null, this.raiseError);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -328,7 +315,7 @@ export class ListOfSpeakersComponent extends BaseViewComponent implements OnInit
|
||||
* @returns whether or not the current operator is in the list
|
||||
*/
|
||||
public isOpInList(): boolean {
|
||||
return this.speakers.some(speaker => speaker.user.id === this.op.user.id);
|
||||
return this.speakers.some(speaker => speaker.userId === this.operator.user.id);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -342,20 +329,24 @@ export class ListOfSpeakersComponent extends BaseViewComponent implements OnInit
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the current speaker list
|
||||
* Closes the current list of speakers
|
||||
*/
|
||||
public closeSpeakerList(): Promise<void> {
|
||||
if (!this.viewItem.item.speaker_list_closed) {
|
||||
return this.itemRepo.update({ speaker_list_closed: true }, this.viewItem);
|
||||
if (!this.viewListOfSpeakers.closed) {
|
||||
return this.listOfSpeakersRepo
|
||||
.update({ closed: true }, this.viewListOfSpeakers)
|
||||
.then(null, this.raiseError);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens the speaker list for the current item
|
||||
* Opens the list of speaker for the current item
|
||||
*/
|
||||
public openSpeakerList(): Promise<void> {
|
||||
if (this.viewItem.item.speaker_list_closed) {
|
||||
return this.itemRepo.update({ speaker_list_closed: false }, this.viewItem);
|
||||
if (this.viewListOfSpeakers.closed) {
|
||||
return this.listOfSpeakersRepo
|
||||
.update({ closed: false }, this.viewListOfSpeakers)
|
||||
.then(null, this.raiseError);
|
||||
}
|
||||
}
|
||||
|
||||
@ -368,7 +359,7 @@ export class ListOfSpeakersComponent extends BaseViewComponent implements OnInit
|
||||
'Are you sure you want to delete all speakers from this list of speakers?'
|
||||
);
|
||||
if (await this.promptService.open(title, null)) {
|
||||
this.repo.deleteAllSpeakers(this.viewItem);
|
||||
this.listOfSpeakersRepo.deleteAllSpeakers(this.viewListOfSpeakers);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -106,10 +106,7 @@
|
||||
</mat-card>
|
||||
|
||||
<mat-menu #topicExtraMenu="matMenu">
|
||||
<button mat-menu-item *ngIf="topic" [routerLink]="getSpeakerLink()">
|
||||
<mat-icon>mic</mat-icon>
|
||||
<span translate>List of speakers</span>
|
||||
</button>
|
||||
<os-speaker-button [object]="topic" [menuItem]="true"></os-speaker-button>
|
||||
<div *osPerms="'agenda.can_manage'">
|
||||
<mat-divider></mat-divider>
|
||||
<button mat-menu-item class="red-warning-text" (click)="onDeleteButton()">
|
||||
|
@ -8,7 +8,7 @@ import { TranslateService } from '@ngx-translate/core';
|
||||
|
||||
import { BaseViewComponent } from 'app/site/base/base-view';
|
||||
import { PromptService } from 'app/core/ui-services/prompt.service';
|
||||
import { TopicRepositoryService } from 'app/core/repositories/agenda/topic-repository.service';
|
||||
import { TopicRepositoryService } from 'app/core/repositories/topics/topic-repository.service';
|
||||
import { ViewTopic } from '../../models/view-topic';
|
||||
import { OperatorService } from 'app/core/core-services/operator.service';
|
||||
import { BehaviorSubject } from 'rxjs';
|
||||
@ -195,20 +195,6 @@ export class TopicDetailComponent extends BaseViewComponent {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the absolute path to the corresponding list of speakers
|
||||
*
|
||||
* @returns the link to the list of speakers as string
|
||||
*/
|
||||
public getSpeakerLink(): string {
|
||||
if (!this.newTopic && this.topic) {
|
||||
const item = this.topic.getAgendaItem();
|
||||
if (item) {
|
||||
return `/agenda/${item.id}/speakers`;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler for the delete button. Uses the PromptService
|
||||
*/
|
||||
|
@ -1,6 +1,5 @@
|
||||
import { CreateTopic } from './create-topic';
|
||||
import { ViewTopic } from './view-topic';
|
||||
import { Topic } from 'app/shared/models/topics/topic';
|
||||
|
||||
/**
|
||||
* View model for Topic('Agenda item') creation.
|
||||
@ -8,7 +7,7 @@ import { Topic } from 'app/shared/models/topics/topic';
|
||||
*/
|
||||
export class ViewCreateTopic extends ViewTopic {
|
||||
public get topic(): CreateTopic {
|
||||
return this._topic as CreateTopic;
|
||||
return this._model as CreateTopic;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -109,10 +108,6 @@ export class ViewCreateTopic extends ViewTopic {
|
||||
super(topic);
|
||||
}
|
||||
|
||||
public getModel(): Topic {
|
||||
return super.getModel();
|
||||
}
|
||||
|
||||
public getVerboseName = () => {
|
||||
throw new Error('This should not be used');
|
||||
};
|
||||
|
@ -1,25 +1,23 @@
|
||||
import { BaseViewModel } from '../../base/base-view-model';
|
||||
import { Item, itemVisibilityChoices } from 'app/shared/models/agenda/item';
|
||||
import { Speaker, SpeakerState } from 'app/shared/models/agenda/speaker';
|
||||
import { BaseAgendaViewModel, isAgendaBaseModel } from 'app/site/base/base-agenda-view-model';
|
||||
import { ProjectorElementBuildDeskriptor } from 'app/site/base/projectable';
|
||||
import {
|
||||
BaseViewModelWithAgendaItem,
|
||||
isBaseViewModelWithAgendaItem
|
||||
} from 'app/site/base/base-view-model-with-agenda-item';
|
||||
import { BaseViewModelWithContentObject } from 'app/site/base/base-view-model-with-content-object';
|
||||
import { ContentObject } from 'app/shared/models/base/content-object';
|
||||
|
||||
export class ViewItem extends BaseViewModel {
|
||||
export interface ItemTitleInformation {
|
||||
contentObject: BaseViewModelWithAgendaItem;
|
||||
contentObjectData: ContentObject;
|
||||
title_information: object;
|
||||
}
|
||||
|
||||
export class ViewItem extends BaseViewModelWithContentObject<Item, BaseViewModelWithAgendaItem>
|
||||
implements ItemTitleInformation {
|
||||
public static COLLECTIONSTRING = Item.COLLECTIONSTRING;
|
||||
|
||||
private _item: Item;
|
||||
private _contentObject: BaseAgendaViewModel;
|
||||
|
||||
public get item(): Item {
|
||||
return this._item;
|
||||
}
|
||||
|
||||
public get contentObject(): BaseAgendaViewModel {
|
||||
return this._contentObject;
|
||||
}
|
||||
|
||||
public get id(): number {
|
||||
return this.item.id;
|
||||
return this._model;
|
||||
}
|
||||
|
||||
public get itemNumber(): string {
|
||||
@ -34,13 +32,6 @@ export class ViewItem extends BaseViewModel {
|
||||
return this.item.duration;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the amount of waiting speakers
|
||||
*/
|
||||
public get waitingSpeakerAmount(): number {
|
||||
return this.item.speakers.filter(speaker => speaker.state === SpeakerState.WAITING).length;
|
||||
}
|
||||
|
||||
public get type(): number {
|
||||
return this.item.type;
|
||||
}
|
||||
@ -81,13 +72,6 @@ export class ViewItem extends BaseViewModel {
|
||||
return type ? type.csvName : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO: make the repository set the ViewSpeakers here.
|
||||
*/
|
||||
public get speakers(): Speaker[] {
|
||||
return this.item.speakers;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns the weight the server assigns to that item. Mostly useful for sorting within
|
||||
* it's own hierarchy level (items sharing a parent)
|
||||
@ -103,46 +87,7 @@ export class ViewItem extends BaseViewModel {
|
||||
return this.item.parent_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is set by the repository
|
||||
*/
|
||||
public getVerboseName: () => string;
|
||||
public getTitle: () => string;
|
||||
public getListTitle: () => string;
|
||||
|
||||
public listOfSpeakersSlide: ProjectorElementBuildDeskriptor = {
|
||||
getBasicProjectorElement: options => ({
|
||||
name: 'agenda/list-of-speakers',
|
||||
id: this.id,
|
||||
getIdentifiers: () => ['name', 'id']
|
||||
}),
|
||||
slideOptions: [],
|
||||
projectionDefaultName: 'agenda_list_of_speakers',
|
||||
getDialogTitle: () => this.getTitle()
|
||||
};
|
||||
|
||||
public constructor(item: Item, contentObject: BaseAgendaViewModel) {
|
||||
super(Item.COLLECTIONSTRING);
|
||||
this._item = item;
|
||||
this._contentObject = contentObject;
|
||||
}
|
||||
|
||||
public getModel(): Item {
|
||||
return this.item;
|
||||
}
|
||||
|
||||
public updateDependencies(update: BaseViewModel): boolean {
|
||||
if (
|
||||
update &&
|
||||
update.collectionString === this.item.content_object.collection &&
|
||||
update.id === this.item.content_object.id
|
||||
) {
|
||||
if (!isAgendaBaseModel(update)) {
|
||||
throw new Error('The item is not an BaseAgendaViewModel:' + update);
|
||||
}
|
||||
this._contentObject = update as BaseAgendaViewModel;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
public constructor(item: Item, contentObject?: BaseViewModelWithAgendaItem) {
|
||||
super(Item.COLLECTIONSTRING, item, isBaseViewModelWithAgendaItem, 'BaseViewModelWithAgendaItem', contentObject);
|
||||
}
|
||||
}
|
||||
|
92
client/src/app/site/agenda/models/view-list-of-speakers.ts
Normal file
92
client/src/app/site/agenda/models/view-list-of-speakers.ts
Normal file
@ -0,0 +1,92 @@
|
||||
import { BaseViewModel } from '../../base/base-view-model';
|
||||
import { Item } from 'app/shared/models/agenda/item';
|
||||
import { ProjectorElementBuildDeskriptor, Projectable } from 'app/site/base/projectable';
|
||||
import { ListOfSpeakers } from 'app/shared/models/agenda/list-of-speakers';
|
||||
import { ViewSpeaker, SpeakerState } from './view-speaker';
|
||||
import {
|
||||
BaseViewModelWithListOfSpeakers,
|
||||
isBaseViewModelWithListOfSpeakers
|
||||
} from 'app/site/base/base-view-model-with-list-of-speakers';
|
||||
import { ViewUser } from 'app/site/users/models/view-user';
|
||||
import { BaseViewModelWithContentObject } from 'app/site/base/base-view-model-with-content-object';
|
||||
import { ContentObject } from 'app/shared/models/base/content-object';
|
||||
|
||||
export interface ListOfSpeakersTitleInformation {
|
||||
contentObject: BaseViewModelWithListOfSpeakers;
|
||||
contentObjectData: ContentObject;
|
||||
title_information: object;
|
||||
}
|
||||
|
||||
export class ViewListOfSpeakers extends BaseViewModelWithContentObject<ListOfSpeakers, BaseViewModelWithListOfSpeakers>
|
||||
implements ListOfSpeakersTitleInformation, Projectable {
|
||||
public static COLLECTIONSTRING = ListOfSpeakers.COLLECTIONSTRING;
|
||||
|
||||
private _speakers?: ViewSpeaker[];
|
||||
|
||||
public get listOfSpeakers(): ListOfSpeakers {
|
||||
return this._model;
|
||||
}
|
||||
|
||||
public get speakers(): ViewSpeaker[] {
|
||||
return this._speakers;
|
||||
}
|
||||
|
||||
public get title_information(): object {
|
||||
return this.listOfSpeakers.title_information;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the amount of waiting speakers
|
||||
*/
|
||||
public get waitingSpeakerAmount(): number {
|
||||
return this.speakers.filter(speaker => speaker.state === SpeakerState.WAITING).length;
|
||||
}
|
||||
|
||||
public get closed(): boolean {
|
||||
return this.listOfSpeakers.closed;
|
||||
}
|
||||
|
||||
public get listOfSpeakersUrl(): string {
|
||||
return `/agenda/speakers/${this.id}`;
|
||||
}
|
||||
|
||||
public constructor(
|
||||
listOfSpeakers: ListOfSpeakers,
|
||||
speakers: ViewSpeaker[],
|
||||
contentObject?: BaseViewModelWithListOfSpeakers
|
||||
) {
|
||||
super(
|
||||
Item.COLLECTIONSTRING,
|
||||
listOfSpeakers,
|
||||
isBaseViewModelWithListOfSpeakers,
|
||||
'BaseViewModelWithListOfSpeakers',
|
||||
contentObject
|
||||
);
|
||||
this._speakers = speakers;
|
||||
}
|
||||
|
||||
public getProjectorTitle(): string {
|
||||
return this.getTitle();
|
||||
}
|
||||
|
||||
public getSlide(): ProjectorElementBuildDeskriptor {
|
||||
return {
|
||||
getBasicProjectorElement: options => ({
|
||||
name: 'agenda/list-of-speakers',
|
||||
id: this.id,
|
||||
getIdentifiers: () => ['name', 'id']
|
||||
}),
|
||||
slideOptions: [],
|
||||
projectionDefaultName: 'agenda_list_of_speakers',
|
||||
getDialogTitle: () => this.getTitle()
|
||||
};
|
||||
}
|
||||
|
||||
public updateDependencies(update: BaseViewModel): boolean {
|
||||
const updated = super.updateDependencies(update);
|
||||
if (!updated && update instanceof ViewUser) {
|
||||
return this.speakers.map(speaker => speaker.updateDependencies(update)).some(x => x);
|
||||
}
|
||||
return updated;
|
||||
}
|
||||
}
|
@ -1,14 +1,24 @@
|
||||
import { BaseViewModel } from 'app/site/base/base-view-model';
|
||||
import { Speaker, SpeakerState } from 'app/shared/models/agenda/speaker';
|
||||
import { Speaker } from 'app/shared/models/agenda/speaker';
|
||||
import { ViewUser } from 'app/site/users/models/view-user';
|
||||
import { User } from 'app/shared/models/users/user';
|
||||
import { Updateable } from 'app/site/base/updateable';
|
||||
import { Identifiable } from 'app/shared/models/base/identifiable';
|
||||
|
||||
/**
|
||||
* Determine the state of the speaker
|
||||
*/
|
||||
export enum SpeakerState {
|
||||
WAITING,
|
||||
CURRENT,
|
||||
FINISHED
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides "safe" access to a speaker with all it's components
|
||||
*/
|
||||
export class ViewSpeaker extends BaseViewModel {
|
||||
export class ViewSpeaker implements Updateable, Identifiable {
|
||||
private _speaker: Speaker;
|
||||
private _user: ViewUser | null;
|
||||
private _user?: ViewUser;
|
||||
|
||||
public get speaker(): Speaker {
|
||||
return this._speaker;
|
||||
@ -22,6 +32,10 @@ export class ViewSpeaker extends BaseViewModel {
|
||||
return this.speaker.id;
|
||||
}
|
||||
|
||||
public get userId(): number {
|
||||
return this.speaker.user_id;
|
||||
}
|
||||
|
||||
public get weight(): number {
|
||||
return this.speaker.weight;
|
||||
}
|
||||
@ -44,8 +58,20 @@ export class ViewSpeaker extends BaseViewModel {
|
||||
return this.speaker.end_time;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns
|
||||
* - waiting if there is no begin nor end time
|
||||
* - current if there is a begin time and not end time
|
||||
* - finished if there are both begin and end time
|
||||
*/
|
||||
public get state(): SpeakerState {
|
||||
return this.speaker.state;
|
||||
if (!this.begin_time && !this.end_time) {
|
||||
return SpeakerState.WAITING;
|
||||
} else if (this.begin_time && !this.end_time) {
|
||||
return SpeakerState.CURRENT;
|
||||
} else {
|
||||
return SpeakerState.FINISHED;
|
||||
}
|
||||
}
|
||||
|
||||
public get name(): string {
|
||||
@ -56,13 +82,7 @@ export class ViewSpeaker extends BaseViewModel {
|
||||
return this.user ? this.user.gender : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* This is set by the repository
|
||||
*/
|
||||
public getVerboseName;
|
||||
|
||||
public constructor(speaker: Speaker, user?: ViewUser) {
|
||||
super('TODO');
|
||||
this._speaker = speaker;
|
||||
this._user = user;
|
||||
}
|
||||
@ -71,13 +91,11 @@ export class ViewSpeaker extends BaseViewModel {
|
||||
return this.name;
|
||||
};
|
||||
|
||||
public getModel(): User {
|
||||
return this.user.user;
|
||||
public updateDependencies(update: BaseViewModel): boolean {
|
||||
if (update instanceof ViewUser && update.id === this.speaker.user_id) {
|
||||
this._user = update;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Speaker is not a base model,
|
||||
* @param update the incoming update
|
||||
*/
|
||||
public updateDependencies(update: BaseViewModel): void {}
|
||||
}
|
||||
|
@ -1,40 +1,33 @@
|
||||
import { Topic } from 'app/shared/models/topics/topic';
|
||||
import { BaseAgendaViewModel } from 'app/site/base/base-agenda-view-model';
|
||||
import { SearchRepresentation } from 'app/core/ui-services/search.service';
|
||||
import { ProjectorElementBuildDeskriptor } from 'app/site/base/projectable';
|
||||
import { ViewMediafile } from 'app/site/mediafiles/models/view-mediafile';
|
||||
import { ViewItem } from './view-item';
|
||||
import { BaseViewModel } from 'app/site/base/base-view-model';
|
||||
import { ViewListOfSpeakers } from './view-list-of-speakers';
|
||||
import { BaseViewModelWithAgendaItemAndListOfSpeakers } from 'app/site/base/base-view-model-with-agenda-item-and-list-of-speakers';
|
||||
import { TitleInformationWithAgendaItem } from 'app/site/base/base-view-model-with-agenda-item';
|
||||
|
||||
export interface TopicTitleInformation extends TitleInformationWithAgendaItem {
|
||||
title: string;
|
||||
agenda_item_number?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides "safe" access to topic with all it's components
|
||||
* @ignore
|
||||
*/
|
||||
export class ViewTopic extends BaseAgendaViewModel {
|
||||
export class ViewTopic extends BaseViewModelWithAgendaItemAndListOfSpeakers implements TopicTitleInformation {
|
||||
public static COLLECTIONSTRING = Topic.COLLECTIONSTRING;
|
||||
|
||||
protected _topic: Topic;
|
||||
private _attachments: ViewMediafile[];
|
||||
private _agendaItem: ViewItem;
|
||||
private _attachments?: ViewMediafile[];
|
||||
|
||||
public get topic(): Topic {
|
||||
return this._topic;
|
||||
return this._model;
|
||||
}
|
||||
|
||||
public get attachments(): ViewMediafile[] {
|
||||
return this._attachments;
|
||||
}
|
||||
|
||||
public get agendaItem(): ViewItem {
|
||||
return this._agendaItem;
|
||||
}
|
||||
|
||||
public get id(): number {
|
||||
return this.topic.id;
|
||||
}
|
||||
|
||||
public get agenda_item_id(): number {
|
||||
return this.topic.agenda_item_id;
|
||||
return this._attachments || [];
|
||||
}
|
||||
|
||||
public get attachments_id(): number[] {
|
||||
@ -49,34 +42,14 @@ export class ViewTopic extends BaseAgendaViewModel {
|
||||
return this.topic.text;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is set by the repository
|
||||
*/
|
||||
public getVerboseName;
|
||||
public getAgendaTitle;
|
||||
public getAgendaTitleWithType;
|
||||
|
||||
public constructor(topic: Topic, attachments?: ViewMediafile[], item?: ViewItem) {
|
||||
super(Topic.COLLECTIONSTRING);
|
||||
this._topic = topic;
|
||||
public constructor(
|
||||
topic: Topic,
|
||||
attachments?: ViewMediafile[],
|
||||
item?: ViewItem,
|
||||
listOfSpeakers?: ViewListOfSpeakers
|
||||
) {
|
||||
super(Topic.COLLECTIONSTRING, topic, item, listOfSpeakers);
|
||||
this._attachments = attachments;
|
||||
this._agendaItem = item;
|
||||
}
|
||||
|
||||
public getTitle = () => {
|
||||
if (this.agendaItem && this.agendaItem.itemNumber) {
|
||||
return this.agendaItem.itemNumber + ' · ' + this.title;
|
||||
} else {
|
||||
return this.title;
|
||||
}
|
||||
};
|
||||
|
||||
public getModel(): Topic {
|
||||
return this.topic;
|
||||
}
|
||||
|
||||
public getAgendaItem(): ViewItem {
|
||||
return this.agendaItem;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -118,6 +91,7 @@ export class ViewTopic extends BaseAgendaViewModel {
|
||||
}
|
||||
|
||||
public updateDependencies(update: BaseViewModel): void {
|
||||
super.updateDependencies(update);
|
||||
if (update instanceof ViewMediafile && this.attachments_id.includes(update.id)) {
|
||||
const attachmentIndex = this.attachments.findIndex(mediafile => mediafile.id === update.id);
|
||||
if (attachmentIndex < 0) {
|
||||
@ -126,8 +100,5 @@ export class ViewTopic extends BaseAgendaViewModel {
|
||||
this.attachments[attachmentIndex] = update;
|
||||
}
|
||||
}
|
||||
if (update instanceof ViewItem && this.agenda_item_id === update.id) {
|
||||
this._agendaItem = update;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -28,12 +28,7 @@
|
||||
<span translate>PDF</span>
|
||||
</button>
|
||||
<!-- List of speakers -->
|
||||
<div *ngIf="assignment.agendaItem">
|
||||
<button mat-menu-item [routerLink]="getSpeakerLink()" *osPerms="'agenda.can_see'">
|
||||
<mat-icon>mic</mat-icon>
|
||||
<span translate>List of speakers</span>
|
||||
</button>
|
||||
</div>
|
||||
<os-speaker-button [object]="assignment" [menuItem]="true"></os-speaker-button>
|
||||
</div>
|
||||
<!-- Project -->
|
||||
<os-projector-button [object]="assignment" [menuItem]="true"></os-projector-button>
|
||||
@ -62,7 +57,9 @@
|
||||
|
||||
<ng-template #metaInfoTemplate>
|
||||
<mat-card class="os-card " *ngIf="assignment">
|
||||
<h1>{{ assignment.getTitle() }}</h1>
|
||||
<div *ngIf="!editAssignment && assignment.getTitle">
|
||||
<h1>{{ assignment.getTitle() }}</h1>
|
||||
</div>
|
||||
<div *ngIf="assignment">
|
||||
<div *ngIf="assignment.assignment.description" [innerHTML]="assignment.assignment.description"></div>
|
||||
</div>
|
||||
@ -227,7 +224,7 @@
|
||||
matInput
|
||||
placeholder="{{ 'Title' | translate }}"
|
||||
formControlName="title"
|
||||
[value]="assignmentCopy.getTitle() || ''"
|
||||
[value]="assignmentCopy.title || ''"
|
||||
/>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
|
@ -501,11 +501,4 @@ export class AssignmentDetailComponent extends BaseViewComponent implements OnIn
|
||||
.sortCandidates(listInNewOrder.map(relatedUser => relatedUser.id), this.assignment)
|
||||
.then(null, this.raiseError);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the link to the list of speakers associated with the assignment
|
||||
*/
|
||||
public getSpeakerLink(): string {
|
||||
return `/agenda/${this.assignment.agendaItem.id}/speakers`;
|
||||
}
|
||||
}
|
||||
|
@ -95,6 +95,10 @@ export class ViewAssignmentPoll implements Identifiable, Updateable, Projectable
|
||||
return this.getTitle();
|
||||
}
|
||||
|
||||
public getProjectorTitle(): string {
|
||||
return this.getTitle();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a copy with deep-copy on all changing numerical values,
|
||||
* but intact uncopied references to the users
|
||||
|
@ -1,5 +1,4 @@
|
||||
import { Assignment } from 'app/shared/models/assignments/assignment';
|
||||
import { BaseAgendaViewModel } from 'app/site/base/base-agenda-view-model';
|
||||
import { SearchRepresentation } from 'app/core/ui-services/search.service';
|
||||
import { ProjectorElementBuildDeskriptor } from 'app/site/base/projectable';
|
||||
import { ViewUser } from 'app/site/users/models/view-user';
|
||||
@ -9,6 +8,13 @@ import { BaseViewModel } from 'app/site/base/base-view-model';
|
||||
import { ViewAssignmentRelatedUser } from './view-assignment-related-user';
|
||||
import { ViewAssignmentPoll } from './view-assignment-poll';
|
||||
import { ViewMediafile } from 'app/site/mediafiles/models/view-mediafile';
|
||||
import { BaseViewModelWithAgendaItemAndListOfSpeakers } from 'app/site/base/base-view-model-with-agenda-item-and-list-of-speakers';
|
||||
import { ViewListOfSpeakers } from 'app/site/agenda/models/view-list-of-speakers';
|
||||
import { TitleInformationWithAgendaItem } from 'app/site/base/base-view-model-with-agenda-item';
|
||||
|
||||
export interface AssignmentTitleInformation extends TitleInformationWithAgendaItem {
|
||||
title: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* A constant containing all possible assignment phases and their different
|
||||
@ -33,22 +39,17 @@ export const AssignmentPhases: { name: string; value: number; display_name: stri
|
||||
}
|
||||
];
|
||||
|
||||
export class ViewAssignment extends BaseAgendaViewModel {
|
||||
export class ViewAssignment extends BaseViewModelWithAgendaItemAndListOfSpeakers<Assignment>
|
||||
implements AssignmentTitleInformation {
|
||||
public static COLLECTIONSTRING = Assignment.COLLECTIONSTRING;
|
||||
|
||||
private _assignment: Assignment;
|
||||
private _assignmentRelatedUsers: ViewAssignmentRelatedUser[];
|
||||
private _assignmentPolls: ViewAssignmentPoll[];
|
||||
private _agendaItem?: ViewItem;
|
||||
private _tags?: ViewTag[];
|
||||
private _attachments?: ViewMediafile[];
|
||||
|
||||
public get id(): number {
|
||||
return this._assignment ? this._assignment.id : null;
|
||||
}
|
||||
|
||||
public get assignment(): Assignment {
|
||||
return this._assignment;
|
||||
return this._model;
|
||||
}
|
||||
|
||||
public get polls(): ViewAssignmentPoll[] {
|
||||
@ -75,10 +76,6 @@ export class ViewAssignment extends BaseAgendaViewModel {
|
||||
return this._assignmentRelatedUsers;
|
||||
}
|
||||
|
||||
public get agendaItem(): ViewItem | null {
|
||||
return this._agendaItem;
|
||||
}
|
||||
|
||||
public get tags(): ViewTag[] {
|
||||
return this._tags || [];
|
||||
}
|
||||
@ -123,35 +120,26 @@ export class ViewAssignment extends BaseAgendaViewModel {
|
||||
return this._assignmentRelatedUsers ? this._assignmentRelatedUsers.length : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor. Is set by the repository
|
||||
*/
|
||||
public getVerboseName;
|
||||
public getAgendaTitle;
|
||||
public getAgendaTitleWithType;
|
||||
|
||||
public constructor(
|
||||
assignment: Assignment,
|
||||
assignmentRelatedUsers: ViewAssignmentRelatedUser[],
|
||||
assignmentPolls: ViewAssignmentPoll[],
|
||||
agendaItem?: ViewItem,
|
||||
item?: ViewItem,
|
||||
listOfSpeakers?: ViewListOfSpeakers,
|
||||
tags?: ViewTag[],
|
||||
attachments?: ViewMediafile[]
|
||||
) {
|
||||
super(Assignment.COLLECTIONSTRING);
|
||||
super(Assignment.COLLECTIONSTRING, assignment, item, listOfSpeakers);
|
||||
|
||||
this._assignment = assignment;
|
||||
this._assignmentRelatedUsers = assignmentRelatedUsers;
|
||||
this._assignmentPolls = assignmentPolls;
|
||||
this._agendaItem = agendaItem;
|
||||
this._tags = tags;
|
||||
this._attachments = attachments;
|
||||
}
|
||||
|
||||
public updateDependencies(update: BaseViewModel): void {
|
||||
if (update instanceof ViewItem && update.id === this.assignment.agenda_item_id) {
|
||||
this._agendaItem = update;
|
||||
} else if (update instanceof ViewTag && this.assignment.tags_id.includes(update.id)) {
|
||||
super.updateDependencies(update);
|
||||
if (update instanceof ViewTag && this.assignment.tags_id.includes(update.id)) {
|
||||
const tagIndex = this._tags.findIndex(_tag => _tag.id === update.id);
|
||||
if (tagIndex < 0) {
|
||||
this._tags.push(update);
|
||||
@ -171,18 +159,6 @@ export class ViewAssignment extends BaseAgendaViewModel {
|
||||
}
|
||||
}
|
||||
|
||||
public getAgendaItem(): ViewItem {
|
||||
return this.agendaItem;
|
||||
}
|
||||
|
||||
public getTitle = () => {
|
||||
return this.title;
|
||||
};
|
||||
|
||||
public getModel(): Assignment {
|
||||
return this.assignment;
|
||||
}
|
||||
|
||||
public formatForSearch(): SearchRepresentation {
|
||||
return [this.title];
|
||||
}
|
||||
|
@ -1,27 +0,0 @@
|
||||
import { DetailNavigable } from '../../shared/models/base/detail-navigable';
|
||||
import { ViewItem } from '../agenda/models/view-item';
|
||||
|
||||
/**
|
||||
* An Interface for all extra information needed for content objects of items.
|
||||
*/
|
||||
export interface AgendaInformation extends DetailNavigable {
|
||||
/**
|
||||
* Should return the title for the agenda list view.
|
||||
*/
|
||||
getAgendaTitle: () => string;
|
||||
|
||||
/**
|
||||
* Should return the title for the list of speakers view.
|
||||
*/
|
||||
getAgendaTitleWithType: () => string;
|
||||
|
||||
/**
|
||||
* An (optional) descriptive text to be exported in the CSV.
|
||||
*/
|
||||
getCSVExportText(): string;
|
||||
|
||||
/**
|
||||
* Get access to the agenda item
|
||||
*/
|
||||
getAgendaItem(): ViewItem;
|
||||
}
|
@ -1,55 +0,0 @@
|
||||
import { AgendaInformation } from 'app/site/base/agenda-information';
|
||||
import { BaseProjectableViewModel } from './base-projectable-view-model';
|
||||
import { SearchRepresentation } from 'app/core/ui-services/search.service';
|
||||
import { ViewItem } from '../agenda/models/view-item';
|
||||
|
||||
export function isAgendaBaseModel(obj: object): obj is BaseAgendaViewModel {
|
||||
const agendaViewModel = <BaseAgendaViewModel>obj;
|
||||
return (
|
||||
agendaViewModel.getAgendaTitle !== undefined &&
|
||||
agendaViewModel.getAgendaTitleWithType !== undefined &&
|
||||
agendaViewModel.getCSVExportText !== undefined &&
|
||||
agendaViewModel.getAgendaItem !== undefined &&
|
||||
agendaViewModel.getDetailStateURL !== undefined
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Base view class for projectable models.
|
||||
*/
|
||||
export abstract class BaseAgendaViewModel extends BaseProjectableViewModel implements AgendaInformation {
|
||||
/**
|
||||
* @returns the contained agenda item
|
||||
*/
|
||||
public abstract getAgendaItem(): ViewItem;
|
||||
|
||||
/**
|
||||
* @returns the agenda title
|
||||
*/
|
||||
public getAgendaTitle = () => {
|
||||
return this.getTitle();
|
||||
};
|
||||
|
||||
/**
|
||||
* @return the agenda title with the verbose name of the content object
|
||||
*/
|
||||
public getAgendaTitleWithType = () => {
|
||||
// Return the agenda title with the model's verbose name appended
|
||||
return this.getAgendaTitle() + ' (' + this.getVerboseName() + ')';
|
||||
};
|
||||
|
||||
/**
|
||||
* @returns the (optional) descriptive text to be exported in the CSV.
|
||||
* May be overridden by inheriting classes
|
||||
*/
|
||||
public getCSVExportText(): string {
|
||||
return '';
|
||||
}
|
||||
|
||||
public abstract getDetailStateURL(): string;
|
||||
|
||||
/**
|
||||
* Should return a string representation of the object, so there can be searched for.
|
||||
*/
|
||||
public abstract formatForSearch(): SearchRepresentation;
|
||||
}
|
@ -1,11 +1,13 @@
|
||||
import { Projectable, ProjectorElementBuildDeskriptor } from './projectable';
|
||||
import { BaseViewModel } from './base-view-model';
|
||||
import { ConfigService } from 'app/core/ui-services/config.service';
|
||||
import { BaseModel } from 'app/shared/models/base/base-model';
|
||||
|
||||
/**
|
||||
* Base view class for projectable models.
|
||||
*/
|
||||
export abstract class BaseProjectableViewModel extends BaseViewModel implements Projectable {
|
||||
export abstract class BaseProjectableViewModel<M extends BaseModel = any> extends BaseViewModel<M>
|
||||
implements Projectable {
|
||||
public abstract getSlide(configService?: ConfigService): ProjectorElementBuildDeskriptor;
|
||||
|
||||
/**
|
||||
|
@ -0,0 +1,82 @@
|
||||
import { SearchRepresentation } from 'app/core/ui-services/search.service';
|
||||
import { BaseModelWithAgendaItemAndListOfSpeakers } from 'app/shared/models/base/base-model-with-agenda-item-and-list-of-speakers';
|
||||
import { ViewItem } from '../agenda/models/view-item';
|
||||
import { ViewListOfSpeakers } from '../agenda/models/view-list-of-speakers';
|
||||
import { BaseProjectableViewModel } from './base-projectable-view-model';
|
||||
import { isBaseViewModelWithAgendaItem, IBaseViewModelWithAgendaItem } from './base-view-model-with-agenda-item';
|
||||
import {
|
||||
isBaseViewModelWithListOfSpeakers,
|
||||
IBaseViewModelWithListOfSpeakers
|
||||
} from './base-view-model-with-list-of-speakers';
|
||||
import { BaseViewModel } from './base-view-model';
|
||||
|
||||
export function isBaseViewModelWithAgendaItemAndListOfSpeakers(
|
||||
obj: any
|
||||
): obj is BaseViewModelWithAgendaItemAndListOfSpeakers {
|
||||
return !!obj && isBaseViewModelWithAgendaItem(obj) && isBaseViewModelWithListOfSpeakers(obj);
|
||||
}
|
||||
|
||||
/**
|
||||
* Base view class for view models with an agenda item and a list of speakers associated.
|
||||
*/
|
||||
export abstract class BaseViewModelWithAgendaItemAndListOfSpeakers<
|
||||
M extends BaseModelWithAgendaItemAndListOfSpeakers = any
|
||||
> extends BaseProjectableViewModel implements IBaseViewModelWithAgendaItem<M>, IBaseViewModelWithListOfSpeakers<M> {
|
||||
protected _item?: ViewItem;
|
||||
protected _listOfSpeakers?: ViewListOfSpeakers;
|
||||
|
||||
public get agendaItem(): ViewItem | null {
|
||||
return this._item;
|
||||
}
|
||||
|
||||
public get agenda_item_id(): number {
|
||||
return this._model.agenda_item_id;
|
||||
}
|
||||
|
||||
public get agenda_item_number(): string | null {
|
||||
return this.agendaItem && this.agendaItem.itemNumber ? this.agendaItem.itemNumber : null;
|
||||
}
|
||||
|
||||
public get listOfSpeakers(): ViewListOfSpeakers | null {
|
||||
return this._listOfSpeakers;
|
||||
}
|
||||
|
||||
public get list_of_speakers_id(): number {
|
||||
return this._model.list_of_speakers_id;
|
||||
}
|
||||
|
||||
public getAgendaSlideTitle: () => string;
|
||||
public getAgendaListTitle: () => string;
|
||||
public getListOfSpeakersTitle: () => string;
|
||||
public getListOfSpeakersSlideTitle: () => string;
|
||||
|
||||
public constructor(collectionString: string, model: M, item?: ViewItem, listOfSpeakers?: ViewListOfSpeakers) {
|
||||
super(collectionString, model);
|
||||
// Explicit set to null instead of undefined, if not given
|
||||
this._item = item || null;
|
||||
this._listOfSpeakers = listOfSpeakers || null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns the (optional) descriptive text to be exported in the CSV.
|
||||
* May be overridden by inheriting classes
|
||||
*/
|
||||
public getCSVExportText(): string {
|
||||
return '';
|
||||
}
|
||||
|
||||
public abstract getDetailStateURL(): string;
|
||||
|
||||
/**
|
||||
* Should return a string representation of the object, so there can be searched for.
|
||||
*/
|
||||
public abstract formatForSearch(): SearchRepresentation;
|
||||
|
||||
public updateDependencies(update: BaseViewModel): void {
|
||||
if (update instanceof ViewItem && update.id === this.agenda_item_id) {
|
||||
this._item = update;
|
||||
} else if (update instanceof ViewListOfSpeakers && update.id === this.list_of_speakers_id) {
|
||||
this._listOfSpeakers = update;
|
||||
}
|
||||
}
|
||||
}
|
114
client/src/app/site/base/base-view-model-with-agenda-item.ts
Normal file
114
client/src/app/site/base/base-view-model-with-agenda-item.ts
Normal file
@ -0,0 +1,114 @@
|
||||
import { BaseProjectableViewModel } from './base-projectable-view-model';
|
||||
import { SearchRepresentation } from 'app/core/ui-services/search.service';
|
||||
import { ViewItem } from '../agenda/models/view-item';
|
||||
import { isDetailNavigable, DetailNavigable } from 'app/shared/models/base/detail-navigable';
|
||||
import { isSearchable, Searchable } from './searchable';
|
||||
import { BaseModelWithAgendaItem } from 'app/shared/models/base/base-model-with-agenda-item';
|
||||
import { BaseViewModel, TitleInformation } from './base-view-model';
|
||||
import { Item } from 'app/shared/models/agenda/item';
|
||||
|
||||
export function isBaseViewModelWithAgendaItem(obj: any): obj is BaseViewModelWithAgendaItem {
|
||||
const model = <BaseViewModelWithAgendaItem>obj;
|
||||
return (
|
||||
!!obj &&
|
||||
isDetailNavigable(model) &&
|
||||
isSearchable(model) &&
|
||||
model.getAgendaSlideTitle !== undefined &&
|
||||
model.getAgendaListTitle !== undefined &&
|
||||
model.getCSVExportText !== undefined &&
|
||||
model.agendaItem !== undefined &&
|
||||
model.agenda_item_id !== undefined
|
||||
);
|
||||
}
|
||||
|
||||
export interface TitleInformationWithAgendaItem extends TitleInformation {
|
||||
agenda_item_number?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Describes a base class for view models.
|
||||
*/
|
||||
export interface IBaseViewModelWithAgendaItem<M extends BaseModelWithAgendaItem = any>
|
||||
extends BaseProjectableViewModel<M>,
|
||||
DetailNavigable,
|
||||
Searchable {
|
||||
agendaItem: ViewItem | null;
|
||||
|
||||
agenda_item_id: number;
|
||||
|
||||
agenda_item_number: string | null;
|
||||
|
||||
/**
|
||||
* @returns the agenda title
|
||||
*/
|
||||
getAgendaSlideTitle: () => string;
|
||||
|
||||
/**
|
||||
* @return the agenda title with the verbose name of the content object
|
||||
*/
|
||||
getAgendaListTitle: () => string;
|
||||
|
||||
/**
|
||||
* @returns the (optional) descriptive text to be exported in the CSV.
|
||||
* May be overridden by inheriting classes
|
||||
*/
|
||||
getCSVExportText(): string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Base view model class for view models with an agenda item.
|
||||
*/
|
||||
export abstract class BaseViewModelWithAgendaItem<M extends BaseModelWithAgendaItem = any>
|
||||
extends BaseProjectableViewModel<M>
|
||||
implements IBaseViewModelWithAgendaItem<M> {
|
||||
protected _item?: ViewItem;
|
||||
|
||||
public get agendaItem(): ViewItem | null {
|
||||
return this._item;
|
||||
}
|
||||
|
||||
public get agenda_item_id(): number {
|
||||
return this._model.agenda_item_id;
|
||||
}
|
||||
|
||||
public get agenda_item_number(): string | null {
|
||||
return this.agendaItem && this.agendaItem.itemNumber ? this.agendaItem.itemNumber : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns the agenda title for the item slides
|
||||
*/
|
||||
public getAgendaSlideTitle: () => string;
|
||||
|
||||
/**
|
||||
* @return the agenda title for the list view
|
||||
*/
|
||||
public getAgendaListTitle: () => string;
|
||||
|
||||
public constructor(collecitonString: string, model: M, item?: ViewItem) {
|
||||
super(collecitonString, model);
|
||||
this._item = item || null; // Explicit set to null instead of undefined, if not given
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns the (optional) descriptive text to be exported in the CSV.
|
||||
* May be overridden by inheriting classes
|
||||
*/
|
||||
public getCSVExportText(): string {
|
||||
return '';
|
||||
}
|
||||
|
||||
public abstract getDetailStateURL(): string;
|
||||
|
||||
/**
|
||||
* Should return a string representation of the object, so there can be searched for.
|
||||
*/
|
||||
public abstract formatForSearch(): SearchRepresentation;
|
||||
|
||||
public updateDependencies(update: BaseViewModel): void {
|
||||
// We cannot check with instanceof, because this gives circular dependency issues...
|
||||
if (update.collectionString === Item.COLLECTIONSTRING && update.id === this.agenda_item_id) {
|
||||
this._item = update as ViewItem;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,63 @@
|
||||
import { BaseViewModel } from './base-view-model';
|
||||
import { BaseModelWithContentObject } from 'app/shared/models/base/base-model-with-content-object';
|
||||
import { ContentObject } from 'app/shared/models/base/content-object';
|
||||
|
||||
/**
|
||||
* Base class for view models with content objects. Ensures a content object attribute and
|
||||
* implements the generic logic for `updateDependencies`.
|
||||
*
|
||||
* Type M is the contained model
|
||||
* Type C is the type of every content object.
|
||||
*/
|
||||
export abstract class BaseViewModelWithContentObject<
|
||||
M extends BaseModelWithContentObject = any,
|
||||
C extends BaseViewModel = any
|
||||
> extends BaseViewModel<M> {
|
||||
protected _contentObject?: C;
|
||||
|
||||
public get contentObjectData(): ContentObject {
|
||||
return this.getModel().content_object;
|
||||
}
|
||||
|
||||
public get contentObject(): C | null {
|
||||
return this._contentObject;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param collectionString The collection string of this model
|
||||
* @param model the model this view model captures
|
||||
* @param isC A function ensuring that an arbitrary object is a valid content object
|
||||
* @param CVerbose is the verbose name of the base content object class, for debugging purposes
|
||||
* @param contentObject (optional) The content object, if it is known during creation.
|
||||
*/
|
||||
public constructor(
|
||||
collectionString: string,
|
||||
model: M,
|
||||
private isC: (obj: any) => obj is C,
|
||||
private CVerbose: string,
|
||||
contentObject?: C
|
||||
) {
|
||||
super(collectionString, model);
|
||||
this._contentObject = contentObject;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check, if the given model mathces the content object definition. If so, the function
|
||||
* returns true, else false.
|
||||
*/
|
||||
public updateDependencies(update: BaseViewModel): boolean {
|
||||
if (
|
||||
update &&
|
||||
update.collectionString === this.contentObjectData.collection &&
|
||||
update.id === this.contentObjectData.id
|
||||
) {
|
||||
if (this.isC(update)) {
|
||||
this._contentObject = update;
|
||||
return true;
|
||||
} else {
|
||||
throw new Error(`The object is not an ${this.CVerbose}:` + update);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
@ -0,0 +1,66 @@
|
||||
import { BaseProjectableViewModel } from './base-projectable-view-model';
|
||||
import { isDetailNavigable, DetailNavigable } from 'app/shared/models/base/detail-navigable';
|
||||
import { ViewListOfSpeakers } from '../agenda/models/view-list-of-speakers';
|
||||
import { BaseModelWithListOfSpeakers } from 'app/shared/models/base/base-model-with-list-of-speakers';
|
||||
import { BaseViewModel } from './base-view-model';
|
||||
import { ListOfSpeakers } from 'app/shared/models/agenda/list-of-speakers';
|
||||
|
||||
export function isBaseViewModelWithListOfSpeakers(obj: any): obj is BaseViewModelWithListOfSpeakers {
|
||||
const model = <BaseViewModelWithListOfSpeakers>obj;
|
||||
return (
|
||||
!!obj &&
|
||||
isDetailNavigable(model) &&
|
||||
model.getListOfSpeakersTitle !== undefined &&
|
||||
model.listOfSpeakers !== undefined &&
|
||||
model.list_of_speakers_id !== undefined
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Describes a base view model with a list of speakers.
|
||||
*/
|
||||
export interface IBaseViewModelWithListOfSpeakers<M extends BaseModelWithListOfSpeakers = any>
|
||||
extends BaseProjectableViewModel<M>,
|
||||
DetailNavigable {
|
||||
listOfSpeakers: ViewListOfSpeakers | null;
|
||||
|
||||
list_of_speakers_id: number;
|
||||
|
||||
getListOfSpeakersTitle: () => string;
|
||||
|
||||
getListOfSpeakersSlideTitle: () => string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Base view model class for models with a list of speakers.
|
||||
*/
|
||||
export abstract class BaseViewModelWithListOfSpeakers<M extends BaseModelWithListOfSpeakers = any>
|
||||
extends BaseProjectableViewModel<M>
|
||||
implements IBaseViewModelWithListOfSpeakers<M> {
|
||||
protected _listOfSpeakers?: ViewListOfSpeakers;
|
||||
|
||||
public get listOfSpeakers(): ViewListOfSpeakers | null {
|
||||
return this._listOfSpeakers;
|
||||
}
|
||||
|
||||
public get list_of_speakers_id(): number {
|
||||
return this._model.list_of_speakers_id;
|
||||
}
|
||||
|
||||
public getListOfSpeakersTitle: () => string;
|
||||
public getListOfSpeakersSlideTitle: () => string;
|
||||
|
||||
public constructor(collectionString: string, model: M, listOfSpeakers?: ViewListOfSpeakers) {
|
||||
super(collectionString, model);
|
||||
this._listOfSpeakers = listOfSpeakers || null; // Explicit set to null instead of undefined, if not given
|
||||
}
|
||||
|
||||
public abstract getDetailStateURL(): string;
|
||||
|
||||
public updateDependencies(update: BaseViewModel): void {
|
||||
// We cannot check with instanceof, becuase this givec circular dependency issues...
|
||||
if (update.collectionString === ListOfSpeakers.COLLECTIONSTRING && update.id === this.list_of_speakers_id) {
|
||||
this._listOfSpeakers = update as ViewListOfSpeakers;
|
||||
}
|
||||
}
|
||||
}
|
@ -4,6 +4,8 @@ import { Collection } from 'app/shared/models/base/collection';
|
||||
import { BaseModel } from 'app/shared/models/base/base-model';
|
||||
import { Updateable } from './updateable';
|
||||
|
||||
export type TitleInformation = object;
|
||||
|
||||
export interface ViewModelConstructor<T extends BaseViewModel> {
|
||||
COLLECTIONSTRING: string;
|
||||
new (...args: any[]): T;
|
||||
@ -12,11 +14,13 @@ export interface ViewModelConstructor<T extends BaseViewModel> {
|
||||
/**
|
||||
* Base class for view models. alls view models should have titles.
|
||||
*/
|
||||
export abstract class BaseViewModel implements Displayable, Identifiable, Collection, Updateable {
|
||||
/**
|
||||
* Force children to have an id.
|
||||
*/
|
||||
public abstract id: number;
|
||||
export abstract class BaseViewModel<M extends BaseModel = any>
|
||||
implements Displayable, Identifiable, Collection, Updateable {
|
||||
protected _model: M;
|
||||
|
||||
public get id(): number {
|
||||
return this._model.id;
|
||||
}
|
||||
|
||||
/**
|
||||
* force children of BaseModel to have a collectionString.
|
||||
@ -34,7 +38,8 @@ export abstract class BaseViewModel implements Displayable, Identifiable, Collec
|
||||
return this._collectionString;
|
||||
}
|
||||
|
||||
public abstract getTitle: () => string;
|
||||
public getTitle: () => string;
|
||||
public getListTitle: () => string;
|
||||
|
||||
/**
|
||||
* Returns the verbose name.
|
||||
@ -42,24 +47,23 @@ export abstract class BaseViewModel implements Displayable, Identifiable, Collec
|
||||
* @param plural If the name should be plural
|
||||
* @returns the verbose name of the model
|
||||
*/
|
||||
public abstract getVerboseName: (plural?: boolean) => string;
|
||||
public getVerboseName: (plural?: boolean) => string;
|
||||
|
||||
/**
|
||||
* TODO: Remove verboseName, this must be overwritten by repos..
|
||||
*
|
||||
* @param verboseName
|
||||
* @param collectionString
|
||||
* @param model
|
||||
*/
|
||||
public constructor(collectionString: string) {
|
||||
public constructor(collectionString: string, model: M) {
|
||||
this._collectionString = collectionString;
|
||||
this._model = model;
|
||||
}
|
||||
|
||||
public getListTitle: () => string = () => {
|
||||
return this.getTitle();
|
||||
};
|
||||
|
||||
/** return the main model of a view model */
|
||||
public abstract getModel(): BaseModel;
|
||||
/**
|
||||
* @returns the main underlying model of the view model
|
||||
*/
|
||||
public getModel(): M {
|
||||
return this._model;
|
||||
}
|
||||
|
||||
public abstract updateDependencies(update: BaseViewModel): void;
|
||||
|
||||
|
@ -1,22 +1,23 @@
|
||||
import { MatTableDataSource, MatTable, MatSort, MatPaginator, MatSnackBar, PageEvent } from '@angular/material';
|
||||
import { Title } from '@angular/platform-browser';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { ViewChild, Type, OnDestroy } from '@angular/core';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { Observable } from 'rxjs';
|
||||
|
||||
import { BaseViewComponent } from './base-view';
|
||||
import { BaseViewModel } from './base-view-model';
|
||||
import { BaseViewModel, TitleInformation } from './base-view-model';
|
||||
import { BaseSortListService } from 'app/core/ui-services/base-sort-list.service';
|
||||
import { BaseFilterListService } from 'app/core/ui-services/base-filter-list.service';
|
||||
import { BaseModel } from 'app/shared/models/base/base-model';
|
||||
import { StorageService } from 'app/core/core-services/storage.service';
|
||||
import { BaseRepository } from 'app/core/repositories/base-repository';
|
||||
import { Observable } from 'rxjs';
|
||||
|
||||
export abstract class ListViewBaseComponent<
|
||||
V extends BaseViewModel,
|
||||
M extends BaseModel,
|
||||
R extends BaseRepository<V, M>
|
||||
R extends BaseRepository<V, M, TitleInformation>
|
||||
> extends BaseViewComponent implements OnDestroy {
|
||||
/**
|
||||
* The data source for a table. Requires to be initialized with a BaseViewModel
|
||||
|
@ -25,16 +25,14 @@ export interface ProjectorElementBuildDeskriptor {
|
||||
}
|
||||
|
||||
export function isProjectable(obj: any): obj is Projectable {
|
||||
if (obj) {
|
||||
return (<Projectable>obj).getSlide !== undefined;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
return !!obj && obj.getSlide !== undefined && obj.getProjectorTitle !== undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface for every model, that should be projectable.
|
||||
*/
|
||||
export interface Projectable extends Displayable {
|
||||
getProjectorTitle: () => string;
|
||||
|
||||
getSlide(configSerice?: ConfigService): ProjectorElementBuildDeskriptor;
|
||||
}
|
||||
|
@ -32,17 +32,16 @@ interface ConfigConstant {
|
||||
choices?: ConfigChoice[];
|
||||
}
|
||||
|
||||
export interface ConfigTitleInformation {
|
||||
key: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* The view model for configs.
|
||||
*/
|
||||
export class ViewConfig extends BaseViewModel {
|
||||
export class ViewConfig extends BaseViewModel<Config> implements ConfigTitleInformation {
|
||||
public static COLLECTIONSTRING = Config.COLLECTIONSTRING;
|
||||
|
||||
/**
|
||||
* The underlying config.
|
||||
*/
|
||||
private _config: Config;
|
||||
|
||||
/* This private members are set by setConstantsInfo. */
|
||||
private _helpText: string;
|
||||
private _inputType: ConfigInputType;
|
||||
@ -60,11 +59,7 @@ export class ViewConfig extends BaseViewModel {
|
||||
}
|
||||
|
||||
public get config(): Config {
|
||||
return this._config;
|
||||
}
|
||||
|
||||
public get id(): number {
|
||||
return this.config.id;
|
||||
return this._model;
|
||||
}
|
||||
|
||||
public get key(): string {
|
||||
@ -95,26 +90,12 @@ export class ViewConfig extends BaseViewModel {
|
||||
return this._defaultValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is set by the repository
|
||||
*/
|
||||
public getVerboseName;
|
||||
|
||||
public constructor(config: Config) {
|
||||
super(Config.COLLECTIONSTRING);
|
||||
this._config = config;
|
||||
super(Config.COLLECTIONSTRING, config);
|
||||
}
|
||||
|
||||
public getTitle = () => {
|
||||
return this.label;
|
||||
};
|
||||
|
||||
public updateDependencies(update: BaseViewModel): void {}
|
||||
|
||||
public getModel(): Config {
|
||||
return this.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,
|
||||
|
@ -4,22 +4,21 @@ import { ViewUser } from 'app/site/users/models/view-user';
|
||||
|
||||
export type ProxyHistory = History & { user?: ViewUser };
|
||||
|
||||
export interface HistoryTitleInformation {
|
||||
element_id: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* View model for history objects
|
||||
*/
|
||||
export class ViewHistory extends BaseViewModel {
|
||||
export class ViewHistory extends BaseViewModel<ProxyHistory> implements HistoryTitleInformation {
|
||||
public static COLLECTIONSTRING = History.COLLECTIONSTRING;
|
||||
|
||||
/**
|
||||
* Private BaseModel of the history
|
||||
*/
|
||||
private _history: ProxyHistory;
|
||||
|
||||
/**
|
||||
* Read the history property
|
||||
*/
|
||||
public get history(): ProxyHistory {
|
||||
return this._history;
|
||||
return this._model;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -73,11 +72,6 @@ export class ViewHistory extends BaseViewModel {
|
||||
return this.history.now;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is set by the repository
|
||||
*/
|
||||
public getVerboseName;
|
||||
|
||||
/**
|
||||
* Construction of a ViewHistory
|
||||
*
|
||||
@ -85,8 +79,7 @@ export class ViewHistory extends BaseViewModel {
|
||||
* @param user the real user BaseModel
|
||||
*/
|
||||
public constructor(history: ProxyHistory) {
|
||||
super(History.COLLECTIONSTRING);
|
||||
this._history = history;
|
||||
super(History.COLLECTIONSTRING, history);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -105,19 +98,5 @@ export class ViewHistory extends BaseViewModel {
|
||||
return +this.element_id.split(':')[1];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the history objects title
|
||||
* Required by BaseViewModel
|
||||
*
|
||||
* @returns history.getTitle which returns the element_id
|
||||
*/
|
||||
public getTitle = () => {
|
||||
return this.element_id;
|
||||
};
|
||||
|
||||
public getModel(): History {
|
||||
return this.history;
|
||||
}
|
||||
|
||||
public updateDependencies(update: BaseViewModel): void {}
|
||||
}
|
||||
|
@ -169,6 +169,8 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<os-speaker-button [object]="file" [menuItem]="true"></os-speaker-button>
|
||||
|
||||
<!-- Edit and delete for all images -->
|
||||
<mat-divider></mat-divider>
|
||||
<button mat-menu-item (click)="onEditFile(file)">
|
||||
|
@ -3,27 +3,28 @@ import { Mediafile } from 'app/shared/models/mediafiles/mediafile';
|
||||
import { Searchable } from 'app/site/base/searchable';
|
||||
import { SearchRepresentation } from 'app/core/ui-services/search.service';
|
||||
import { ViewUser } from 'app/site/users/models/view-user';
|
||||
import { BaseProjectableViewModel } from 'app/site/base/base-projectable-view-model';
|
||||
import { ProjectorElementBuildDeskriptor } from 'app/site/base/projectable';
|
||||
import { BaseViewModelWithListOfSpeakers } from 'app/site/base/base-view-model-with-list-of-speakers';
|
||||
import { ViewListOfSpeakers } from 'app/site/agenda/models/view-list-of-speakers';
|
||||
|
||||
export class ViewMediafile extends BaseProjectableViewModel implements Searchable {
|
||||
export interface MediafileTitleInformation {
|
||||
title: string;
|
||||
}
|
||||
|
||||
export class ViewMediafile extends BaseViewModelWithListOfSpeakers<Mediafile>
|
||||
implements MediafileTitleInformation, Searchable {
|
||||
public static COLLECTIONSTRING = Mediafile.COLLECTIONSTRING;
|
||||
|
||||
private _mediafile: Mediafile;
|
||||
private _uploader: ViewUser;
|
||||
|
||||
public get mediafile(): Mediafile {
|
||||
return this._mediafile;
|
||||
return this._model;
|
||||
}
|
||||
|
||||
public get uploader(): ViewUser {
|
||||
return this._uploader;
|
||||
}
|
||||
|
||||
public get id(): number {
|
||||
return this.mediafile.id;
|
||||
}
|
||||
|
||||
public get uploader_id(): number {
|
||||
return this.mediafile.uploader_id;
|
||||
}
|
||||
@ -65,25 +66,11 @@ export class ViewMediafile extends BaseProjectableViewModel implements Searchabl
|
||||
return this.mediafile.hidden;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is set by the repository
|
||||
*/
|
||||
public getVerboseName;
|
||||
|
||||
public constructor(mediafile: Mediafile, uploader?: ViewUser) {
|
||||
super(Mediafile.COLLECTIONSTRING);
|
||||
this._mediafile = mediafile;
|
||||
public constructor(mediafile: Mediafile, listOfSpeakers?: ViewListOfSpeakers, uploader?: ViewUser) {
|
||||
super(Mediafile.COLLECTIONSTRING, mediafile, listOfSpeakers);
|
||||
this._uploader = uploader;
|
||||
}
|
||||
|
||||
public getTitle = () => {
|
||||
return this.title;
|
||||
};
|
||||
|
||||
public getModel(): Mediafile {
|
||||
return this.mediafile;
|
||||
}
|
||||
|
||||
public formatForSearch(): SearchRepresentation {
|
||||
const searchValues = [this.title];
|
||||
if (this.uploader) {
|
||||
@ -167,6 +154,7 @@ export class ViewMediafile extends BaseProjectableViewModel implements Searchabl
|
||||
}
|
||||
|
||||
public updateDependencies(update: BaseViewModel): void {
|
||||
super.updateDependencies(update);
|
||||
if (update instanceof ViewUser && this.uploader_id === update.id) {
|
||||
this._uploader = update;
|
||||
}
|
||||
|
@ -3,6 +3,11 @@ import { BaseViewModel } from '../../base/base-view-model';
|
||||
import { SearchRepresentation } from 'app/core/ui-services/search.service';
|
||||
import { Searchable } from 'app/site/base/searchable';
|
||||
|
||||
export interface CategoryTitleInformation {
|
||||
prefix: string;
|
||||
name: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Category class for the View
|
||||
*
|
||||
@ -10,17 +15,11 @@ import { Searchable } from 'app/site/base/searchable';
|
||||
* Provides "safe" access to variables and functions in {@link Category}
|
||||
* @ignore
|
||||
*/
|
||||
export class ViewCategory extends BaseViewModel implements Searchable {
|
||||
export class ViewCategory extends BaseViewModel<Category> implements CategoryTitleInformation, Searchable {
|
||||
public static COLLECTIONSTRING = Category.COLLECTIONSTRING;
|
||||
|
||||
private _category: Category;
|
||||
|
||||
public get category(): Category {
|
||||
return this._category;
|
||||
}
|
||||
|
||||
public get id(): number {
|
||||
return this.category.id;
|
||||
return this._model;
|
||||
}
|
||||
|
||||
public get name(): string {
|
||||
@ -31,32 +30,28 @@ export class ViewCategory extends BaseViewModel implements Searchable {
|
||||
return this.category.prefix;
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO: Where is this used? Try to avoid this.
|
||||
*/
|
||||
public set prefix(prefix: string) {
|
||||
this._category.prefix = prefix;
|
||||
this._model.prefix = prefix;
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO: Where is this used? Try to avoid this.
|
||||
*/
|
||||
public set name(name: string) {
|
||||
this._category.name = name;
|
||||
this._model.name = name;
|
||||
}
|
||||
|
||||
public get prefixedName(): string {
|
||||
return this.prefix ? this.prefix + ' - ' + this.name : this.name;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is set by the repository
|
||||
*/
|
||||
public getVerboseName;
|
||||
|
||||
public constructor(category: Category) {
|
||||
super(Category.COLLECTIONSTRING);
|
||||
this._category = category;
|
||||
super(Category.COLLECTIONSTRING, category);
|
||||
}
|
||||
|
||||
public getTitle = () => {
|
||||
return this.prefixedName;
|
||||
};
|
||||
|
||||
public formatForSearch(): SearchRepresentation {
|
||||
return [this.name, this.prefix];
|
||||
}
|
||||
@ -65,10 +60,6 @@ export class ViewCategory extends BaseViewModel implements Searchable {
|
||||
return '/motions/category';
|
||||
}
|
||||
|
||||
public getModel(): Category {
|
||||
return this.category;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the local objects if required
|
||||
* @param update
|
||||
|
@ -6,6 +6,11 @@ import { ViewMotionBlock } from './view-motion-block';
|
||||
import { ViewItem } from 'app/site/agenda/models/view-item';
|
||||
import { ViewCategory } from './view-category';
|
||||
import { ViewWorkflow } from './view-workflow';
|
||||
import { ViewListOfSpeakers } from 'app/site/agenda/models/view-list-of-speakers';
|
||||
import { ViewMediafile } from 'app/site/mediafiles/models/view-mediafile';
|
||||
import { ViewTag } from 'app/site/tags/models/view-tag';
|
||||
import { ViewMotionChangeRecommendation } from './view-motion-change-recommendation';
|
||||
import { PersonalNoteContent } from 'app/shared/models/users/personal-note';
|
||||
|
||||
/**
|
||||
* Create motion class for the View. Its different to ViewMotion in fact that the submitter handling is different
|
||||
@ -14,10 +19,10 @@ import { ViewWorkflow } from './view-workflow';
|
||||
* @ignore
|
||||
*/
|
||||
export class ViewCreateMotion extends ViewMotion {
|
||||
protected _motion: CreateMotion;
|
||||
protected _model: CreateMotion;
|
||||
|
||||
public get motion(): CreateMotion {
|
||||
return this._motion;
|
||||
return this._model;
|
||||
}
|
||||
|
||||
public get submitters(): ViewUser[] {
|
||||
@ -30,20 +35,43 @@ export class ViewCreateMotion extends ViewMotion {
|
||||
|
||||
public set submitters(users: ViewUser[]) {
|
||||
this._submitters = users;
|
||||
this._motion.submitters_id = users.map(user => user.id);
|
||||
this._model.submitters_id = users.map(user => user.id);
|
||||
}
|
||||
|
||||
public constructor(
|
||||
motion?: CreateMotion,
|
||||
motion: CreateMotion,
|
||||
category?: ViewCategory,
|
||||
submitters?: ViewUser[],
|
||||
supporters?: ViewUser[],
|
||||
workflow?: ViewWorkflow,
|
||||
state?: WorkflowState,
|
||||
item?: ViewItem,
|
||||
block?: ViewMotionBlock
|
||||
listOfSpeakers?: ViewListOfSpeakers,
|
||||
block?: ViewMotionBlock,
|
||||
attachments?: ViewMediafile[],
|
||||
tags?: ViewTag[],
|
||||
parent?: ViewMotion,
|
||||
changeRecommendations?: ViewMotionChangeRecommendation[],
|
||||
amendments?: ViewMotion[],
|
||||
personalNote?: PersonalNoteContent
|
||||
) {
|
||||
super(motion, category, submitters, supporters, workflow, state, item, block, null);
|
||||
super(
|
||||
motion,
|
||||
category,
|
||||
submitters,
|
||||
supporters,
|
||||
workflow,
|
||||
state,
|
||||
item,
|
||||
listOfSpeakers,
|
||||
block,
|
||||
attachments,
|
||||
tags,
|
||||
parent,
|
||||
changeRecommendations,
|
||||
amendments,
|
||||
personalNote
|
||||
);
|
||||
}
|
||||
|
||||
public getVerboseName = () => {
|
||||
@ -55,7 +83,7 @@ export class ViewCreateMotion extends ViewMotion {
|
||||
*/
|
||||
public copy(): ViewCreateMotion {
|
||||
return new ViewCreateMotion(
|
||||
this._motion,
|
||||
this._model,
|
||||
this._category,
|
||||
this._submitters,
|
||||
this._supporters,
|
||||
|
@ -1,52 +1,34 @@
|
||||
import { MotionBlock } from 'app/shared/models/motions/motion-block';
|
||||
import { BaseAgendaViewModel } from 'app/site/base/base-agenda-view-model';
|
||||
import { SearchRepresentation } from 'app/core/ui-services/search.service';
|
||||
import { ProjectorElementBuildDeskriptor } from 'app/site/base/projectable';
|
||||
import { Searchable } from 'app/site/base/searchable';
|
||||
import { ViewItem } from 'app/site/agenda/models/view-item';
|
||||
import { BaseViewModel } from 'app/site/base/base-view-model';
|
||||
import { BaseViewModelWithAgendaItemAndListOfSpeakers } from 'app/site/base/base-view-model-with-agenda-item-and-list-of-speakers';
|
||||
import { ViewListOfSpeakers } from 'app/site/agenda/models/view-list-of-speakers';
|
||||
import { TitleInformationWithAgendaItem } from 'app/site/base/base-view-model-with-agenda-item';
|
||||
|
||||
export interface MotionBlockTitleInformation extends TitleInformationWithAgendaItem {
|
||||
title: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* ViewModel for motion blocks.
|
||||
* @ignore
|
||||
*/
|
||||
export class ViewMotionBlock extends BaseAgendaViewModel implements Searchable {
|
||||
export class ViewMotionBlock extends BaseViewModelWithAgendaItemAndListOfSpeakers
|
||||
implements MotionBlockTitleInformation, Searchable {
|
||||
public static COLLECTIONSTRING = MotionBlock.COLLECTIONSTRING;
|
||||
|
||||
private _motionBlock: MotionBlock;
|
||||
private _agendaItem: ViewItem;
|
||||
|
||||
public get motionBlock(): MotionBlock {
|
||||
return this._motionBlock;
|
||||
}
|
||||
|
||||
public get agendaItem(): ViewItem {
|
||||
return this._agendaItem;
|
||||
}
|
||||
|
||||
public get id(): number {
|
||||
return this.motionBlock.id;
|
||||
return this._model;
|
||||
}
|
||||
|
||||
public get title(): string {
|
||||
return this.motionBlock.title;
|
||||
}
|
||||
|
||||
public get agenda_item_id(): number {
|
||||
return this.motionBlock.agenda_item_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is set by the repository
|
||||
*/
|
||||
public getVerboseName;
|
||||
public getAgendaTitle;
|
||||
public getAgendaTitleWithType;
|
||||
|
||||
public constructor(motionBlock: MotionBlock, agendaItem?: ViewItem) {
|
||||
super(MotionBlock.COLLECTIONSTRING);
|
||||
this._motionBlock = motionBlock;
|
||||
this._agendaItem = agendaItem;
|
||||
public constructor(motionBlock: MotionBlock, agendaItem?: ViewItem, listOfSpeakers?: ViewListOfSpeakers) {
|
||||
super(MotionBlock.COLLECTIONSTRING, motionBlock, agendaItem, listOfSpeakers);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -58,10 +40,6 @@ export class ViewMotionBlock extends BaseAgendaViewModel implements Searchable {
|
||||
return [this.title];
|
||||
}
|
||||
|
||||
public getAgendaItem(): ViewItem {
|
||||
return this.agendaItem;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the URL to the motion block
|
||||
*
|
||||
@ -71,20 +49,6 @@ export class ViewMotionBlock extends BaseAgendaViewModel implements Searchable {
|
||||
return `/motions/blocks/${this.id}`;
|
||||
}
|
||||
|
||||
public updateDependencies(update: BaseViewModel): void {
|
||||
if (update instanceof ViewItem && this.agenda_item_id === update.id) {
|
||||
this._agendaItem = update;
|
||||
}
|
||||
}
|
||||
|
||||
public getTitle = () => {
|
||||
return this.title;
|
||||
};
|
||||
|
||||
public getModel(): MotionBlock {
|
||||
return this.motionBlock;
|
||||
}
|
||||
|
||||
public getSlide(): ProjectorElementBuildDeskriptor {
|
||||
return {
|
||||
getBasicProjectorElement: options => ({
|
||||
|
@ -3,6 +3,8 @@ import { ModificationType } from 'app/core/ui-services/diff.service';
|
||||
import { MotionChangeRecommendation } from 'app/shared/models/motions/motion-change-reco';
|
||||
import { ViewUnifiedChange, ViewUnifiedChangeType } from '../../../shared/models/motions/view-unified-change';
|
||||
|
||||
export type MotionChangeRecommendationTitleInformation = object;
|
||||
|
||||
/**
|
||||
* Change recommendation class for the View
|
||||
*
|
||||
@ -10,76 +12,57 @@ import { ViewUnifiedChange, ViewUnifiedChangeType } from '../../../shared/models
|
||||
* Provides "safe" access to variables and functions in {@link MotionChangeRecommendation}
|
||||
* @ignore
|
||||
*/
|
||||
export class ViewMotionChangeRecommendation extends BaseViewModel implements ViewUnifiedChange {
|
||||
export class ViewMotionChangeRecommendation extends BaseViewModel<MotionChangeRecommendation>
|
||||
implements MotionChangeRecommendationTitleInformation, ViewUnifiedChange {
|
||||
public static COLLECTIONSTRING = MotionChangeRecommendation.COLLECTIONSTRING;
|
||||
|
||||
private _changeRecommendation: MotionChangeRecommendation;
|
||||
|
||||
public get id(): number {
|
||||
return this._changeRecommendation.id;
|
||||
}
|
||||
|
||||
public get changeRecommendation(): MotionChangeRecommendation {
|
||||
return this._changeRecommendation;
|
||||
return this._model;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is set by the repository
|
||||
*/
|
||||
public getVerboseName;
|
||||
|
||||
public constructor(changeReco: MotionChangeRecommendation) {
|
||||
super(MotionChangeRecommendation.COLLECTIONSTRING);
|
||||
this._changeRecommendation = changeReco;
|
||||
public constructor(motionChangeRecommendation: MotionChangeRecommendation) {
|
||||
super(MotionChangeRecommendation.COLLECTIONSTRING, motionChangeRecommendation);
|
||||
}
|
||||
|
||||
public getTitle = () => {
|
||||
return 'Change recommendation';
|
||||
};
|
||||
|
||||
public updateDependencies(update: BaseViewModel): void {}
|
||||
|
||||
public getModel(): MotionChangeRecommendation {
|
||||
return this.changeRecommendation;
|
||||
}
|
||||
|
||||
public updateChangeReco(type: number, text: string, internal: boolean): void {
|
||||
// @TODO HTML sanitazion
|
||||
this._changeRecommendation.type = type;
|
||||
this._changeRecommendation.text = text;
|
||||
this._changeRecommendation.internal = internal;
|
||||
this.changeRecommendation.type = type;
|
||||
this.changeRecommendation.text = text;
|
||||
this.changeRecommendation.internal = internal;
|
||||
}
|
||||
|
||||
public get rejected(): boolean {
|
||||
return this._changeRecommendation.rejected;
|
||||
return this.changeRecommendation.rejected;
|
||||
}
|
||||
|
||||
public get internal(): boolean {
|
||||
return this._changeRecommendation.internal;
|
||||
return this.changeRecommendation.internal;
|
||||
}
|
||||
|
||||
public get type(): number {
|
||||
return this._changeRecommendation.type || ModificationType.TYPE_REPLACEMENT;
|
||||
return this.changeRecommendation.type || ModificationType.TYPE_REPLACEMENT;
|
||||
}
|
||||
|
||||
public get other_description(): string {
|
||||
return this._changeRecommendation.other_description;
|
||||
return this.changeRecommendation.other_description;
|
||||
}
|
||||
|
||||
public get line_from(): number {
|
||||
return this._changeRecommendation.line_from;
|
||||
return this.changeRecommendation.line_from;
|
||||
}
|
||||
|
||||
public get line_to(): number {
|
||||
return this._changeRecommendation.line_to;
|
||||
return this.changeRecommendation.line_to;
|
||||
}
|
||||
|
||||
public get text(): string {
|
||||
return this._changeRecommendation.text;
|
||||
return this.changeRecommendation.text;
|
||||
}
|
||||
|
||||
public get motion_id(): number {
|
||||
return this._changeRecommendation.motion_id;
|
||||
return this.changeRecommendation.motion_id;
|
||||
}
|
||||
|
||||
public getChangeId(): string {
|
@ -2,6 +2,10 @@ import { BaseViewModel } from '../../base/base-view-model';
|
||||
import { MotionCommentSection } from 'app/shared/models/motions/motion-comment-section';
|
||||
import { ViewGroup } from 'app/site/users/models/view-group';
|
||||
|
||||
export interface MotionCommentSectionTitleInformation {
|
||||
name: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Motion comment section class for the View
|
||||
*
|
||||
@ -9,16 +13,15 @@ import { ViewGroup } from 'app/site/users/models/view-group';
|
||||
* Provides "safe" access to variables and functions in {@link MotionCommentSection}
|
||||
* @ignore
|
||||
*/
|
||||
export class ViewMotionCommentSection extends BaseViewModel {
|
||||
export class ViewMotionCommentSection extends BaseViewModel<MotionCommentSection>
|
||||
implements MotionCommentSectionTitleInformation {
|
||||
public static COLLECTIONSTRING = MotionCommentSection.COLLECTIONSTRING;
|
||||
|
||||
private _section: MotionCommentSection;
|
||||
|
||||
private _readGroups: ViewGroup[];
|
||||
private _writeGroups: ViewGroup[];
|
||||
|
||||
public get section(): MotionCommentSection {
|
||||
return this._section;
|
||||
return this._model;
|
||||
}
|
||||
|
||||
public get id(): number {
|
||||
@ -45,30 +48,19 @@ export class ViewMotionCommentSection extends BaseViewModel {
|
||||
return this._writeGroups;
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO: Where is this needed? Try to avoid this.
|
||||
*/
|
||||
public set name(name: string) {
|
||||
this._section.name = name;
|
||||
this._model.name = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is set by the repository
|
||||
*/
|
||||
public getVerboseName;
|
||||
|
||||
public constructor(section: MotionCommentSection, readGroups: ViewGroup[], writeGroups: ViewGroup[]) {
|
||||
super(MotionCommentSection.COLLECTIONSTRING);
|
||||
this._section = section;
|
||||
public constructor(motionCommentSection: MotionCommentSection, readGroups: ViewGroup[], writeGroups: ViewGroup[]) {
|
||||
super(MotionCommentSection.COLLECTIONSTRING, motionCommentSection);
|
||||
this._readGroups = readGroups;
|
||||
this._writeGroups = writeGroups;
|
||||
}
|
||||
|
||||
public getTitle = () => {
|
||||
return this.name;
|
||||
};
|
||||
|
||||
public getModel(): MotionCommentSection {
|
||||
return this.section;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the local objects if required
|
||||
* @param section
|
||||
|
@ -4,7 +4,6 @@ import { ViewMotionCommentSection } from './view-motion-comment-section';
|
||||
import { WorkflowState } from 'app/shared/models/motions/workflow-state';
|
||||
import { ProjectorElementBuildDeskriptor } from 'app/site/base/projectable';
|
||||
import { SearchRepresentation } from 'app/core/ui-services/search.service';
|
||||
import { BaseAgendaViewModel } from 'app/site/base/base-agenda-view-model';
|
||||
import { Searchable } from 'app/site/base/searchable';
|
||||
import { ViewUser } from 'app/site/users/models/view-user';
|
||||
import { ViewTag } from 'app/site/tags/models/view-tag';
|
||||
@ -15,9 +14,12 @@ import { ViewCategory } from './view-category';
|
||||
import { ViewMotionBlock } from './view-motion-block';
|
||||
import { BaseViewModel } from 'app/site/base/base-view-model';
|
||||
import { ConfigService } from 'app/core/ui-services/config.service';
|
||||
import { ViewMotionChangeRecommendation } from './view-change-recommendation';
|
||||
import { ViewPersonalNote } from 'app/site/users/models/view-personal-note';
|
||||
import { ViewMotionChangeRecommendation } from './view-motion-change-recommendation';
|
||||
import { _ } from 'app/core/translate/translation-marker';
|
||||
import { BaseViewModelWithAgendaItemAndListOfSpeakers } from 'app/site/base/base-view-model-with-agenda-item-and-list-of-speakers';
|
||||
import { ViewListOfSpeakers } from 'app/site/agenda/models/view-list-of-speakers';
|
||||
import { TitleInformationWithAgendaItem } from 'app/site/base/base-view-model-with-agenda-item';
|
||||
|
||||
/**
|
||||
* The line numbering mode for the motion detail view.
|
||||
@ -40,6 +42,11 @@ export enum ChangeRecoMode {
|
||||
ModifiedFinal = 'modified_final_version'
|
||||
}
|
||||
|
||||
export interface MotionTitleInformation extends TitleInformationWithAgendaItem {
|
||||
title: string;
|
||||
identifier?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Motion class for the View
|
||||
*
|
||||
@ -47,30 +54,65 @@ export enum ChangeRecoMode {
|
||||
* Provides "safe" access to variables and functions in {@link Motion}
|
||||
* @ignore
|
||||
*/
|
||||
export class ViewMotion extends BaseAgendaViewModel implements Searchable {
|
||||
export class ViewMotion extends BaseViewModelWithAgendaItemAndListOfSpeakers<Motion>
|
||||
implements MotionTitleInformation, Searchable {
|
||||
public static COLLECTIONSTRING = Motion.COLLECTIONSTRING;
|
||||
|
||||
protected _motion: Motion;
|
||||
protected _category: ViewCategory;
|
||||
protected _submitters: ViewUser[];
|
||||
protected _supporters: ViewUser[];
|
||||
protected _workflow: ViewWorkflow;
|
||||
protected _state: WorkflowState;
|
||||
protected _item: ViewItem;
|
||||
protected _block: ViewMotionBlock;
|
||||
protected _attachments: ViewMediafile[];
|
||||
protected _tags: ViewTag[];
|
||||
protected _parent: ViewMotion;
|
||||
protected _amendments: ViewMotion[];
|
||||
protected _changeRecommendations: ViewMotionChangeRecommendation[];
|
||||
protected _category?: ViewCategory;
|
||||
protected _submitters?: ViewUser[];
|
||||
protected _supporters?: ViewUser[];
|
||||
protected _workflow?: ViewWorkflow;
|
||||
protected _state?: WorkflowState;
|
||||
protected _block?: ViewMotionBlock;
|
||||
protected _attachments?: ViewMediafile[];
|
||||
protected _tags?: ViewTag[];
|
||||
protected _parent?: ViewMotion;
|
||||
protected _amendments?: ViewMotion[];
|
||||
protected _changeRecommendations?: ViewMotionChangeRecommendation[];
|
||||
public personalNote?: PersonalNoteContent;
|
||||
|
||||
public get motion(): Motion {
|
||||
return this._motion;
|
||||
return this._model;
|
||||
}
|
||||
|
||||
public get id(): number {
|
||||
return this.motion.id;
|
||||
public get category(): ViewCategory | null {
|
||||
return this._category;
|
||||
}
|
||||
|
||||
public get submitters(): ViewUser[] {
|
||||
return this._submitters || [];
|
||||
}
|
||||
|
||||
public get supporters(): ViewUser[] {
|
||||
return this._supporters || [];
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO: Where is this needed. Try to avoid this..
|
||||
*/
|
||||
public set supporters(users: ViewUser[]) {
|
||||
this._supporters = users;
|
||||
this._model.supporters_id = users.map(user => user.id);
|
||||
}
|
||||
|
||||
public get motion_block(): ViewMotionBlock | null {
|
||||
return this._block;
|
||||
}
|
||||
|
||||
public get attachments(): ViewMediafile[] {
|
||||
return this._attachments || [];
|
||||
}
|
||||
|
||||
public get tags(): ViewTag[] {
|
||||
return this._tags || [];
|
||||
}
|
||||
|
||||
public get parent(): ViewMotion | null {
|
||||
return this._parent;
|
||||
}
|
||||
|
||||
public get amendments(): ViewMotion[] {
|
||||
return this._amendments || [];
|
||||
}
|
||||
|
||||
public get identifier(): string {
|
||||
@ -111,43 +153,22 @@ export class ViewMotion extends BaseAgendaViewModel implements Searchable {
|
||||
return this.motion.sort_parent_id;
|
||||
}
|
||||
|
||||
public get agenda_item_id(): number {
|
||||
return this.motion.agenda_item_id;
|
||||
}
|
||||
|
||||
public get category_id(): number {
|
||||
return this.motion.category_id;
|
||||
}
|
||||
|
||||
public get category(): ViewCategory {
|
||||
return this._category;
|
||||
}
|
||||
|
||||
public get category_weight(): number {
|
||||
return this.motion.category_weight;
|
||||
}
|
||||
|
||||
public get submitters(): ViewUser[] {
|
||||
return this._submitters;
|
||||
}
|
||||
|
||||
public get sorted_submitters_id(): number[] {
|
||||
return this.motion.sorted_submitters_id;
|
||||
}
|
||||
|
||||
public get supporters(): ViewUser[] {
|
||||
return this._supporters;
|
||||
}
|
||||
|
||||
public get supporters_id(): number[] {
|
||||
return this.motion.supporters_id;
|
||||
}
|
||||
|
||||
public set supporters(users: ViewUser[]) {
|
||||
this._supporters = users;
|
||||
this._motion.supporters_id = users.map(user => user.id);
|
||||
}
|
||||
|
||||
public get workflow(): ViewWorkflow {
|
||||
return this._workflow;
|
||||
}
|
||||
@ -207,24 +228,16 @@ export class ViewMotion extends BaseAgendaViewModel implements Searchable {
|
||||
return this.state && this.workflow ? this.state.getPreviousStates(this.workflow.workflow) : [];
|
||||
}
|
||||
|
||||
public get item(): ViewItem {
|
||||
return this._item;
|
||||
}
|
||||
|
||||
public get agenda_type(): number {
|
||||
return this.item ? this.item.type : null;
|
||||
return this.agendaItem ? this.agendaItem.type : null;
|
||||
}
|
||||
|
||||
public get motion_block_id(): number {
|
||||
return this.motion.motion_block_id;
|
||||
}
|
||||
|
||||
public get motion_block(): ViewMotionBlock {
|
||||
return this._block;
|
||||
}
|
||||
|
||||
public get agendaSpeakerAmount(): number {
|
||||
return this.item ? this.item.waitingSpeakerAmount : null;
|
||||
public get speakerAmount(): number {
|
||||
return this.listOfSpeakers ? this.listOfSpeakers.waitingSpeakerAmount : null;
|
||||
}
|
||||
|
||||
public get parent_id(): number {
|
||||
@ -243,22 +256,6 @@ export class ViewMotion extends BaseAgendaViewModel implements Searchable {
|
||||
return this.motion.attachments_id;
|
||||
}
|
||||
|
||||
public get attachments(): ViewMediafile[] {
|
||||
return this._attachments;
|
||||
}
|
||||
|
||||
public get tags(): ViewTag[] {
|
||||
return this._tags;
|
||||
}
|
||||
|
||||
public get parent(): ViewMotion {
|
||||
return this._parent;
|
||||
}
|
||||
|
||||
public get amendments(): ViewMotion[] {
|
||||
return this._amendments;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns the creation date as Date object
|
||||
*/
|
||||
@ -347,31 +344,9 @@ export class ViewMotion extends BaseAgendaViewModel implements Searchable {
|
||||
return StateCssClassMapping[this.state.css_class] || '';
|
||||
}
|
||||
|
||||
/**
|
||||
* This is set by the repository
|
||||
*/
|
||||
public getTitle: () => string;
|
||||
|
||||
/**
|
||||
* This is set by the repository
|
||||
*/
|
||||
// This is set by the repository
|
||||
public getIdentifierOrTitle: () => string;
|
||||
|
||||
/**
|
||||
* This is set by the repository
|
||||
*/
|
||||
public getAgendaTitle: () => string;
|
||||
|
||||
/**
|
||||
* This is set by the repository
|
||||
*/
|
||||
public getAgendaTitleWithType: () => string;
|
||||
|
||||
/**
|
||||
* This is set by the repository
|
||||
*/
|
||||
public getVerboseName: () => string;
|
||||
|
||||
public constructor(
|
||||
motion: Motion,
|
||||
category?: ViewCategory,
|
||||
@ -380,6 +355,7 @@ export class ViewMotion extends BaseAgendaViewModel implements Searchable {
|
||||
workflow?: ViewWorkflow,
|
||||
state?: WorkflowState,
|
||||
item?: ViewItem,
|
||||
listOfSpeakers?: ViewListOfSpeakers,
|
||||
block?: ViewMotionBlock,
|
||||
attachments?: ViewMediafile[],
|
||||
tags?: ViewTag[],
|
||||
@ -388,14 +364,12 @@ export class ViewMotion extends BaseAgendaViewModel implements Searchable {
|
||||
amendments?: ViewMotion[],
|
||||
personalNote?: PersonalNoteContent
|
||||
) {
|
||||
super(Motion.COLLECTIONSTRING);
|
||||
this._motion = motion;
|
||||
super(Motion.COLLECTIONSTRING, motion, item, listOfSpeakers);
|
||||
this._category = category;
|
||||
this._submitters = submitters;
|
||||
this._supporters = supporters;
|
||||
this._workflow = workflow;
|
||||
this._state = state;
|
||||
this._item = item;
|
||||
this._block = block;
|
||||
this._attachments = attachments;
|
||||
this._tags = tags;
|
||||
@ -405,14 +379,6 @@ export class ViewMotion extends BaseAgendaViewModel implements Searchable {
|
||||
this.personalNote = personalNote;
|
||||
}
|
||||
|
||||
public getAgendaItem(): ViewItem {
|
||||
return this.item;
|
||||
}
|
||||
|
||||
public getModel(): Motion {
|
||||
return this.motion;
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats the category for search
|
||||
*
|
||||
@ -461,12 +427,11 @@ export class ViewMotion extends BaseAgendaViewModel implements Searchable {
|
||||
* @param update
|
||||
*/
|
||||
public updateDependencies(update: BaseViewModel): void {
|
||||
super.updateDependencies(update);
|
||||
if (update instanceof ViewWorkflow) {
|
||||
this.updateWorkflow(update);
|
||||
} else if (update instanceof ViewCategory) {
|
||||
this.updateCategory(update);
|
||||
} else if (update instanceof ViewItem) {
|
||||
this.updateItem(update);
|
||||
} else if (update instanceof ViewMotionBlock) {
|
||||
this.updateMotionBlock(update);
|
||||
} else if (update instanceof ViewUser) {
|
||||
@ -507,17 +472,6 @@ export class ViewMotion extends BaseAgendaViewModel implements Searchable {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update routine for the agenda Item
|
||||
*
|
||||
* @param item potentially the changed agenda Item. Needs manual verification
|
||||
*/
|
||||
private updateItem(item: ViewItem): void {
|
||||
if (item.id === this.motion.agenda_item_id) {
|
||||
this._item = item;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update routine for the motion block
|
||||
*
|
||||
@ -532,7 +486,7 @@ export class ViewMotion extends BaseAgendaViewModel implements Searchable {
|
||||
/**
|
||||
* Update routine for supporters and submitters
|
||||
*
|
||||
* @param update potentially the changed agenda Item. Needs manual verification
|
||||
* @param update potentially the changed user. Needs manual verification
|
||||
*/
|
||||
private updateUser(update: ViewUser): void {
|
||||
if (this.motion.submitters && this.motion.submitters.find(user => user.user_id === update.id)) {
|
||||
@ -665,7 +619,7 @@ export class ViewMotion extends BaseAgendaViewModel implements Searchable {
|
||||
}),
|
||||
slideOptions: slideOptions,
|
||||
projectionDefaultName: 'motions',
|
||||
getDialogTitle: this.getAgendaTitle
|
||||
getDialogTitle: this.getAgendaSlideTitle
|
||||
};
|
||||
}
|
||||
|
||||
@ -674,13 +628,14 @@ export class ViewMotion extends BaseAgendaViewModel implements Searchable {
|
||||
*/
|
||||
public copy(): ViewMotion {
|
||||
return new ViewMotion(
|
||||
this._motion,
|
||||
this._model,
|
||||
this._category,
|
||||
this._submitters,
|
||||
this._supporters,
|
||||
this._workflow,
|
||||
this._state,
|
||||
this._item,
|
||||
this._listOfSpeakers,
|
||||
this._block,
|
||||
this._attachments,
|
||||
this._tags,
|
||||
|
@ -3,6 +3,10 @@ import { StatuteParagraph } from 'app/shared/models/motions/statute-paragraph';
|
||||
import { Searchable } from 'app/site/base/searchable';
|
||||
import { SearchRepresentation } from 'app/core/ui-services/search.service';
|
||||
|
||||
export interface StatuteParagraphTitleInformation {
|
||||
title: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* State paragrpah class for the View
|
||||
*
|
||||
@ -10,17 +14,12 @@ import { SearchRepresentation } from 'app/core/ui-services/search.service';
|
||||
* Provides "safe" access to variables and functions in {@link StatuteParagraph}
|
||||
* @ignore
|
||||
*/
|
||||
export class ViewStatuteParagraph extends BaseViewModel implements Searchable {
|
||||
export class ViewStatuteParagraph extends BaseViewModel<StatuteParagraph>
|
||||
implements StatuteParagraphTitleInformation, Searchable {
|
||||
public static COLLECTIONSTRING = StatuteParagraph.COLLECTIONSTRING;
|
||||
|
||||
private _paragraph: StatuteParagraph;
|
||||
|
||||
public get statuteParagraph(): StatuteParagraph {
|
||||
return this._paragraph;
|
||||
}
|
||||
|
||||
public get id(): number {
|
||||
return this.statuteParagraph.id;
|
||||
return this._model;
|
||||
}
|
||||
|
||||
public get title(): string {
|
||||
@ -35,22 +34,8 @@ export class ViewStatuteParagraph extends BaseViewModel implements Searchable {
|
||||
return this.statuteParagraph.weight;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is set by the repository
|
||||
*/
|
||||
public getVerboseName;
|
||||
|
||||
public constructor(paragraph: StatuteParagraph) {
|
||||
super(StatuteParagraph.COLLECTIONSTRING);
|
||||
this._paragraph = paragraph;
|
||||
}
|
||||
|
||||
public getTitle = () => {
|
||||
return this.title;
|
||||
};
|
||||
|
||||
public getModel(): StatuteParagraph {
|
||||
return this.statuteParagraph;
|
||||
public constructor(statuteParagraph: StatuteParagraph) {
|
||||
super(StatuteParagraph.COLLECTIONSTRING, statuteParagraph);
|
||||
}
|
||||
|
||||
public formatForSearch(): SearchRepresentation {
|
||||
|
@ -10,21 +10,19 @@ export const StateCssClassMapping = {
|
||||
warning: 'yellow'
|
||||
};
|
||||
|
||||
export interface WorkflowTitleInformation {
|
||||
name: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* class for the ViewWorkflow.
|
||||
* @ignore
|
||||
*/
|
||||
export class ViewWorkflow extends BaseViewModel {
|
||||
export class ViewWorkflow extends BaseViewModel<Workflow> implements WorkflowTitleInformation {
|
||||
public static COLLECTIONSTRING = Workflow.COLLECTIONSTRING;
|
||||
|
||||
private _workflow: Workflow;
|
||||
|
||||
public get workflow(): Workflow {
|
||||
return this._workflow;
|
||||
}
|
||||
|
||||
public get id(): number {
|
||||
return this.workflow.id;
|
||||
return this._model;
|
||||
}
|
||||
|
||||
public get name(): string {
|
||||
@ -43,28 +41,14 @@ export class ViewWorkflow extends BaseViewModel {
|
||||
return this.getStateById(this.first_state_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* This is set by the repository
|
||||
*/
|
||||
public getVerboseName;
|
||||
|
||||
public constructor(workflow: Workflow) {
|
||||
super(Workflow.COLLECTIONSTRING);
|
||||
this._workflow = workflow;
|
||||
super(Workflow.COLLECTIONSTRING, workflow);
|
||||
}
|
||||
|
||||
public getTitle = () => {
|
||||
return this.name;
|
||||
};
|
||||
|
||||
public sortStates(): void {
|
||||
this.workflow.sortStates();
|
||||
}
|
||||
|
||||
public getModel(): Workflow {
|
||||
return this.workflow;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the local objects if required
|
||||
*
|
||||
|
@ -101,7 +101,7 @@
|
||||
|
||||
<!-- The menu content -->
|
||||
<mat-menu #motionBlockMenu="matMenu">
|
||||
<button mat-menu-item [routerLink]="getSpeakerLink()">
|
||||
<button *ngIf="block" mat-menu-item [routerLink]="block.listOfSpeakersUrl">
|
||||
<mat-icon>mic</mat-icon>
|
||||
<span translate>List of speakers</span>
|
||||
</button>
|
||||
|
@ -103,17 +103,6 @@ export class MotionBlockDetailComponent extends ListViewBaseComponent<ViewMotion
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get link to the list of speakers of the corresponding agenda item
|
||||
*
|
||||
* @returns the link to the list of speakers as string
|
||||
*/
|
||||
public getSpeakerLink(): string {
|
||||
if (this.block) {
|
||||
return `/agenda/${this.block.agenda_item_id}/speakers`;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the columns that should be shown in the table
|
||||
*
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user