Merge pull request #4352 from FinnStutzenstein/losSlide
List of speakers slide
This commit is contained in:
commit
e97c308747
@ -18,6 +18,7 @@ import { SlideManager } from 'app/slides/services/slide-manager.service';
|
|||||||
import { BaseModel } from 'app/shared/models/base/base-model';
|
import { BaseModel } from 'app/shared/models/base/base-model';
|
||||||
import { ViewModelStoreService } from './view-model-store.service';
|
import { ViewModelStoreService } from './view-model-store.service';
|
||||||
import { BaseProjectableViewModel } from 'app/site/base/base-projectable-view-model';
|
import { BaseProjectableViewModel } from 'app/site/base/base-projectable-view-model';
|
||||||
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This service cares about Projectables being projected and manage all projection-related
|
* This service cares about Projectables being projected and manage all projection-related
|
||||||
@ -39,7 +40,8 @@ export class ProjectorService {
|
|||||||
private DS: DataStoreService,
|
private DS: DataStoreService,
|
||||||
private http: HttpService,
|
private http: HttpService,
|
||||||
private slideManager: SlideManager,
|
private slideManager: SlideManager,
|
||||||
private viewModelStore: ViewModelStoreService
|
private viewModelStore: ViewModelStoreService,
|
||||||
|
private translate: TranslateService
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -273,6 +275,24 @@ export class ProjectorService {
|
|||||||
return viewModel;
|
return viewModel;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
public getSlideTitle(element: ProjectorElement): string {
|
||||||
|
if (this.slideManager.canSlideBeMappedToModel(element.name)) {
|
||||||
|
const idElement = this.slideManager.getIdentifialbeProjectorElement(element);
|
||||||
|
const viewModel = this.getViewModelFromProjectorElement(idElement);
|
||||||
|
if (viewModel) {
|
||||||
|
return viewModel.getProjectorTitle();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const configuration = this.slideManager.getSlideConfiguration(element.name);
|
||||||
|
if (configuration.getSlideTitle) {
|
||||||
|
return configuration.getSlideTitle(element, this.translate, this.viewModelStore);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.translate.instant(this.slideManager.getSlideVerboseName(element.name));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Projects the next slide in the queue. Moves all currently projected
|
* Projects the next slide in the queue. Moves all currently projected
|
||||||
* non-stable slides to the history.
|
* non-stable slides to the history.
|
||||||
|
@ -20,6 +20,7 @@ import { ViewModelStoreService } from 'app/core/core-services/view-model-store.s
|
|||||||
import { BaseViewModel } from 'app/site/base/base-view-model';
|
import { BaseViewModel } from 'app/site/base/base-view-model';
|
||||||
import { ViewUser } from 'app/site/users/models/view-user';
|
import { ViewUser } from 'app/site/users/models/view-user';
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
import { BaseAgendaContentObjectRepository } from '../base-agenda-content-object-repository';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Repository service for users
|
* Repository service for users
|
||||||
@ -77,6 +78,28 @@ export class ItemRepositoryService extends BaseRepository<ViewItem, Item> {
|
|||||||
viewItem.getVerboseName = (plural: boolean = false) => {
|
viewItem.getVerboseName = (plural: boolean = false) => {
|
||||||
return this.translate.instant(plural ? 'Items' : 'Item');
|
return this.translate.instant(plural ? 'Items' : 'Item');
|
||||||
};
|
};
|
||||||
|
viewItem.getTitle = () => {
|
||||||
|
if (viewItem.contentObject) {
|
||||||
|
return viewItem.contentObject.getAgendaTitle();
|
||||||
|
} else {
|
||||||
|
const repo = this.collectionStringMapperService.getRepository(
|
||||||
|
viewItem.item.content_object.collection
|
||||||
|
) as BaseAgendaContentObjectRepository<any, any>;
|
||||||
|
return repo.getAgendaTitle(viewItem);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
viewItem.getListTitle = () => {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
};
|
||||||
return viewItem;
|
return viewItem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
|
|
||||||
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
|
||||||
import { Topic } from 'app/shared/models/topics/topic';
|
import { Topic } from 'app/shared/models/topics/topic';
|
||||||
import { BaseRepository } from 'app/core/repositories/base-repository';
|
|
||||||
import { Mediafile } from 'app/shared/models/mediafiles/mediafile';
|
import { Mediafile } from 'app/shared/models/mediafiles/mediafile';
|
||||||
import { Item } from 'app/shared/models/agenda/item';
|
import { Item } from 'app/shared/models/agenda/item';
|
||||||
import { DataStoreService } from 'app/core/core-services/data-store.service';
|
import { DataStoreService } from 'app/core/core-services/data-store.service';
|
||||||
@ -13,7 +14,7 @@ import { CreateTopic } from 'app/site/agenda/models/create-topic';
|
|||||||
import { ViewModelStoreService } from 'app/core/core-services/view-model-store.service';
|
import { ViewModelStoreService } from 'app/core/core-services/view-model-store.service';
|
||||||
import { ViewMediafile } from 'app/site/mediafiles/models/view-mediafile';
|
import { ViewMediafile } from 'app/site/mediafiles/models/view-mediafile';
|
||||||
import { ViewItem } from 'app/site/agenda/models/view-item';
|
import { ViewItem } from 'app/site/agenda/models/view-item';
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { BaseAgendaContentObjectRepository } from '../base-agenda-content-object-repository';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Repository for topics
|
* Repository for topics
|
||||||
@ -21,7 +22,7 @@ import { TranslateService } from '@ngx-translate/core';
|
|||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
})
|
})
|
||||||
export class TopicRepositoryService extends BaseRepository<ViewTopic, Topic> {
|
export class TopicRepositoryService extends BaseAgendaContentObjectRepository<ViewTopic, Topic> {
|
||||||
/**
|
/**
|
||||||
* Constructor calls the parent constructor
|
* Constructor calls the parent constructor
|
||||||
*
|
*
|
||||||
@ -39,6 +40,19 @@ export class TopicRepositoryService extends BaseRepository<ViewTopic, Topic> {
|
|||||||
super(DS, mapperService, viewModelStoreService, Topic, [Mediafile, Item]);
|
super(DS, mapperService, viewModelStoreService, Topic, [Mediafile, Item]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public getAgendaTitle = (topic: Partial<Topic> | Partial<ViewTopic>) => {
|
||||||
|
return topic.title;
|
||||||
|
};
|
||||||
|
|
||||||
|
public getAgendaTitleWithType = (topic: Partial<Topic> | Partial<ViewTopic>) => {
|
||||||
|
// Do not append ' (Topic)' to the title.
|
||||||
|
return topic.title;
|
||||||
|
};
|
||||||
|
|
||||||
|
public getVerboseName = (plural: boolean = false) => {
|
||||||
|
return this.translate.instant(plural ? 'Topics' : 'Topic');
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new viewModel out of the given model
|
* Creates a new viewModel out of the given model
|
||||||
*
|
*
|
||||||
@ -49,9 +63,9 @@ export class TopicRepositoryService extends BaseRepository<ViewTopic, Topic> {
|
|||||||
const attachments = this.viewModelStoreService.getMany(ViewMediafile, topic.attachments_id);
|
const attachments = this.viewModelStoreService.getMany(ViewMediafile, topic.attachments_id);
|
||||||
const item = this.viewModelStoreService.get(ViewItem, topic.agenda_item_id);
|
const item = this.viewModelStoreService.get(ViewItem, topic.agenda_item_id);
|
||||||
const viewTopic = new ViewTopic(topic, attachments, item);
|
const viewTopic = new ViewTopic(topic, attachments, item);
|
||||||
viewTopic.getVerboseName = (plural: boolean = false) => {
|
viewTopic.getVerboseName = this.getVerboseName;
|
||||||
return this.translate.instant(plural ? 'Topics' : 'Topic');
|
viewTopic.getAgendaTitle = () => this.getAgendaTitle(viewTopic);
|
||||||
};
|
viewTopic.getAgendaTitleWithType = () => this.getAgendaTitle(viewTopic);
|
||||||
return viewTopic;
|
return viewTopic;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,10 +1,12 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
|
|
||||||
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
|
||||||
import { ViewAssignment } from 'app/site/assignments/models/view-assignment';
|
import { ViewAssignment } from 'app/site/assignments/models/view-assignment';
|
||||||
import { Assignment } from 'app/shared/models/assignments/assignment';
|
import { Assignment } from 'app/shared/models/assignments/assignment';
|
||||||
import { User } from 'app/shared/models/users/user';
|
import { User } from 'app/shared/models/users/user';
|
||||||
import { Tag } from 'app/shared/models/core/tag';
|
import { Tag } from 'app/shared/models/core/tag';
|
||||||
import { Item } from 'app/shared/models/agenda/item';
|
import { Item } from 'app/shared/models/agenda/item';
|
||||||
import { BaseRepository } from '../base-repository';
|
|
||||||
import { DataStoreService } from '../../core-services/data-store.service';
|
import { DataStoreService } from '../../core-services/data-store.service';
|
||||||
import { Identifiable } from 'app/shared/models/base/identifiable';
|
import { Identifiable } from 'app/shared/models/base/identifiable';
|
||||||
import { CollectionStringMapperService } from '../../core-services/collectionStringMapper.service';
|
import { CollectionStringMapperService } from '../../core-services/collectionStringMapper.service';
|
||||||
@ -12,7 +14,7 @@ import { ViewModelStoreService } from 'app/core/core-services/view-model-store.s
|
|||||||
import { ViewItem } from 'app/site/agenda/models/view-item';
|
import { ViewItem } from 'app/site/agenda/models/view-item';
|
||||||
import { ViewUser } from 'app/site/users/models/view-user';
|
import { ViewUser } from 'app/site/users/models/view-user';
|
||||||
import { ViewTag } from 'app/site/tags/models/view-tag';
|
import { ViewTag } from 'app/site/tags/models/view-tag';
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { BaseAgendaContentObjectRepository } from '../base-agenda-content-object-repository';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Repository Service for Assignments.
|
* Repository Service for Assignments.
|
||||||
@ -22,7 +24,7 @@ import { TranslateService } from '@ngx-translate/core';
|
|||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
})
|
})
|
||||||
export class AssignmentRepositoryService extends BaseRepository<ViewAssignment, Assignment> {
|
export class AssignmentRepositoryService extends BaseAgendaContentObjectRepository<ViewAssignment, Assignment> {
|
||||||
/**
|
/**
|
||||||
* Constructor for the Assignment Repository.
|
* Constructor for the Assignment Repository.
|
||||||
*
|
*
|
||||||
@ -38,15 +40,27 @@ export class AssignmentRepositoryService extends BaseRepository<ViewAssignment,
|
|||||||
super(DS, mapperService, viewModelStoreService, Assignment, [User, Item, Tag]);
|
super(DS, mapperService, viewModelStoreService, Assignment, [User, Item, Tag]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public getAgendaTitle = (assignment: Partial<Assignment> | Partial<ViewAssignment>) => {
|
||||||
|
return assignment.title;
|
||||||
|
};
|
||||||
|
|
||||||
|
public getAgendaTitleWithType = (assignment: Partial<Assignment> | Partial<ViewAssignment>) => {
|
||||||
|
return assignment.title + ' (' + this.getVerboseName() + ')';
|
||||||
|
};
|
||||||
|
|
||||||
|
public getVerboseName = (plural: boolean = false) => {
|
||||||
|
return this.translate.instant(plural ? 'Elections' : 'Election');
|
||||||
|
};
|
||||||
|
|
||||||
public createViewModel(assignment: Assignment): ViewAssignment {
|
public createViewModel(assignment: Assignment): ViewAssignment {
|
||||||
const relatedUser = this.viewModelStoreService.getMany(ViewUser, assignment.candidates_id);
|
const relatedUser = this.viewModelStoreService.getMany(ViewUser, assignment.candidates_id);
|
||||||
const agendaItem = this.viewModelStoreService.get(ViewItem, assignment.agenda_item_id);
|
const agendaItem = this.viewModelStoreService.get(ViewItem, assignment.agenda_item_id);
|
||||||
const tags = this.viewModelStoreService.getMany(ViewTag, assignment.tags_id);
|
const tags = this.viewModelStoreService.getMany(ViewTag, assignment.tags_id);
|
||||||
|
|
||||||
const viewAssignment = new ViewAssignment(assignment, relatedUser, agendaItem, tags);
|
const viewAssignment = new ViewAssignment(assignment, relatedUser, agendaItem, tags);
|
||||||
viewAssignment.getVerboseName = (plural: boolean = false) => {
|
viewAssignment.getVerboseName = this.getVerboseName;
|
||||||
return this.translate.instant(plural ? 'Elections' : 'Election');
|
viewAssignment.getAgendaTitle = () => this.getAgendaTitle(viewAssignment);
|
||||||
};
|
viewAssignment.getAgendaTitleWithType = () => this.getAgendaTitleWithType(viewAssignment);
|
||||||
return viewAssignment;
|
return viewAssignment;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,37 @@
|
|||||||
|
import { BaseViewModel } from '../../site/base/base-view-model';
|
||||||
|
import { BaseModel, ModelConstructor } from '../../shared/models/base/base-model';
|
||||||
|
import { CollectionStringMapperService } from '../core-services/collectionStringMapper.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.getVerboseName !== undefined &&
|
||||||
|
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 abstract getVerboseName: (plural?: boolean) => string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
public constructor(
|
||||||
|
DS: DataStoreService,
|
||||||
|
collectionStringMapperService: CollectionStringMapperService,
|
||||||
|
viewModelStoreService: ViewModelStoreService,
|
||||||
|
baseModelCtor: ModelConstructor<M>,
|
||||||
|
depsModelCtors?: ModelConstructor<BaseModel>[]
|
||||||
|
) {
|
||||||
|
super(DS, collectionStringMapperService, viewModelStoreService, baseModelCtor, depsModelCtors);
|
||||||
|
}
|
||||||
|
}
|
@ -2,6 +2,7 @@ import { Injectable } from '@angular/core';
|
|||||||
|
|
||||||
import { Observable } from 'rxjs';
|
import { Observable } from 'rxjs';
|
||||||
import { map } from 'rxjs/operators';
|
import { map } from 'rxjs/operators';
|
||||||
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
|
||||||
import { DataSendService } from 'app/core/core-services/data-send.service';
|
import { DataSendService } from 'app/core/core-services/data-send.service';
|
||||||
import { User } from 'app/shared/models/users/user';
|
import { User } from 'app/shared/models/users/user';
|
||||||
@ -14,7 +15,6 @@ import { ViewMotionChangeRecommendation } from 'app/site/motions/models/view-cha
|
|||||||
import { Identifiable } from 'app/shared/models/base/identifiable';
|
import { Identifiable } from 'app/shared/models/base/identifiable';
|
||||||
import { CollectionStringMapperService } from 'app/core/core-services/collectionStringMapper.service';
|
import { CollectionStringMapperService } from 'app/core/core-services/collectionStringMapper.service';
|
||||||
import { ViewModelStoreService } from 'app/core/core-services/view-model-store.service';
|
import { ViewModelStoreService } from 'app/core/core-services/view-model-store.service';
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Repository Services for change recommendations
|
* Repository Services for change recommendations
|
||||||
|
@ -2,8 +2,8 @@ import { Injectable } from '@angular/core';
|
|||||||
|
|
||||||
import { Observable } from 'rxjs';
|
import { Observable } from 'rxjs';
|
||||||
import { map } from 'rxjs/operators';
|
import { map } from 'rxjs/operators';
|
||||||
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
|
||||||
import { BaseRepository } from 'app/core/repositories/base-repository';
|
|
||||||
import { CollectionStringMapperService } from 'app/core/core-services/collectionStringMapper.service';
|
import { CollectionStringMapperService } from 'app/core/core-services/collectionStringMapper.service';
|
||||||
import { DataSendService } from 'app/core/core-services/data-send.service';
|
import { DataSendService } from 'app/core/core-services/data-send.service';
|
||||||
import { DataStoreService } from 'app/core/core-services/data-store.service';
|
import { DataStoreService } from 'app/core/core-services/data-store.service';
|
||||||
@ -17,7 +17,7 @@ import { ViewMotionBlock } from 'app/site/motions/models/view-motion-block';
|
|||||||
import { ViewModelStoreService } from 'app/core/core-services/view-model-store.service';
|
import { ViewModelStoreService } from 'app/core/core-services/view-model-store.service';
|
||||||
import { Item } from 'app/shared/models/agenda/item';
|
import { Item } from 'app/shared/models/agenda/item';
|
||||||
import { ViewItem } from 'app/site/agenda/models/view-item';
|
import { ViewItem } from 'app/site/agenda/models/view-item';
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { BaseAgendaContentObjectRepository } from '../base-agenda-content-object-repository';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Repository service for motion blocks
|
* Repository service for motion blocks
|
||||||
@ -25,7 +25,7 @@ import { TranslateService } from '@ngx-translate/core';
|
|||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
})
|
})
|
||||||
export class MotionBlockRepositoryService extends BaseRepository<ViewMotionBlock, MotionBlock> {
|
export class MotionBlockRepositoryService extends BaseAgendaContentObjectRepository<ViewMotionBlock, MotionBlock> {
|
||||||
/**
|
/**
|
||||||
* Constructor for the motion block repository
|
* Constructor for the motion block repository
|
||||||
*
|
*
|
||||||
@ -47,6 +47,18 @@ export class MotionBlockRepositoryService extends BaseRepository<ViewMotionBlock
|
|||||||
super(DS, mapperService, viewModelStoreService, MotionBlock, [Item]);
|
super(DS, mapperService, viewModelStoreService, MotionBlock, [Item]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public getAgendaTitle = (motionBlock: Partial<MotionBlock> | Partial<ViewMotionBlock>) => {
|
||||||
|
return motionBlock.title;
|
||||||
|
};
|
||||||
|
|
||||||
|
public getAgendaTitleWithType = (motionBlock: Partial<MotionBlock> | Partial<ViewMotionBlock>) => {
|
||||||
|
return motionBlock.title + ' (' + this.getVerboseName() + ')';
|
||||||
|
};
|
||||||
|
|
||||||
|
public getVerboseName = (plural: boolean = false) => {
|
||||||
|
return this.translate.instant(plural ? 'Motion blocks' : 'Motion block');
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts a given motion block into a ViewModel
|
* Converts a given motion block into a ViewModel
|
||||||
*
|
*
|
||||||
@ -56,9 +68,9 @@ export class MotionBlockRepositoryService extends BaseRepository<ViewMotionBlock
|
|||||||
protected createViewModel(block: MotionBlock): ViewMotionBlock {
|
protected createViewModel(block: MotionBlock): ViewMotionBlock {
|
||||||
const item = this.viewModelStoreService.get(ViewItem, block.agenda_item_id);
|
const item = this.viewModelStoreService.get(ViewItem, block.agenda_item_id);
|
||||||
const viewMotionBlock = new ViewMotionBlock(block, item);
|
const viewMotionBlock = new ViewMotionBlock(block, item);
|
||||||
viewMotionBlock.getVerboseName = (plural: boolean = false) => {
|
viewMotionBlock.getVerboseName = this.getVerboseName;
|
||||||
return this.translate.instant(plural ? 'Motion blocks' : 'Motion block');
|
viewMotionBlock.getAgendaTitle = () => this.getAgendaTitle(viewMotionBlock);
|
||||||
};
|
viewMotionBlock.getAgendaTitleWithType = () => this.getAgendaTitleWithType(viewMotionBlock);
|
||||||
return viewMotionBlock;
|
return viewMotionBlock;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,7 +4,6 @@ import { TranslateService } from '@ngx-translate/core';
|
|||||||
import { Observable } from 'rxjs';
|
import { Observable } from 'rxjs';
|
||||||
import { tap, map } from 'rxjs/operators';
|
import { tap, map } from 'rxjs/operators';
|
||||||
|
|
||||||
import { BaseRepository } from '../base-repository';
|
|
||||||
import { Category } from 'app/shared/models/motions/category';
|
import { Category } from 'app/shared/models/motions/category';
|
||||||
import { ChangeRecoMode, ViewMotion } from 'app/site/motions/models/view-motion';
|
import { ChangeRecoMode, ViewMotion } from 'app/site/motions/models/view-motion';
|
||||||
import { CollectionStringMapperService } from '../../core-services/collectionStringMapper.service';
|
import { CollectionStringMapperService } from '../../core-services/collectionStringMapper.service';
|
||||||
@ -40,6 +39,7 @@ import { ViewItem } from 'app/site/agenda/models/view-item';
|
|||||||
import { ViewMotionBlock } from 'app/site/motions/models/view-motion-block';
|
import { ViewMotionBlock } from 'app/site/motions/models/view-motion-block';
|
||||||
import { ViewMediafile } from 'app/site/mediafiles/models/view-mediafile';
|
import { ViewMediafile } from 'app/site/mediafiles/models/view-mediafile';
|
||||||
import { ViewTag } from 'app/site/tags/models/view-tag';
|
import { ViewTag } from 'app/site/tags/models/view-tag';
|
||||||
|
import { BaseAgendaContentObjectRepository } from '../base-agenda-content-object-repository';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Repository Services for motions (and potentially categories)
|
* Repository Services for motions (and potentially categories)
|
||||||
@ -54,7 +54,7 @@ import { ViewTag } from 'app/site/tags/models/view-tag';
|
|||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
})
|
})
|
||||||
export class MotionRepositoryService extends BaseRepository<ViewMotion, Motion> {
|
export class MotionRepositoryService extends BaseAgendaContentObjectRepository<ViewMotion, Motion> {
|
||||||
/**
|
/**
|
||||||
* Creates a MotionRepository
|
* Creates a MotionRepository
|
||||||
*
|
*
|
||||||
@ -92,6 +92,28 @@ export class MotionRepositoryService extends BaseRepository<ViewMotion, Motion>
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public getAgendaTitle = (motion: Partial<Motion> | Partial<ViewMotion>) => {
|
||||||
|
// if the identifier is set, the title will be 'Motion <identifier>'.
|
||||||
|
if (motion.identifier) {
|
||||||
|
return this.translate.instant('Motion') + ' ' + motion.identifier;
|
||||||
|
} else {
|
||||||
|
return motion.title;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public getAgendaTitleWithType = (motion: Partial<Motion> | Partial<ViewMotion>) => {
|
||||||
|
// Append the verbose name only, if not the special format 'Motion <identifier>' is used.
|
||||||
|
if (motion.identifier) {
|
||||||
|
return this.translate.instant('Motion') + ' ' + motion.identifier;
|
||||||
|
} else {
|
||||||
|
return motion.title + ' (' + this.getVerboseName() + ')';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public getVerboseName = (plural: boolean = false) => {
|
||||||
|
return this.translate.instant(plural ? 'Motions' : 'Motion');
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts a motion to a ViewMotion and adds it to the store.
|
* Converts a motion to a ViewMotion and adds it to the store.
|
||||||
*
|
*
|
||||||
@ -127,26 +149,10 @@ export class MotionRepositoryService extends BaseRepository<ViewMotion, Motion>
|
|||||||
tags,
|
tags,
|
||||||
parent
|
parent
|
||||||
);
|
);
|
||||||
viewMotion.getVerboseName = (plural: boolean = false) => {
|
viewMotion.getVerboseName = this.getVerboseName;
|
||||||
return this.translate.instant(plural ? 'Motions' : 'Motion');
|
viewMotion.getAgendaTitle = () => this.getAgendaTitle(viewMotion);
|
||||||
};
|
|
||||||
viewMotion.getAgendaTitle = () => {
|
|
||||||
// if the identifier is set, the title will be 'Motion <identifier>'.
|
|
||||||
if (viewMotion.identifier) {
|
|
||||||
return this.translate.instant('Motion') + ' ' + viewMotion.identifier;
|
|
||||||
} else {
|
|
||||||
return viewMotion.getTitle();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
viewMotion.getProjectorTitle = viewMotion.getAgendaTitle;
|
viewMotion.getProjectorTitle = viewMotion.getAgendaTitle;
|
||||||
viewMotion.getAgendaTitleWithType = () => {
|
viewMotion.getAgendaTitleWithType = () => this.getAgendaTitleWithType(viewMotion);
|
||||||
// Append the verbose name only, if not the special format 'Motion <identifier>' is used.
|
|
||||||
if (viewMotion.identifier) {
|
|
||||||
return this.translate.instant('Motion') + ' ' + viewMotion.identifier;
|
|
||||||
} else {
|
|
||||||
return viewMotion.getTitle() + ' (' + viewMotion.getVerboseName() + ')';
|
|
||||||
}
|
|
||||||
};
|
|
||||||
return viewMotion;
|
return viewMotion;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<h2 mat-dialog-title>
|
<h2 mat-dialog-title>
|
||||||
<span translate>Project</span> {{ projectorElementBuildDescriptor.getTitle() }}?
|
<span translate>Project</span> {{ projectorElementBuildDescriptor.getDialogTitle() }}?
|
||||||
</h2>
|
</h2>
|
||||||
<mat-dialog-content>
|
<mat-dialog-content>
|
||||||
<div class="projectors"
|
<div class="projectors"
|
||||||
|
@ -1,4 +1,16 @@
|
|||||||
<button type="button" mat-mini-fab (click)="onClick($event)"
|
<ng-container *osPerms="'core.can_manage_projector'">
|
||||||
[ngClass]="isProjected() ? 'projectorbutton-active' : 'projectorbutton-inactive'">
|
<button type="button" *ngIf="!text && !menuItem" mat-mini-fab (click)="onClick($event)"
|
||||||
|
[ngClass]="isProjected() ? 'projector-active' : 'projector-inactive'">
|
||||||
<mat-icon>videocam</mat-icon>
|
<mat-icon>videocam</mat-icon>
|
||||||
</button>
|
</button>
|
||||||
|
<button type="button" *ngIf="menuItem" mat-menu-item (click)="onClick($event)"
|
||||||
|
[ngClass]="isProjected() ? 'projector-active' : 'projector-inactive'">
|
||||||
|
<mat-icon>videocam</mat-icon>
|
||||||
|
<span translate>Project</span>
|
||||||
|
</button>
|
||||||
|
<button type="button" *ngIf="text && !menuItem" mat-button (click)="onClick($event)"
|
||||||
|
[ngClass]="isProjected() ? 'projector-active' : 'projector-inactive'">
|
||||||
|
<mat-icon>videocam</mat-icon>
|
||||||
|
{{ text | translate }}
|
||||||
|
</button>
|
||||||
|
</ng-container>
|
||||||
|
@ -1,4 +1,18 @@
|
|||||||
.projectorbutton-inactive {
|
.projector-inactive {
|
||||||
background-color: white !important;
|
background-color: white !important;
|
||||||
|
|
||||||
|
mat-icon {
|
||||||
color: grey !important;
|
color: grey !important;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** TODO: Take this from the accent color. Make the hovering visible */
|
||||||
|
|
||||||
|
.projector-active {
|
||||||
|
background-color: #03a9f4;
|
||||||
|
color: white !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
button.mat-menu-item.projector-active:hover {
|
||||||
|
background-color: #03a9f4;
|
||||||
|
}
|
||||||
|
@ -39,6 +39,12 @@ export class ProjectorButtonComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
public text: string | null;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
public menuItem = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The constructor
|
* The constructor
|
||||||
*/
|
*/
|
||||||
|
@ -8,6 +8,8 @@
|
|||||||
left: 0;
|
left: 0;
|
||||||
transform-origin: left top;
|
transform-origin: left top;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
font-size: 22px !important;
|
||||||
|
line-height: 24px !important;
|
||||||
|
|
||||||
#header {
|
#header {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
@ -29,8 +29,7 @@ export class Item extends BaseModel<Item> {
|
|||||||
|
|
||||||
public id: number;
|
public id: number;
|
||||||
public item_number: string;
|
public item_number: string;
|
||||||
public title: string;
|
public title_information: object;
|
||||||
public title_with_type: string;
|
|
||||||
public comment: string;
|
public comment: string;
|
||||||
public closed: boolean;
|
public closed: boolean;
|
||||||
public type: number;
|
public type: number;
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
<span *ngIf="currentListOfSpeakers">Current list of speakers</span>
|
<span *ngIf="currentListOfSpeakers">Current list of speakers</span>
|
||||||
</h2>
|
</h2>
|
||||||
</div>
|
</div>
|
||||||
<div class="menu-slot" *osPerms="'agenda.can_manage_list_of_speakers'">
|
<div class="menu-slot" *osPerms="['agenda.can_manage_list_of_speakers', 'core.can_manage_projector']">
|
||||||
<button type="button" mat-icon-button [matMenuTriggerFor]="speakerMenu"><mat-icon>more_vert</mat-icon></button>
|
<button type="button" mat-icon-button [matMenuTriggerFor]="speakerMenu"><mat-icon>more_vert</mat-icon></button>
|
||||||
</div>
|
</div>
|
||||||
</os-head-bar>
|
</os-head-bar>
|
||||||
@ -131,6 +131,8 @@
|
|||||||
</mat-card>
|
</mat-card>
|
||||||
|
|
||||||
<mat-menu #speakerMenu="matMenu">
|
<mat-menu #speakerMenu="matMenu">
|
||||||
|
<os-projector-button *ngIf="viewItem" [object]="viewItem.listOfSpeakersSlide" [menuItem]="true"></os-projector-button>
|
||||||
|
|
||||||
<button mat-menu-item *ngIf="closedList" (click)="openSpeakerList()">
|
<button mat-menu-item *ngIf="closedList" (click)="openSpeakerList()">
|
||||||
<mat-icon>mic</mat-icon>
|
<mat-icon>mic</mat-icon>
|
||||||
<span translate>Open list of speakers</span>
|
<span translate>Open list of speakers</span>
|
||||||
|
@ -2,6 +2,7 @@ import { BaseViewModel } from '../../base/base-view-model';
|
|||||||
import { Item, itemVisibilityChoices } from 'app/shared/models/agenda/item';
|
import { Item, itemVisibilityChoices } from 'app/shared/models/agenda/item';
|
||||||
import { Speaker, SpeakerState } from 'app/shared/models/agenda/speaker';
|
import { Speaker, SpeakerState } from 'app/shared/models/agenda/speaker';
|
||||||
import { BaseAgendaViewModel, isAgendaBaseModel } from 'app/site/base/base-agenda-view-model';
|
import { BaseAgendaViewModel, isAgendaBaseModel } from 'app/site/base/base-agenda-view-model';
|
||||||
|
import { ProjectorElementBuildDeskriptor } from 'app/site/base/projectable';
|
||||||
|
|
||||||
export class ViewItem extends BaseViewModel {
|
export class ViewItem extends BaseViewModel {
|
||||||
public static COLLECTIONSTRING = Item.COLLECTIONSTRING;
|
public static COLLECTIONSTRING = Item.COLLECTIONSTRING;
|
||||||
@ -111,6 +112,19 @@ export class ViewItem extends BaseViewModel {
|
|||||||
* This is set by the repository
|
* This is set by the repository
|
||||||
*/
|
*/
|
||||||
public getVerboseName;
|
public getVerboseName;
|
||||||
|
public getTitle;
|
||||||
|
public getListTitle;
|
||||||
|
|
||||||
|
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) {
|
public constructor(item: Item, contentObject: BaseAgendaViewModel) {
|
||||||
super(Item.COLLECTIONSTRING);
|
super(Item.COLLECTIONSTRING);
|
||||||
@ -118,30 +132,6 @@ export class ViewItem extends BaseViewModel {
|
|||||||
this._contentObject = contentObject;
|
this._contentObject = contentObject;
|
||||||
}
|
}
|
||||||
|
|
||||||
public getTitle = () => {
|
|
||||||
if (this.contentObject) {
|
|
||||||
return this.contentObject.getAgendaTitle();
|
|
||||||
} else {
|
|
||||||
return this.item ? this.item.title : null;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create the list view title.
|
|
||||||
* If a number was given, 'whitespac-dot-whitespace' will be added to the prefix number
|
|
||||||
*
|
|
||||||
* @returns the agenda list title as string
|
|
||||||
*/
|
|
||||||
public getListTitle = () => {
|
|
||||||
const numberPrefix = this.itemNumber ? `${this.itemNumber} · ` : '';
|
|
||||||
|
|
||||||
if (this.contentObject) {
|
|
||||||
return numberPrefix + this.contentObject.getAgendaTitleWithType();
|
|
||||||
} else {
|
|
||||||
return numberPrefix + this.item.title_with_type;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
public updateDependencies(update: BaseViewModel): boolean {
|
public updateDependencies(update: BaseViewModel): boolean {
|
||||||
if (
|
if (
|
||||||
update &&
|
update &&
|
||||||
|
@ -53,6 +53,8 @@ export class ViewTopic extends BaseAgendaViewModel {
|
|||||||
* This is set by the repository
|
* This is set by the repository
|
||||||
*/
|
*/
|
||||||
public getVerboseName;
|
public getVerboseName;
|
||||||
|
public getAgendaTitle;
|
||||||
|
public getAgendaTitleWithType;
|
||||||
|
|
||||||
public constructor(topic: Topic, attachments?: ViewMediafile[], item?: ViewItem) {
|
public constructor(topic: Topic, attachments?: ViewMediafile[], item?: ViewItem) {
|
||||||
super(Topic.COLLECTIONSTRING);
|
super(Topic.COLLECTIONSTRING);
|
||||||
@ -69,11 +71,6 @@ export class ViewTopic extends BaseAgendaViewModel {
|
|||||||
return this.agendaItem;
|
return this.agendaItem;
|
||||||
}
|
}
|
||||||
|
|
||||||
public getAgendaTitleWithType = () => {
|
|
||||||
// Do not append ' (Topic)' to the title.
|
|
||||||
return this.getAgendaTitle();
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Formats the category for search
|
* Formats the category for search
|
||||||
*
|
*
|
||||||
@ -104,7 +101,7 @@ export class ViewTopic extends BaseAgendaViewModel {
|
|||||||
}),
|
}),
|
||||||
slideOptions: [],
|
slideOptions: [],
|
||||||
projectionDefaultName: 'topics',
|
projectionDefaultName: 'topics',
|
||||||
getTitle: () => this.getTitle()
|
getDialogTitle: () => this.getTitle()
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,6 +23,10 @@ export class ViewAssignment extends BaseAgendaViewModel {
|
|||||||
return this._assignment;
|
return this._assignment;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public get title(): string {
|
||||||
|
return this.assignment.title;
|
||||||
|
}
|
||||||
|
|
||||||
public get candidates(): ViewUser[] {
|
public get candidates(): ViewUser[] {
|
||||||
return this._relatedUser;
|
return this._relatedUser;
|
||||||
}
|
}
|
||||||
@ -50,6 +54,8 @@ export class ViewAssignment extends BaseAgendaViewModel {
|
|||||||
* This is set by the repository
|
* This is set by the repository
|
||||||
*/
|
*/
|
||||||
public getVerboseName;
|
public getVerboseName;
|
||||||
|
public getAgendaTitle;
|
||||||
|
public getAgendaTitleWithType;
|
||||||
|
|
||||||
public constructor(assignment: Assignment, relatedUser?: ViewUser[], agendaItem?: ViewItem, tags?: ViewTag[]) {
|
public constructor(assignment: Assignment, relatedUser?: ViewUser[], agendaItem?: ViewItem, tags?: ViewTag[]) {
|
||||||
super(Assignment.COLLECTIONSTRING);
|
super(Assignment.COLLECTIONSTRING);
|
||||||
@ -68,7 +74,7 @@ export class ViewAssignment extends BaseAgendaViewModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public getTitle = () => {
|
public getTitle = () => {
|
||||||
return this.assignment.title;
|
return this.title;
|
||||||
};
|
};
|
||||||
|
|
||||||
public formatForSearch(): SearchRepresentation {
|
public formatForSearch(): SearchRepresentation {
|
||||||
@ -88,7 +94,7 @@ export class ViewAssignment extends BaseAgendaViewModel {
|
|||||||
}),
|
}),
|
||||||
slideOptions: [],
|
slideOptions: [],
|
||||||
projectionDefaultName: 'assignments',
|
projectionDefaultName: 'assignments',
|
||||||
getTitle: () => this.getTitle()
|
getDialogTitle: () => this.getTitle()
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@ export function isProjectorElementBuildDeskriptor(obj: any): obj is ProjectorEle
|
|||||||
!!deskriptor &&
|
!!deskriptor &&
|
||||||
deskriptor.slideOptions !== undefined &&
|
deskriptor.slideOptions !== undefined &&
|
||||||
deskriptor.getBasicProjectorElement !== undefined &&
|
deskriptor.getBasicProjectorElement !== undefined &&
|
||||||
deskriptor.getTitle !== undefined
|
deskriptor.getDialogTitle !== undefined
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -20,7 +20,7 @@ export interface ProjectorElementBuildDeskriptor {
|
|||||||
/**
|
/**
|
||||||
* The title to show in the projection dialog
|
* The title to show in the projection dialog
|
||||||
*/
|
*/
|
||||||
getTitle(): string;
|
getDialogTitle(): string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isProjectable(obj: any): obj is Projectable {
|
export function isProjectable(obj: any): obj is Projectable {
|
||||||
|
@ -97,7 +97,7 @@ export class ViewMediafile extends BaseProjectableViewModel implements Searchabl
|
|||||||
}),
|
}),
|
||||||
slideOptions: [],
|
slideOptions: [],
|
||||||
projectionDefaultName: 'mediafiles',
|
projectionDefaultName: 'mediafiles',
|
||||||
getTitle: () => this.getTitle()
|
getDialogTitle: () => this.getTitle()
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -40,6 +40,8 @@ export class ViewMotionBlock extends BaseAgendaViewModel implements Searchable {
|
|||||||
* This is set by the repository
|
* This is set by the repository
|
||||||
*/
|
*/
|
||||||
public getVerboseName;
|
public getVerboseName;
|
||||||
|
public getAgendaTitle;
|
||||||
|
public getAgendaTitleWithType;
|
||||||
|
|
||||||
public constructor(motionBlock: MotionBlock, agendaItem?: ViewItem) {
|
public constructor(motionBlock: MotionBlock, agendaItem?: ViewItem) {
|
||||||
super(MotionBlock.COLLECTIONSTRING);
|
super(MotionBlock.COLLECTIONSTRING);
|
||||||
|
@ -580,7 +580,7 @@ export class ViewMotion extends BaseAgendaViewModel implements Searchable {
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
projectionDefaultName: 'motions',
|
projectionDefaultName: 'motions',
|
||||||
getTitle: this.getAgendaTitle
|
getDialogTitle: this.getAgendaTitle
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -56,7 +56,7 @@
|
|||||||
<div class="queue">
|
<div class="queue">
|
||||||
<h5 translate>History</h5>
|
<h5 translate>History</h5>
|
||||||
<p *ngFor="let elements of projector?.elements_history">
|
<p *ngFor="let elements of projector?.elements_history">
|
||||||
{{ getElementDescription(elements[0]) }}
|
{{ getSlideTitle(elements[0]) }}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -70,7 +70,7 @@
|
|||||||
<button type="button" mat-icon-button (click)="unprojectCurrent(element)">
|
<button type="button" mat-icon-button (click)="unprojectCurrent(element)">
|
||||||
<mat-icon>videocam</mat-icon>
|
<mat-icon>videocam</mat-icon>
|
||||||
</button>
|
</button>
|
||||||
{{ getElementDescription(element) }}
|
{{ getSlideTitle(element) }}
|
||||||
</mat-list-item>
|
</mat-list-item>
|
||||||
</mat-list>
|
</mat-list>
|
||||||
</div>
|
</div>
|
||||||
@ -142,7 +142,7 @@
|
|||||||
<mat-icon>drag_indicator</mat-icon>
|
<mat-icon>drag_indicator</mat-icon>
|
||||||
</div>
|
</div>
|
||||||
<div class="name">
|
<div class="name">
|
||||||
{{ i+1 }}. <span>{{ getElementDescription(element) }}</span>
|
{{ i+1 }}. <span>{{ getSlideTitle(element) }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="button-right">
|
<div class="button-right">
|
||||||
<div>
|
<div>
|
||||||
|
@ -116,16 +116,8 @@ export class ProjectorDetailComponent extends BaseViewComponent implements OnIni
|
|||||||
this.projectorService.projectPreviewSlide(this.projector.projector, elementIndex).then(null, this.raiseError);
|
this.projectorService.projectPreviewSlide(this.projector.projector, elementIndex).then(null, this.raiseError);
|
||||||
}
|
}
|
||||||
|
|
||||||
public getElementDescription(element: ProjectorElement): string {
|
public getSlideTitle(element: ProjectorElement): string {
|
||||||
if (this.slideManager.canSlideBeMappedToModel(element.name)) {
|
return this.projectorService.getSlideTitle(element);
|
||||||
const idElement = this.slideManager.getIdentifialbeProjectorElement(element);
|
|
||||||
const viewModel = this.projectorService.getViewModelFromProjectorElement(idElement);
|
|
||||||
if (viewModel) {
|
|
||||||
return viewModel.getProjectorTitle();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.slideManager.getSlideVerboseName(element.name);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public isProjected(obj: Projectable): boolean {
|
public isProjected(obj: Projectable): boolean {
|
||||||
|
@ -64,7 +64,7 @@ export class ViewCountdown extends BaseProjectableViewModel {
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
projectionDefaultName: 'countdowns',
|
projectionDefaultName: 'countdowns',
|
||||||
getTitle: () => this.getTitle()
|
getDialogTitle: () => this.getTitle()
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -47,7 +47,7 @@ export class ViewProjectorMessage extends BaseProjectableViewModel {
|
|||||||
}),
|
}),
|
||||||
slideOptions: [],
|
slideOptions: [],
|
||||||
projectionDefaultName: 'messages',
|
projectionDefaultName: 'messages',
|
||||||
getTitle: () => this.getTitle()
|
getDialogTitle: () => this.getTitle()
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -199,7 +199,7 @@ export class ViewUser extends BaseProjectableViewModel implements Searchable {
|
|||||||
}),
|
}),
|
||||||
slideOptions: [],
|
slideOptions: [],
|
||||||
projectionDefaultName: 'users',
|
projectionDefaultName: 'users',
|
||||||
getTitle: () => this.getTitle()
|
getDialogTitle: () => this.getTitle()
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,3 +0,0 @@
|
|||||||
export interface AgendaCurrentListOfSpeakersSlideData {
|
|
||||||
error: string;
|
|
||||||
}
|
|
@ -0,0 +1,3 @@
|
|||||||
|
export interface CurrentListOfSpeakersSlideData {
|
||||||
|
error: string;
|
||||||
|
}
|
@ -1,26 +0,0 @@
|
|||||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
|
||||||
|
|
||||||
import { AgendaCurrentListOfSpeakersOverlaySlideComponent } from './agenda-current-list-of-speakers-overlay-slide.component';
|
|
||||||
import { E2EImportsModule } from '../../../../e2e-imports.module';
|
|
||||||
|
|
||||||
describe('AgendaCurrentListOfSpeakersOverlaySlideComponent', () => {
|
|
||||||
let component: AgendaCurrentListOfSpeakersOverlaySlideComponent;
|
|
||||||
let fixture: ComponentFixture<AgendaCurrentListOfSpeakersOverlaySlideComponent>;
|
|
||||||
|
|
||||||
beforeEach(async(() => {
|
|
||||||
TestBed.configureTestingModule({
|
|
||||||
imports: [E2EImportsModule],
|
|
||||||
declarations: [AgendaCurrentListOfSpeakersOverlaySlideComponent]
|
|
||||||
}).compileComponents();
|
|
||||||
}));
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
fixture = TestBed.createComponent(AgendaCurrentListOfSpeakersOverlaySlideComponent);
|
|
||||||
component = fixture.componentInstance;
|
|
||||||
fixture.detectChanges();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should create', () => {
|
|
||||||
expect(component).toBeTruthy();
|
|
||||||
});
|
|
||||||
});
|
|
@ -1,17 +0,0 @@
|
|||||||
import { Component } from '@angular/core';
|
|
||||||
|
|
||||||
import { BaseSlideComponent } from 'app/slides/base-slide-component';
|
|
||||||
import { AgendaCurrentListOfSpeakersSlideData } from '../base/agenda-current-list-of-speakers-slide-data';
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'os-agenda-current-list-of-speakers-overlay-slide',
|
|
||||||
templateUrl: './agenda-current-list-of-speakers-overlay-slide.component.html',
|
|
||||||
styleUrls: ['./agenda-current-list-of-speakers-overlay-slide.component.scss']
|
|
||||||
})
|
|
||||||
export class AgendaCurrentListOfSpeakersOverlaySlideComponent extends BaseSlideComponent<
|
|
||||||
AgendaCurrentListOfSpeakersSlideData
|
|
||||||
> {
|
|
||||||
public constructor() {
|
|
||||||
super();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,13 +0,0 @@
|
|||||||
import { AgendaCurrentListOfSpeakersOverlaySlideModule } from './agenda-current-list-of-speakers-overlay-slide.module';
|
|
||||||
|
|
||||||
describe('AgendaCurrentListOfSpeakersOverlaySlideModule', () => {
|
|
||||||
let agendaCurrentListOfSpeakersOverlaySlideModule: AgendaCurrentListOfSpeakersOverlaySlideModule;
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
agendaCurrentListOfSpeakersOverlaySlideModule = new AgendaCurrentListOfSpeakersOverlaySlideModule();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should create an instance', () => {
|
|
||||||
expect(agendaCurrentListOfSpeakersOverlaySlideModule).toBeTruthy();
|
|
||||||
});
|
|
||||||
});
|
|
@ -1,7 +0,0 @@
|
|||||||
import { NgModule } from '@angular/core';
|
|
||||||
|
|
||||||
import { makeSlideModule } from 'app/slides/base-slide-module';
|
|
||||||
import { AgendaCurrentListOfSpeakersOverlaySlideComponent } from './agenda-current-list-of-speakers-overlay-slide.component';
|
|
||||||
|
|
||||||
@NgModule(makeSlideModule(AgendaCurrentListOfSpeakersOverlaySlideComponent))
|
|
||||||
export class AgendaCurrentListOfSpeakersOverlaySlideModule {}
|
|
@ -0,0 +1,26 @@
|
|||||||
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { CurrentListOfSpeakersOverlaySlideComponent } from './current-list-of-speakers-overlay-slide.component';
|
||||||
|
import { E2EImportsModule } from '../../../../e2e-imports.module';
|
||||||
|
|
||||||
|
describe('CurrentListOfSpeakersOverlaySlideComponent', () => {
|
||||||
|
let component: CurrentListOfSpeakersOverlaySlideComponent;
|
||||||
|
let fixture: ComponentFixture<CurrentListOfSpeakersOverlaySlideComponent>;
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
imports: [E2EImportsModule],
|
||||||
|
declarations: [CurrentListOfSpeakersOverlaySlideComponent]
|
||||||
|
}).compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(CurrentListOfSpeakersOverlaySlideComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,15 @@
|
|||||||
|
import { Component } from '@angular/core';
|
||||||
|
|
||||||
|
import { BaseSlideComponent } from 'app/slides/base-slide-component';
|
||||||
|
import { CurrentListOfSpeakersSlideData } from '../base/current-list-of-speakers-slide-data';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'os-current-list-of-speakers-overlay-slide',
|
||||||
|
templateUrl: './current-list-of-speakers-overlay-slide.component.html',
|
||||||
|
styleUrls: ['./current-list-of-speakers-overlay-slide.component.scss']
|
||||||
|
})
|
||||||
|
export class CurrentListOfSpeakersOverlaySlideComponent extends BaseSlideComponent<CurrentListOfSpeakersSlideData> {
|
||||||
|
public constructor() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,13 @@
|
|||||||
|
import { CurrentListOfSpeakersOverlaySlideModule } from './current-list-of-speakers-overlay-slide.module';
|
||||||
|
|
||||||
|
describe('CurrentListOfSpeakersOverlaySlideModule', () => {
|
||||||
|
let currentListOfSpeakersOverlaySlideModule: CurrentListOfSpeakersOverlaySlideModule;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
currentListOfSpeakersOverlaySlideModule = new CurrentListOfSpeakersOverlaySlideModule();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create an instance', () => {
|
||||||
|
expect(currentListOfSpeakersOverlaySlideModule).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,7 @@
|
|||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
|
||||||
|
import { makeSlideModule } from 'app/slides/base-slide-module';
|
||||||
|
import { CurrentListOfSpeakersOverlaySlideComponent } from './current-list-of-speakers-overlay-slide.component';
|
||||||
|
|
||||||
|
@NgModule(makeSlideModule(CurrentListOfSpeakersOverlaySlideComponent))
|
||||||
|
export class CurrentListOfSpeakersOverlaySlideModule {}
|
@ -1,18 +0,0 @@
|
|||||||
import { Component, OnInit } from '@angular/core';
|
|
||||||
|
|
||||||
import { BaseSlideComponent } from 'app/slides/base-slide-component';
|
|
||||||
import { AgendaCurrentListOfSpeakersSlideData } from '../base/agenda-current-list-of-speakers-slide-data';
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'os-agenda-current-list-of-speakers-slide',
|
|
||||||
templateUrl: './agenda-current-list-of-speakers-slide.component.html',
|
|
||||||
styleUrls: ['./agenda-current-list-of-speakers-slide.component.scss']
|
|
||||||
})
|
|
||||||
export class AgendaCurrentListOfSpeakersSlideComponent extends BaseSlideComponent<AgendaCurrentListOfSpeakersSlideData>
|
|
||||||
implements OnInit {
|
|
||||||
public constructor() {
|
|
||||||
super();
|
|
||||||
}
|
|
||||||
|
|
||||||
public ngOnInit(): void {}
|
|
||||||
}
|
|
@ -1,13 +0,0 @@
|
|||||||
import { AgendaCurrentListOfSpeakersSlideModule } from './agenda-current-list-of-speakers-slide.module';
|
|
||||||
|
|
||||||
describe('AgendaCurrentListOfSpeakersModule', () => {
|
|
||||||
let agendaCurrentListOfSpeakersSlideModule: AgendaCurrentListOfSpeakersSlideModule;
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
agendaCurrentListOfSpeakersSlideModule = new AgendaCurrentListOfSpeakersSlideModule();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should create an instance', () => {
|
|
||||||
expect(agendaCurrentListOfSpeakersSlideModule).toBeTruthy();
|
|
||||||
});
|
|
||||||
});
|
|
@ -1,7 +0,0 @@
|
|||||||
import { NgModule } from '@angular/core';
|
|
||||||
|
|
||||||
import { makeSlideModule } from 'app/slides/base-slide-module';
|
|
||||||
import { AgendaCurrentListOfSpeakersSlideComponent } from './agenda-current-list-of-speakers-slide.component';
|
|
||||||
|
|
||||||
@NgModule(makeSlideModule(AgendaCurrentListOfSpeakersSlideComponent))
|
|
||||||
export class AgendaCurrentListOfSpeakersSlideModule {}
|
|
@ -1,21 +1,21 @@
|
|||||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
import { AgendaCurrentListOfSpeakersSlideComponent } from './agenda-current-list-of-speakers-slide.component';
|
import { CurrentListOfSpeakersSlideComponent } from './current-list-of-speakers-slide.component';
|
||||||
import { E2EImportsModule } from '../../../../e2e-imports.module';
|
import { E2EImportsModule } from '../../../../e2e-imports.module';
|
||||||
|
|
||||||
describe('CoreCountdownSlideComponent', () => {
|
describe('CurrentListOfSpeakersSlideComponent', () => {
|
||||||
let component: AgendaCurrentListOfSpeakersSlideComponent;
|
let component: CurrentListOfSpeakersSlideComponent;
|
||||||
let fixture: ComponentFixture<AgendaCurrentListOfSpeakersSlideComponent>;
|
let fixture: ComponentFixture<CurrentListOfSpeakersSlideComponent>;
|
||||||
|
|
||||||
beforeEach(async(() => {
|
beforeEach(async(() => {
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
imports: [E2EImportsModule],
|
imports: [E2EImportsModule],
|
||||||
declarations: [AgendaCurrentListOfSpeakersSlideComponent]
|
declarations: [CurrentListOfSpeakersSlideComponent]
|
||||||
}).compileComponents();
|
}).compileComponents();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
fixture = TestBed.createComponent(AgendaCurrentListOfSpeakersSlideComponent);
|
fixture = TestBed.createComponent(CurrentListOfSpeakersSlideComponent);
|
||||||
component = fixture.componentInstance;
|
component = fixture.componentInstance;
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
});
|
});
|
@ -0,0 +1,18 @@
|
|||||||
|
import { Component, OnInit } from '@angular/core';
|
||||||
|
|
||||||
|
import { BaseSlideComponent } from 'app/slides/base-slide-component';
|
||||||
|
import { CurrentListOfSpeakersSlideData } from '../base/current-list-of-speakers-slide-data';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'os-current-list-of-speakers-slide',
|
||||||
|
templateUrl: './current-list-of-speakers-slide.component.html',
|
||||||
|
styleUrls: ['./current-list-of-speakers-slide.component.scss']
|
||||||
|
})
|
||||||
|
export class CurrentListOfSpeakersSlideComponent extends BaseSlideComponent<CurrentListOfSpeakersSlideData>
|
||||||
|
implements OnInit {
|
||||||
|
public constructor() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ngOnInit(): void {}
|
||||||
|
}
|
@ -0,0 +1,13 @@
|
|||||||
|
import { CurrentListOfSpeakersSlideModule } from './current-list-of-speakers-slide.module';
|
||||||
|
|
||||||
|
describe('CurrentListOfSpeakersSlideModule', () => {
|
||||||
|
let currentListOfSpeakersSlideModule: CurrentListOfSpeakersSlideModule;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
currentListOfSpeakersSlideModule = new CurrentListOfSpeakersSlideModule();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create an instance', () => {
|
||||||
|
expect(currentListOfSpeakersSlideModule).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,7 @@
|
|||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
|
||||||
|
import { makeSlideModule } from 'app/slides/base-slide-module';
|
||||||
|
import { CurrentListOfSpeakersSlideComponent } from './current-list-of-speakers-slide.component';
|
||||||
|
|
||||||
|
@NgModule(makeSlideModule(CurrentListOfSpeakersSlideComponent))
|
||||||
|
export class CurrentListOfSpeakersSlideModule {}
|
@ -0,0 +1,13 @@
|
|||||||
|
interface SlideSpeaker {
|
||||||
|
user: string;
|
||||||
|
marked: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ListOfSpeakersSlideData {
|
||||||
|
waiting: SlideSpeaker[];
|
||||||
|
current: SlideSpeaker;
|
||||||
|
finished: SlideSpeaker[];
|
||||||
|
title_information: object;
|
||||||
|
content_object_collection: string;
|
||||||
|
item_number: string;
|
||||||
|
}
|
@ -0,0 +1,29 @@
|
|||||||
|
<div *ngIf="data">
|
||||||
|
<div class="slidetitle">
|
||||||
|
<h1 translate>List of speakers</h1>
|
||||||
|
<h2> {{ getTitle() }}</h2>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Last speakers -->
|
||||||
|
<div *ngIf="data.data.finished.length">
|
||||||
|
<div *ngFor="let speaker of data.data.finished" class="lastSpeakers">
|
||||||
|
{{ speaker.user }}
|
||||||
|
<mat-icon *ngIf="speaker.marked">star</mat-icon>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Current speaker -->
|
||||||
|
<div *ngIf="data.data.current" class="currentSpeaker">
|
||||||
|
<mat-icon>mic</mat-icon> {{ data.data.current.user }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Next speakers -->
|
||||||
|
<div *ngIf="data.data.finished.length">
|
||||||
|
<ol class="nextSpeakers">
|
||||||
|
<li *ngFor="let speaker of data.data.waiting">
|
||||||
|
{{ speaker.user }}
|
||||||
|
<mat-icon *ngIf="speaker.marked">star</mat-icon>
|
||||||
|
</li>
|
||||||
|
</ol>
|
||||||
|
</div>
|
||||||
|
</div>
|
@ -0,0 +1,16 @@
|
|||||||
|
.lastSpeakers {
|
||||||
|
color: #9a9898;
|
||||||
|
margin-left: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.currentSpeaker {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nextSpeakers {
|
||||||
|
margin-left: 13px !important;
|
||||||
|
|
||||||
|
li {
|
||||||
|
line-height: 150%;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,26 @@
|
|||||||
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { ListOfSpeakersSlideComponent } from './list-of-speakers-slide.component';
|
||||||
|
import { E2EImportsModule } from '../../../../e2e-imports.module';
|
||||||
|
|
||||||
|
describe('ListOfSpeakersSlideComponent', () => {
|
||||||
|
let component: ListOfSpeakersSlideComponent;
|
||||||
|
let fixture: ComponentFixture<ListOfSpeakersSlideComponent>;
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
imports: [E2EImportsModule],
|
||||||
|
declarations: [ListOfSpeakersSlideComponent]
|
||||||
|
}).compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(ListOfSpeakersSlideComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,28 @@
|
|||||||
|
import { Component } from '@angular/core';
|
||||||
|
|
||||||
|
import { BaseSlideComponent } from 'app/slides/base-slide-component';
|
||||||
|
import { ListOfSpeakersSlideData } from './list-of-speakers-slide-data';
|
||||||
|
import { CollectionStringMapperService } from 'app/core/core-services/collectionStringMapper.service';
|
||||||
|
import { isBaseAgendaContentObjectRepository } from 'app/core/repositories/base-agenda-content-object-repository';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'os-list-of-speakers-slide',
|
||||||
|
templateUrl: './list-of-speakers-slide.component.html',
|
||||||
|
styleUrls: ['./list-of-speakers-slide.component.scss']
|
||||||
|
})
|
||||||
|
export class ListOfSpeakersSlideComponent extends BaseSlideComponent<ListOfSpeakersSlideData> {
|
||||||
|
public constructor(private collectionStringMapperService: CollectionStringMapperService) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
public getTitle(): string {
|
||||||
|
const numberPrefix = this.data.data.item_number ? `${this.data.data.item_number} · ` : '';
|
||||||
|
const repo = this.collectionStringMapperService.getRepository(this.data.data.content_object_collection);
|
||||||
|
|
||||||
|
if (isBaseAgendaContentObjectRepository(repo)) {
|
||||||
|
return numberPrefix + repo.getAgendaTitle(this.data.data.title_information);
|
||||||
|
} else {
|
||||||
|
throw new Error('The content object has no agenda based repository!');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,13 @@
|
|||||||
|
import { ListOfSpeakersSlideModule } from './list-of-speakers-slide.module';
|
||||||
|
|
||||||
|
describe('ListOfSpeakersSlideModule', () => {
|
||||||
|
let listOfSpeakersSlideModule: ListOfSpeakersSlideModule;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
listOfSpeakersSlideModule = new ListOfSpeakersSlideModule();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create an instance', () => {
|
||||||
|
expect(listOfSpeakersSlideModule).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,7 @@
|
|||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
|
||||||
|
import { makeSlideModule } from 'app/slides/base-slide-module';
|
||||||
|
import { ListOfSpeakersSlideComponent } from './list-of-speakers-slide.component';
|
||||||
|
|
||||||
|
@NgModule(makeSlideModule(ListOfSpeakersSlideComponent))
|
||||||
|
export class ListOfSpeakersSlideModule {}
|
4
client/src/app/slides/agenda/topic/topic-slide-data.ts
Normal file
4
client/src/app/slides/agenda/topic/topic-slide-data.ts
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
export interface TopicSlideData {
|
||||||
|
title: string;
|
||||||
|
text: string;
|
||||||
|
}
|
@ -1,21 +1,21 @@
|
|||||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
import { TopicsTopicSlideComponent } from './topics-topic-slide.component';
|
import { TopicSlideComponent } from './topic-slide.component';
|
||||||
import { E2EImportsModule } from 'e2e-imports.module';
|
import { E2EImportsModule } from 'e2e-imports.module';
|
||||||
|
|
||||||
describe('TopicsTopicSlideComponent', () => {
|
describe('TopicSlideComponent', () => {
|
||||||
let component: TopicsTopicSlideComponent;
|
let component: TopicSlideComponent;
|
||||||
let fixture: ComponentFixture<TopicsTopicSlideComponent>;
|
let fixture: ComponentFixture<TopicSlideComponent>;
|
||||||
|
|
||||||
beforeEach(async(() => {
|
beforeEach(async(() => {
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
imports: [E2EImportsModule],
|
imports: [E2EImportsModule],
|
||||||
declarations: [TopicsTopicSlideComponent]
|
declarations: [TopicSlideComponent]
|
||||||
}).compileComponents();
|
}).compileComponents();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
fixture = TestBed.createComponent(TopicsTopicSlideComponent);
|
fixture = TestBed.createComponent(TopicSlideComponent);
|
||||||
component = fixture.componentInstance;
|
component = fixture.componentInstance;
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
});
|
});
|
14
client/src/app/slides/agenda/topic/topic-slide.component.ts
Normal file
14
client/src/app/slides/agenda/topic/topic-slide.component.ts
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import { Component } from '@angular/core';
|
||||||
|
import { BaseSlideComponent } from 'app/slides/base-slide-component';
|
||||||
|
import { TopicSlideData } from './topic-slide-data';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'os-topic-slide',
|
||||||
|
templateUrl: './topic-slide.component.html',
|
||||||
|
styleUrls: ['./topic-slide.component.scss']
|
||||||
|
})
|
||||||
|
export class TopicSlideComponent extends BaseSlideComponent<TopicSlideData> {
|
||||||
|
public constructor() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,13 @@
|
|||||||
|
import { TopicSlideModule } from './topic-slide.module';
|
||||||
|
|
||||||
|
describe('TopicSlideModule', () => {
|
||||||
|
let topicsTopicSlideModule: TopicSlideModule;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
topicsTopicSlideModule = new TopicSlideModule();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create an instance', () => {
|
||||||
|
expect(topicsTopicSlideModule).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
7
client/src/app/slides/agenda/topic/topic-slide.module.ts
Normal file
7
client/src/app/slides/agenda/topic/topic-slide.module.ts
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
|
||||||
|
import { makeSlideModule } from 'app/slides/base-slide-module';
|
||||||
|
import { TopicSlideComponent } from './topic-slide.component';
|
||||||
|
|
||||||
|
@NgModule(makeSlideModule(TopicSlideComponent))
|
||||||
|
export class TopicSlideModule {}
|
@ -1,4 +0,0 @@
|
|||||||
export interface TopicsTopicSlideData {
|
|
||||||
title: string;
|
|
||||||
text: string;
|
|
||||||
}
|
|
@ -1,14 +0,0 @@
|
|||||||
import { Component } from '@angular/core';
|
|
||||||
import { BaseSlideComponent } from 'app/slides/base-slide-component';
|
|
||||||
import { TopicsTopicSlideData } from './topics-topic-slide-data';
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'os-topic-slide',
|
|
||||||
templateUrl: './topics-topic-slide.component.html',
|
|
||||||
styleUrls: ['./topics-topic-slide.component.scss']
|
|
||||||
})
|
|
||||||
export class TopicsTopicSlideComponent extends BaseSlideComponent<TopicsTopicSlideData> {
|
|
||||||
public constructor() {
|
|
||||||
super();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,13 +0,0 @@
|
|||||||
import { TopicsTopicSlideModule } from './topics-topic-slide.module';
|
|
||||||
|
|
||||||
describe('TopicsTopicSlideModule', () => {
|
|
||||||
let topicsTopicSlideModule: TopicsTopicSlideModule;
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
topicsTopicSlideModule = new TopicsTopicSlideModule();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should create an instance', () => {
|
|
||||||
expect(topicsTopicSlideModule).toBeTruthy();
|
|
||||||
});
|
|
||||||
});
|
|
@ -1,7 +0,0 @@
|
|||||||
import { NgModule } from '@angular/core';
|
|
||||||
|
|
||||||
import { makeSlideModule } from 'app/slides/base-slide-module';
|
|
||||||
import { TopicsTopicSlideComponent } from './topics-topic-slide.component';
|
|
||||||
|
|
||||||
@NgModule(makeSlideModule(TopicsTopicSlideComponent))
|
|
||||||
export class TopicsTopicSlideModule {}
|
|
@ -1,4 +1,8 @@
|
|||||||
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
|
||||||
import { SlideDynamicConfiguration, Slide } from './slide-manifest';
|
import { SlideDynamicConfiguration, Slide } from './slide-manifest';
|
||||||
|
import { ViewModelStoreService } from 'app/core/core-services/view-model-store.service';
|
||||||
|
import { ProjectorElement } from 'app/shared/models/core/projector';
|
||||||
|
|
||||||
export const allSlidesDynamicConfiguration: (SlideDynamicConfiguration & Slide)[] = [
|
export const allSlidesDynamicConfiguration: (SlideDynamicConfiguration & Slide)[] = [
|
||||||
{
|
{
|
||||||
@ -31,6 +35,23 @@ export const allSlidesDynamicConfiguration: (SlideDynamicConfiguration & Slide)[
|
|||||||
scaleable: false,
|
scaleable: false,
|
||||||
scrollable: false
|
scrollable: false
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
slide: 'agenda/list-of-speakers',
|
||||||
|
scaleable: true,
|
||||||
|
scrollable: true,
|
||||||
|
getSlideTitle: (
|
||||||
|
element: ProjectorElement,
|
||||||
|
translate: TranslateService,
|
||||||
|
viewModelStore: ViewModelStoreService
|
||||||
|
) => {
|
||||||
|
const item = viewModelStore.get('agenda/item', element.id);
|
||||||
|
if (item) {
|
||||||
|
const title = translate.instant('List of speakers for');
|
||||||
|
return title + ' ' + item.getTitle();
|
||||||
|
}
|
||||||
|
return translate.instant('List of speakers');
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
slide: 'agenda/current-list-of-speakers',
|
slide: 'agenda/current-list-of-speakers',
|
||||||
scaleable: true,
|
scaleable: true,
|
||||||
|
@ -11,7 +11,7 @@ export const allSlides: SlideManifest[] = [
|
|||||||
{
|
{
|
||||||
slide: 'topics/topic',
|
slide: 'topics/topic',
|
||||||
path: 'topics/topic',
|
path: 'topics/topic',
|
||||||
loadChildren: './slides/agenda/topic/topics-topic-slide.module#TopicsTopicSlideModule',
|
loadChildren: './slides/agenda/topic/topic-slide.module#TopicSlideModule',
|
||||||
verboseName: 'Topic',
|
verboseName: 'Topic',
|
||||||
elementIdentifiers: ['name', 'id'],
|
elementIdentifiers: ['name', 'id'],
|
||||||
canBeMappedToModel: true
|
canBeMappedToModel: true
|
||||||
@ -60,7 +60,7 @@ export const allSlides: SlideManifest[] = [
|
|||||||
slide: 'agenda/current-list-of-speakers',
|
slide: 'agenda/current-list-of-speakers',
|
||||||
path: 'agenda/current-list-of-speakers',
|
path: 'agenda/current-list-of-speakers',
|
||||||
loadChildren:
|
loadChildren:
|
||||||
'./slides/agenda/current-list-of-speakers/agenda-current-list-of-speakers-slide.module#AgendaCurrentListOfSpeakersSlideModule',
|
'./slides/agenda/current-list-of-speakers/current-list-of-speakers-slide.module#CurrentListOfSpeakersSlideModule',
|
||||||
verboseName: 'Current list of speakers',
|
verboseName: 'Current list of speakers',
|
||||||
elementIdentifiers: ['name', 'id'],
|
elementIdentifiers: ['name', 'id'],
|
||||||
canBeMappedToModel: false
|
canBeMappedToModel: false
|
||||||
@ -69,11 +69,19 @@ export const allSlides: SlideManifest[] = [
|
|||||||
slide: 'agenda/current-list-of-speakers-overlay',
|
slide: 'agenda/current-list-of-speakers-overlay',
|
||||||
path: 'agenda/current-list-of-speakers-overlay',
|
path: 'agenda/current-list-of-speakers-overlay',
|
||||||
loadChildren:
|
loadChildren:
|
||||||
'./slides/agenda/current-list-of-speakers-overlay/agenda-current-list-of-speakers-overlay-slide.module#AgendaCurrentListOfSpeakersOverlaySlideModule',
|
'./slides/agenda/current-list-of-speakers-overlay/current-list-of-speakers-overlay-slide.module#CurrentListOfSpeakersOverlaySlideModule',
|
||||||
verboseName: 'Current list of speakers overlay',
|
verboseName: 'Current list of speakers overlay',
|
||||||
elementIdentifiers: ['name', 'id'],
|
elementIdentifiers: ['name', 'id'],
|
||||||
canBeMappedToModel: false
|
canBeMappedToModel: false
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
slide: 'agenda/list-of-speakers',
|
||||||
|
path: 'agenda/list-of-speakers',
|
||||||
|
loadChildren: './slides/agenda/list-of-speakers/list-of-speakers-slide.module#ListOfSpeakersSlideModule',
|
||||||
|
verboseName: 'List of speakers',
|
||||||
|
elementIdentifiers: ['name', 'id'],
|
||||||
|
canBeMappedToModel: false
|
||||||
|
},
|
||||||
{
|
{
|
||||||
slide: 'assignments/assignment',
|
slide: 'assignments/assignment',
|
||||||
path: 'assignments/assignment',
|
path: 'assignments/assignment',
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
import { Component, Input } from '@angular/core';
|
import { Component, Input } from '@angular/core';
|
||||||
|
import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
|
||||||
|
|
||||||
import { BaseSlideComponent } from 'app/slides/base-slide-component';
|
import { BaseSlideComponent } from 'app/slides/base-slide-component';
|
||||||
import { MotionsMotionSlideData, MotionsMotionSlideDataAmendment } from './motions-motion-slide-data';
|
import { MotionsMotionSlideData, MotionsMotionSlideDataAmendment } from './motions-motion-slide-data';
|
||||||
import { ChangeRecoMode, LineNumberingMode } from '../../../site/motions/models/view-motion';
|
import { ChangeRecoMode, LineNumberingMode } from '../../../site/motions/models/view-motion';
|
||||||
import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
|
|
||||||
import { DiffLinesInParagraph, DiffService, LineRange } from '../../../core/ui-services/diff.service';
|
import { DiffLinesInParagraph, DiffService, LineRange } from '../../../core/ui-services/diff.service';
|
||||||
import { LinenumberingService } from '../../../core/ui-services/linenumbering.service';
|
import { LinenumberingService } from '../../../core/ui-services/linenumbering.service';
|
||||||
import { ViewUnifiedChange } from '../../../shared/models/motions/view-unified-change';
|
import { ViewUnifiedChange } from '../../../shared/models/motions/view-unified-change';
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
import { InjectionToken } from '@angular/core';
|
import { InjectionToken } from '@angular/core';
|
||||||
import { IdentifiableProjectorElement, ProjectorElement } from 'app/shared/models/core/projector';
|
import { IdentifiableProjectorElement, ProjectorElement } from 'app/shared/models/core/projector';
|
||||||
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
import { ViewModelStoreService } from 'app/core/core-services/view-model-store.service';
|
||||||
|
|
||||||
type BooleanOrFunction = boolean | ((element: ProjectorElement) => boolean);
|
type BooleanOrFunction = boolean | ((element: ProjectorElement) => boolean);
|
||||||
|
|
||||||
@ -20,6 +22,12 @@ export interface SlideDynamicConfiguration {
|
|||||||
* Should this slide be scaleable?
|
* Should this slide be scaleable?
|
||||||
*/
|
*/
|
||||||
scaleable: BooleanOrFunction;
|
scaleable: BooleanOrFunction;
|
||||||
|
|
||||||
|
getSlideTitle?: (
|
||||||
|
element: ProjectorElement,
|
||||||
|
translate: TranslateService,
|
||||||
|
viewModelStore: ViewModelStoreService
|
||||||
|
) => string;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -617,7 +617,7 @@ button.mat-menu-item.selected {
|
|||||||
margin-bottom: 40px;
|
margin-bottom: 40px;
|
||||||
|
|
||||||
h1 {
|
h1 {
|
||||||
font-size: 2.25em;
|
font-size: 2em;
|
||||||
line-height: 1.1em;
|
line-height: 1.1em;
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
padding-bottom: 0;
|
padding-bottom: 0;
|
||||||
@ -625,7 +625,7 @@ button.mat-menu-item.selected {
|
|||||||
h2 {
|
h2 {
|
||||||
color: #9a9898;
|
color: #9a9898;
|
||||||
margin-top: 10px;
|
margin-top: 10px;
|
||||||
margin-bottom: 0px;
|
margin-bottom: 5px;
|
||||||
font-size: 28px;
|
font-size: 28px;
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
display: block;
|
display: block;
|
||||||
|
@ -57,7 +57,13 @@ class ItemAccessPermissions(BaseAccessPermissions):
|
|||||||
# so that list of speakers is provided regardless. Hidden items can only be seen by managers.
|
# so that list of speakers is provided regardless. Hidden items can only be seen by managers.
|
||||||
# We know that full_data has at least one entry which can be used to parse the keys.
|
# We know that full_data has at least one entry which can be used to parse the keys.
|
||||||
blocked_keys_internal_hidden_case = set(full_data[0].keys()) - set(
|
blocked_keys_internal_hidden_case = set(full_data[0].keys()) - set(
|
||||||
("id", "title", "speakers", "speaker_list_closed", "content_object")
|
(
|
||||||
|
"id",
|
||||||
|
"title_information",
|
||||||
|
"speakers",
|
||||||
|
"speaker_list_closed",
|
||||||
|
"content_object",
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
# In non internal case managers see everything and non managers see
|
# In non internal case managers see everything and non managers see
|
||||||
|
@ -283,32 +283,16 @@ class Item(RESTModelMixin, models.Model):
|
|||||||
)
|
)
|
||||||
unique_together = ("content_type", "object_id")
|
unique_together = ("content_type", "object_id")
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return self.title
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def title(self):
|
def title_information(self):
|
||||||
"""
|
"""
|
||||||
Return get_agenda_title() from the content_object.
|
Return get_agenda_title_information() from the content_object.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
return self.content_object.get_agenda_title()
|
return self.content_object.get_agenda_title_information()
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
raise NotImplementedError(
|
raise NotImplementedError(
|
||||||
"You have to provide a get_agenda_title "
|
"You have to provide a get_agenda_title_information "
|
||||||
"method on your related model."
|
|
||||||
)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def title_with_type(self):
|
|
||||||
"""
|
|
||||||
Return get_agenda_title_with_type() from the content_object.
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
return self.content_object.get_agenda_title_with_type()
|
|
||||||
except AttributeError:
|
|
||||||
raise NotImplementedError(
|
|
||||||
"You have to provide a get_agenda_title_with_type "
|
|
||||||
"method on your related model."
|
"method on your related model."
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
from typing import Any, Dict, List, Tuple
|
from typing import Any, Dict, List, Tuple
|
||||||
|
|
||||||
|
from ..users.projector import get_user_name
|
||||||
from ..utils.projector import (
|
from ..utils.projector import (
|
||||||
AllData,
|
AllData,
|
||||||
ProjectorElementException,
|
ProjectorElementException,
|
||||||
|
get_config,
|
||||||
register_projector_slide,
|
register_projector_slide,
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -75,19 +77,56 @@ def list_of_speakers_slide(
|
|||||||
|
|
||||||
Returns all usernames, that are on the list of speaker of a slide.
|
Returns all usernames, that are on the list of speaker of a slide.
|
||||||
"""
|
"""
|
||||||
item_id = element.get("id") or 0 # item_id 0 means current_list_of_speakers
|
item_id = element.get("id")
|
||||||
|
|
||||||
# TODO: handle item_id == 0
|
if item_id is None:
|
||||||
|
raise ProjectorElementException("id is required for list of speakers slide")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
item = all_data["agenda/item"][item_id]
|
item = all_data["agenda/item"][item_id]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
raise ProjectorElementException(f"Item {item_id} does not exist")
|
raise ProjectorElementException(f"Item {item_id} does not exist")
|
||||||
|
|
||||||
user_ids = []
|
# Partition speaker objects to waiting, current and finished
|
||||||
|
speakers_waiting = []
|
||||||
|
speakers_finished = []
|
||||||
|
current_speaker = None
|
||||||
for speaker in item["speakers"]:
|
for speaker in item["speakers"]:
|
||||||
user_ids.append(speaker["user"])
|
user = get_user_name(all_data, speaker["user_id"])
|
||||||
return {"user_ids": user_ids}
|
formatted_speaker = {
|
||||||
|
"user": user,
|
||||||
|
"marked": speaker["marked"],
|
||||||
|
"weight": speaker["weight"],
|
||||||
|
"end_time": speaker["end_time"],
|
||||||
|
}
|
||||||
|
|
||||||
|
if speaker["begin_time"] is None and speaker["end_time"] is None:
|
||||||
|
speakers_waiting.append(formatted_speaker)
|
||||||
|
elif speaker["begin_time"] is not None and speaker["end_time"] is None:
|
||||||
|
current_speaker = formatted_speaker
|
||||||
|
else:
|
||||||
|
speakers_finished.append(formatted_speaker)
|
||||||
|
|
||||||
|
# sort speakers
|
||||||
|
speakers_waiting = sorted(speakers_waiting, key=lambda s: s["weight"])
|
||||||
|
speakers_finished = sorted(speakers_finished, key=lambda s: s["end_time"])
|
||||||
|
|
||||||
|
number_of_last_speakers = get_config(all_data, "agenda_show_last_speakers")
|
||||||
|
if number_of_last_speakers == 0:
|
||||||
|
speakers_finished = []
|
||||||
|
else:
|
||||||
|
speakers_finished = speakers_finished[
|
||||||
|
-number_of_last_speakers:
|
||||||
|
] # Take the last speakers
|
||||||
|
|
||||||
|
return {
|
||||||
|
"waiting": speakers_waiting,
|
||||||
|
"current": current_speaker,
|
||||||
|
"finished": speakers_finished,
|
||||||
|
"content_object_collection": item["content_object"]["collection"],
|
||||||
|
"title_information": item["title_information"],
|
||||||
|
"item_number": item["item_number"],
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
def current_list_of_speakers_slide(
|
def current_list_of_speakers_slide(
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
from openslides.utils.rest_api import ModelSerializer, RelatedField
|
from openslides.utils.rest_api import JSONField, ModelSerializer, RelatedField
|
||||||
|
|
||||||
from .models import Item, Speaker
|
from .models import Item, Speaker
|
||||||
|
|
||||||
@ -42,13 +42,14 @@ class ItemSerializer(ModelSerializer):
|
|||||||
content_object = RelatedItemRelatedField(read_only=True)
|
content_object = RelatedItemRelatedField(read_only=True)
|
||||||
speakers = SpeakerSerializer(many=True, read_only=True)
|
speakers = SpeakerSerializer(many=True, read_only=True)
|
||||||
|
|
||||||
|
title_information = JSONField(read_only=True)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Item
|
model = Item
|
||||||
fields = (
|
fields = (
|
||||||
"id",
|
"id",
|
||||||
"item_number",
|
"item_number",
|
||||||
"title",
|
"title_information",
|
||||||
"title_with_type",
|
|
||||||
"comment",
|
"comment",
|
||||||
"closed",
|
"closed",
|
||||||
"type",
|
"type",
|
||||||
|
@ -16,7 +16,7 @@ def listen_to_related_object_post_save(sender, instance, created, **kwargs):
|
|||||||
Do not run caching and autoupdate if the instance has a key
|
Do not run caching and autoupdate if the instance has a key
|
||||||
skip_autoupdate in the agenda_item_update_information container.
|
skip_autoupdate in the agenda_item_update_information container.
|
||||||
"""
|
"""
|
||||||
if hasattr(instance, "get_agenda_title"):
|
if hasattr(instance, "get_agenda_title_information"):
|
||||||
if created:
|
if created:
|
||||||
attrs = {}
|
attrs = {}
|
||||||
for attr in ("type", "parent_id", "comment", "duration", "weight"):
|
for attr in ("type", "parent_id", "comment", "duration", "weight"):
|
||||||
|
@ -318,18 +318,8 @@ class Assignment(RESTModelMixin, models.Model):
|
|||||||
"""
|
"""
|
||||||
agenda_item_update_information: Dict[str, Any] = {}
|
agenda_item_update_information: Dict[str, Any] = {}
|
||||||
|
|
||||||
def get_agenda_title(self):
|
def get_agenda_title_information(self):
|
||||||
"""
|
return {"title": self.title}
|
||||||
Returns the title for the agenda.
|
|
||||||
"""
|
|
||||||
return str(self)
|
|
||||||
|
|
||||||
def get_agenda_title_with_type(self):
|
|
||||||
"""
|
|
||||||
Return a title for the agenda with the appended assignment verbose name.
|
|
||||||
Note: It has to be the same return value like in JavaScript.
|
|
||||||
"""
|
|
||||||
return f"{self.get_agenda_title()} (self._meta.verbose_name)"
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def agenda_item(self):
|
def agenda_item(self):
|
||||||
|
@ -520,32 +520,8 @@ class Motion(RESTModelMixin, models.Model):
|
|||||||
"""
|
"""
|
||||||
agenda_item_update_information: Dict[str, Any] = {}
|
agenda_item_update_information: Dict[str, Any] = {}
|
||||||
|
|
||||||
def get_agenda_title(self):
|
def get_agenda_title_information(self):
|
||||||
"""
|
return {"title": self.title, "identifier": self.identifier}
|
||||||
Return the title string for the agenda.
|
|
||||||
|
|
||||||
If the identifier is given, the title consists of the motion verbose name
|
|
||||||
and the identifier.
|
|
||||||
Note: It has to be the same return value like in JavaScript.
|
|
||||||
"""
|
|
||||||
if self.identifier:
|
|
||||||
title = f"{self._meta.verbose_name} {self.identifier}"
|
|
||||||
else:
|
|
||||||
title = self.title
|
|
||||||
return title
|
|
||||||
|
|
||||||
def get_agenda_title_with_type(self):
|
|
||||||
"""
|
|
||||||
Return a title for the agenda with the type or the modified title if the
|
|
||||||
identifier is set..
|
|
||||||
|
|
||||||
Note: It has to be the same return value like in JavaScript.
|
|
||||||
"""
|
|
||||||
if self.identifier:
|
|
||||||
title = f"{self._meta.verbose_name} {self.identifier}"
|
|
||||||
else:
|
|
||||||
title = f"{self.title} ({self._meta.verbose_name})"
|
|
||||||
return title
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def agenda_item(self):
|
def agenda_item(self):
|
||||||
@ -908,11 +884,8 @@ class MotionBlock(RESTModelMixin, models.Model):
|
|||||||
"""
|
"""
|
||||||
return self.agenda_item.pk
|
return self.agenda_item.pk
|
||||||
|
|
||||||
def get_agenda_title(self):
|
def get_agenda_title_information(self):
|
||||||
return self.title
|
return {"title": self.title}
|
||||||
|
|
||||||
def get_agenda_title_with_type(self):
|
|
||||||
return f"{self.get_agenda_title()} ({self._meta.verbose_name})"
|
|
||||||
|
|
||||||
|
|
||||||
class MotionLog(RESTModelMixin, models.Model):
|
class MotionLog(RESTModelMixin, models.Model):
|
||||||
|
@ -67,14 +67,5 @@ class Topic(RESTModelMixin, models.Model):
|
|||||||
"""
|
"""
|
||||||
return self.agenda_item.pk
|
return self.agenda_item.pk
|
||||||
|
|
||||||
def get_agenda_title(self):
|
def get_agenda_title_information(self):
|
||||||
"""
|
return {"title": self.title}
|
||||||
Returns the title for the agenda.
|
|
||||||
"""
|
|
||||||
return self.title
|
|
||||||
|
|
||||||
def get_agenda_title_with_type(self):
|
|
||||||
"""
|
|
||||||
Returns the agenda title. Topicy should not get a type postfix.
|
|
||||||
"""
|
|
||||||
return self.get_agenda_title()
|
|
||||||
|
@ -25,19 +25,18 @@ def user_slide(all_data: AllData, element: Dict[str, Any]) -> Dict[str, Any]:
|
|||||||
if user_id is None:
|
if user_id is None:
|
||||||
raise ProjectorElementException("id is required for user slide")
|
raise ProjectorElementException("id is required for user slide")
|
||||||
|
|
||||||
try:
|
return {"user": get_user_name(all_data, user_id)}
|
||||||
user = all_data["users/user"][user_id]
|
|
||||||
except KeyError:
|
|
||||||
raise ProjectorElementException(f"user with id {user_id} does not exist")
|
|
||||||
|
|
||||||
return {"user": get_user_name(all_data, user["id"])}
|
|
||||||
|
|
||||||
|
|
||||||
def get_user_name(all_data: AllData, user_id: int) -> str:
|
def get_user_name(all_data: AllData, user_id: int) -> str:
|
||||||
"""
|
"""
|
||||||
Returns the short name for an user_id.
|
Returns the short name for an user_id.
|
||||||
"""
|
"""
|
||||||
|
try:
|
||||||
user = all_data["users/user"][user_id]
|
user = all_data["users/user"][user_id]
|
||||||
|
except KeyError:
|
||||||
|
raise ProjectorElementException(f"user with id {user_id} does not exist")
|
||||||
|
|
||||||
name_parts: List[str] = []
|
name_parts: List[str] = []
|
||||||
for name_part in ("title", "first_name", "last_name"):
|
for name_part in ("title", "first_name", "last_name"):
|
||||||
if user[name_part]:
|
if user[name_part]:
|
||||||
|
@ -77,7 +77,13 @@ class RetrieveItem(TestCase):
|
|||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
sorted(response.data.keys()),
|
sorted(response.data.keys()),
|
||||||
sorted(
|
sorted(
|
||||||
("id", "title", "speakers", "speaker_list_closed", "content_object")
|
(
|
||||||
|
"id",
|
||||||
|
"title_information",
|
||||||
|
"speakers",
|
||||||
|
"speaker_list_closed",
|
||||||
|
"content_object",
|
||||||
|
)
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
forbidden_keys = (
|
forbidden_keys = (
|
||||||
|
@ -8,15 +8,17 @@ class TestItemTitle(TestCase):
|
|||||||
@patch("openslides.agenda.models.Item.content_object")
|
@patch("openslides.agenda.models.Item.content_object")
|
||||||
def test_title_from_content_object(self, content_object):
|
def test_title_from_content_object(self, content_object):
|
||||||
item = Item()
|
item = Item()
|
||||||
content_object.get_agenda_title.return_value = "related_title"
|
content_object.get_agenda_title_information.return_value = {
|
||||||
|
"attr": "related_title"
|
||||||
|
}
|
||||||
|
|
||||||
self.assertEqual(item.title, "related_title")
|
self.assertEqual(item.title_information, {"attr": "related_title"})
|
||||||
|
|
||||||
@patch("openslides.agenda.models.Item.content_object")
|
@patch("openslides.agenda.models.Item.content_object")
|
||||||
def test_title_invalid_related(self, content_object):
|
def test_title_invalid_related(self, content_object):
|
||||||
item = Item()
|
item = Item()
|
||||||
content_object.get_agenda_title.return_value = "related_title"
|
content_object.get_agenda_title_information.return_value = "related_title"
|
||||||
del content_object.get_agenda_title
|
del content_object.get_agenda_title_information
|
||||||
|
|
||||||
with self.assertRaises(NotImplementedError):
|
with self.assertRaises(NotImplementedError):
|
||||||
item.title
|
item.title_information
|
||||||
|
Loading…
Reference in New Issue
Block a user