Merge pull request #4427 from FinnStutzenstein/motionProjectionMode

motion projection mode is optional and sensitive for the config
This commit is contained in:
Emanuel Schütze 2019-03-01 10:23:55 +01:00 committed by GitHub
commit 573cdd88a2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 86 additions and 36 deletions

View File

@ -19,6 +19,7 @@ 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'; import { TranslateService } from '@ngx-translate/core';
import { ConfigService } from '../ui-services/config.service';
/** /**
* This service cares about Projectables being projected and manage all projection-related * This service cares about Projectables being projected and manage all projection-related
@ -41,7 +42,8 @@ export class ProjectorService {
private http: HttpService, private http: HttpService,
private slideManager: SlideManager, private slideManager: SlideManager,
private viewModelStore: ViewModelStoreService, private viewModelStore: ViewModelStoreService,
private translate: TranslateService private translate: TranslateService,
private configService: ConfigService
) {} ) {}
/** /**
@ -54,7 +56,7 @@ export class ProjectorService {
obj: Projectable | ProjectorElementBuildDeskriptor | IdentifiableProjectorElement obj: Projectable | ProjectorElementBuildDeskriptor | IdentifiableProjectorElement
): IdentifiableProjectorElement { ): IdentifiableProjectorElement {
if (isProjectable(obj)) { if (isProjectable(obj)) {
return obj.getSlide().getBasicProjectorElement({}); return obj.getSlide(this.configService).getBasicProjectorElement({});
} else if (isProjectorElementBuildDeskriptor(obj)) { } else if (isProjectorElementBuildDeskriptor(obj)) {
return obj.getBasicProjectorElement({}); return obj.getBasicProjectorElement({});
} else { } else {

View File

@ -69,8 +69,11 @@ export class ViewModelStoreService {
* @param callback The function to check * @param callback The function to check
* @returns all matched view models of the collection * @returns all matched view models of the collection
*/ */
public filter<T extends BaseViewModel>(collectionString: string, callback: (model: T) => boolean): T[] { public filter<T extends BaseViewModel>(
return this.getAll<T>(collectionString).filter(callback); collectionType: ViewModelConstructor<T> | string,
callback: (model: T) => boolean
): T[] {
return this.getAll<T>(collectionType).filter(callback);
} }
/** /**
@ -80,7 +83,10 @@ export class ViewModelStoreService {
* @param callback THe callback to satisfy * @param callback THe callback to satisfy
* @returns a found view model or null, if nothing was found. * @returns a found view model or null, if nothing was found.
*/ */
public find<T extends BaseViewModel>(collectionString: string, callback: (model: T) => boolean): T { public find<T extends BaseViewModel>(
return this.getAll<T>(collectionString).find(callback); collectionType: ViewModelConstructor<T> | string,
callback: (model: T) => boolean
): T {
return this.getAll<T>(collectionType).find(callback);
} }
} }

View File

@ -167,6 +167,7 @@ _('Amendment to');
_('Statute amendment for'); _('Statute amendment for');
_('Creation date'); _('Creation date');
_('Last modified'); _('Last modified');
_('Which version?');
// motion workflow 1 // motion workflow 1
_('Simple Workflow'); _('Simple Workflow');

View File

@ -88,7 +88,8 @@ export class MotionRepositoryService extends BaseAgendaContentObjectRepository<V
Item, Item,
MotionBlock, MotionBlock,
Mediafile, Mediafile,
Tag Tag,
MotionChangeRecommendation
]); ]);
} }
@ -148,6 +149,10 @@ export class MotionRepositoryService extends BaseAgendaContentObjectRepository<V
const attachments = this.viewModelStoreService.getMany(ViewMediafile, motion.attachments_id); const attachments = this.viewModelStoreService.getMany(ViewMediafile, motion.attachments_id);
const tags = this.viewModelStoreService.getMany(ViewTag, motion.tags_id); const tags = this.viewModelStoreService.getMany(ViewTag, motion.tags_id);
const parent = this.viewModelStoreService.get(ViewMotion, motion.parent_id); const parent = this.viewModelStoreService.get(ViewMotion, motion.parent_id);
const changeRecommendations = this.viewModelStoreService.filter(
ViewMotionChangeRecommendation,
cr => cr.motion_id === motion.id
);
let state: WorkflowState = null; let state: WorkflowState = null;
if (workflow) { if (workflow) {
state = workflow.getStateById(motion.state_id); state = workflow.getStateById(motion.state_id);
@ -163,7 +168,8 @@ export class MotionRepositoryService extends BaseAgendaContentObjectRepository<V
block, block,
attachments, attachments,
tags, tags,
parent parent,
changeRecommendations
); );
viewMotion.getIdentifierOrTitle = () => this.getIdentifierOrTitle(viewMotion); viewMotion.getIdentifierOrTitle = () => this.getIdentifierOrTitle(viewMotion);
viewMotion.getTitle = () => this.getTitle(viewMotion); viewMotion.getTitle = () => this.getTitle(viewMotion);

View File

@ -7,6 +7,7 @@ import {
ProjectionDialogReturnType ProjectionDialogReturnType
} from 'app/shared/components/projection-dialog/projection-dialog.component'; } from 'app/shared/components/projection-dialog/projection-dialog.component';
import { ProjectorService } from '../core-services/projector.service'; import { ProjectorService } from '../core-services/projector.service';
import { ConfigService } from './config.service';
/** /**
* Manages the projection dialog. Projects the result of the user's choice. * Manages the projection dialog. Projects the result of the user's choice.
@ -21,7 +22,11 @@ export class ProjectionDialogService {
* @param dialog * @param dialog
* @param projectorService * @param projectorService
*/ */
public constructor(private dialog: MatDialog, private projectorService: ProjectorService) {} public constructor(
private dialog: MatDialog,
private projectorService: ProjectorService,
private configService: ConfigService
) {}
/** /**
* Opens the projection dialog for the given projectable. After the user's choice, * Opens the projection dialog for the given projectable. After the user's choice,
@ -32,7 +37,7 @@ export class ProjectionDialogService {
public async openProjectDialogFor(obj: Projectable | ProjectorElementBuildDeskriptor): Promise<void> { public async openProjectDialogFor(obj: Projectable | ProjectorElementBuildDeskriptor): Promise<void> {
let descriptor: ProjectorElementBuildDeskriptor; let descriptor: ProjectorElementBuildDeskriptor;
if (isProjectable(obj)) { if (isProjectable(obj)) {
descriptor = obj.getSlide(); descriptor = obj.getSlide(this.configService);
} else { } else {
descriptor = obj; descriptor = obj;
} }

View File

@ -1,7 +1,9 @@
h2 { h2 {
margin-bottom: 5px; margin-bottom: 5px;
} }
h3 {
margin-bottom: 5px;
}
.element-name { .element-name {
margin-bottom: 10px; margin-bottom: 10px;
} }

View File

@ -1,11 +1,12 @@
import { Projectable, ProjectorElementBuildDeskriptor } from './projectable'; import { Projectable, ProjectorElementBuildDeskriptor } from './projectable';
import { BaseViewModel } from './base-view-model'; import { BaseViewModel } from './base-view-model';
import { ConfigService } from 'app/core/ui-services/config.service';
/** /**
* Base view class for projectable models. * Base view class for projectable models.
*/ */
export abstract class BaseProjectableViewModel extends BaseViewModel implements Projectable { export abstract class BaseProjectableViewModel extends BaseViewModel implements Projectable {
public abstract getSlide(): ProjectorElementBuildDeskriptor; public abstract getSlide(configService?: ConfigService): ProjectorElementBuildDeskriptor;
/** /**
* @returns the projector title used for managing projector elements. * @returns the projector title used for managing projector elements.

View File

@ -1,6 +1,7 @@
import { Displayable } from 'app/site/base/displayable'; import { Displayable } from 'app/site/base/displayable';
import { IdentifiableProjectorElement, ProjectorElementOptions } from 'app/shared/models/core/projector'; import { IdentifiableProjectorElement, ProjectorElementOptions } from 'app/shared/models/core/projector';
import { SlideOptions } from './slide-options'; import { SlideOptions } from './slide-options';
import { ConfigService } from 'app/core/ui-services/config.service';
export function isProjectorElementBuildDeskriptor(obj: any): obj is ProjectorElementBuildDeskriptor { export function isProjectorElementBuildDeskriptor(obj: any): obj is ProjectorElementBuildDeskriptor {
const deskriptor = <ProjectorElementBuildDeskriptor>obj; const deskriptor = <ProjectorElementBuildDeskriptor>obj;
@ -35,5 +36,5 @@ export function isProjectable(obj: any): obj is Projectable {
* Interface for every model, that should be projectable. * Interface for every model, that should be projectable.
*/ */
export interface Projectable extends Displayable { export interface Projectable extends Displayable {
getSlide(): ProjectorElementBuildDeskriptor; getSlide(configSerice?: ConfigService): ProjectorElementBuildDeskriptor;
} }

View File

@ -15,6 +15,8 @@ import { ViewWorkflow } from './view-workflow';
import { ViewCategory } from './view-category'; import { ViewCategory } from './view-category';
import { ViewMotionBlock } from './view-motion-block'; import { ViewMotionBlock } from './view-motion-block';
import { BaseViewModel } from 'app/site/base/base-view-model'; import { BaseViewModel } from 'app/site/base/base-view-model';
import { ConfigService } from 'app/core/ui-services/config.service';
import { ViewMotionChangeRecommendation } from './view-change-recommendation';
/** /**
* The line numbering mode for the motion detail view. * The line numbering mode for the motion detail view.
@ -58,6 +60,7 @@ export class ViewMotion extends BaseAgendaViewModel implements Searchable {
protected _attachments: ViewMediafile[]; protected _attachments: ViewMediafile[];
protected _tags: ViewTag[]; protected _tags: ViewTag[];
protected _parent: ViewMotion; protected _parent: ViewMotion;
protected _changeRecommendations: ViewMotionChangeRecommendation[];
public personalNote: PersonalNoteContent; public personalNote: PersonalNoteContent;
/** /**
@ -157,6 +160,10 @@ export class ViewMotion extends BaseAgendaViewModel implements Searchable {
return this._state; return this._state;
} }
public get changeRecommendations(): ViewMotionChangeRecommendation[] {
return this._changeRecommendations;
}
/** /**
* Checks if the current state of thw workflow is final * Checks if the current state of thw workflow is final
* *
@ -356,7 +363,8 @@ export class ViewMotion extends BaseAgendaViewModel implements Searchable {
block?: ViewMotionBlock, block?: ViewMotionBlock,
attachments?: ViewMediafile[], attachments?: ViewMediafile[],
tags?: ViewTag[], tags?: ViewTag[],
parent?: ViewMotion parent?: ViewMotion,
changeRecommendations?: ViewMotionChangeRecommendation[]
) { ) {
super(Motion.COLLECTIONSTRING); super(Motion.COLLECTIONSTRING);
this._motion = motion; this._motion = motion;
@ -370,6 +378,7 @@ export class ViewMotion extends BaseAgendaViewModel implements Searchable {
this._attachments = attachments; this._attachments = attachments;
this._tags = tags; this._tags = tags;
this._parent = parent; this._parent = parent;
this._changeRecommendations = changeRecommendations;
} }
public getAgendaItem(): ViewItem { public getAgendaItem(): ViewItem {
@ -429,6 +438,8 @@ export class ViewMotion extends BaseAgendaViewModel implements Searchable {
this.updateTags(update); this.updateTags(update);
} else if (update instanceof ViewMotion && update.id !== this.id) { } else if (update instanceof ViewMotion && update.id !== this.id) {
this.updateParent(update); this.updateParent(update);
} else if (update instanceof ViewMotionChangeRecommendation) {
this.updateChangeRecommendation(update);
} }
} }
@ -437,7 +448,7 @@ export class ViewMotion extends BaseAgendaViewModel implements Searchable {
* *
* @param workflow potentially the (changed workflow (state). Needs manual verification * @param workflow potentially the (changed workflow (state). Needs manual verification
*/ */
public updateWorkflow(workflow: ViewWorkflow): void { private updateWorkflow(workflow: ViewWorkflow): void {
if (workflow.id === this.motion.workflow_id) { if (workflow.id === this.motion.workflow_id) {
this._workflow = workflow; this._workflow = workflow;
this._state = workflow.getStateById(this.state_id); this._state = workflow.getStateById(this.state_id);
@ -449,7 +460,7 @@ export class ViewMotion extends BaseAgendaViewModel implements Searchable {
* *
* @param category potentially the changed category. Needs manual verification * @param category potentially the changed category. Needs manual verification
*/ */
public updateCategory(category: ViewCategory): void { private updateCategory(category: ViewCategory): void {
if (this.category_id && category.id === this.motion.category_id) { if (this.category_id && category.id === this.motion.category_id) {
this._category = category; this._category = category;
} }
@ -460,7 +471,7 @@ export class ViewMotion extends BaseAgendaViewModel implements Searchable {
* *
* @param item potentially the changed agenda Item. Needs manual verification * @param item potentially the changed agenda Item. Needs manual verification
*/ */
public updateItem(item: ViewItem): void { private updateItem(item: ViewItem): void {
if (item.id === this.motion.agenda_item_id) { if (item.id === this.motion.agenda_item_id) {
this._item = item; this._item = item;
} }
@ -471,7 +482,7 @@ export class ViewMotion extends BaseAgendaViewModel implements Searchable {
* *
* @param block potentially the changed motion block. Needs manual verification * @param block potentially the changed motion block. Needs manual verification
*/ */
public updateMotionBlock(block: ViewMotionBlock): void { private updateMotionBlock(block: ViewMotionBlock): void {
if (this.motion_block_id && block.id === this.motion.motion_block_id) { if (this.motion_block_id && block.id === this.motion.motion_block_id) {
this._block = block; this._block = block;
} }
@ -482,7 +493,7 @@ export class ViewMotion extends BaseAgendaViewModel implements Searchable {
* *
* @param update potentially the changed agenda Item. Needs manual verification * @param update potentially the changed agenda Item. Needs manual verification
*/ */
public updateUser(update: ViewUser): void { private updateUser(update: ViewUser): void {
if (this.motion.submitters && this.motion.submitters.find(user => user.user_id === update.id)) { if (this.motion.submitters && this.motion.submitters.find(user => user.user_id === update.id)) {
const userIndex = this.motion.submitters.findIndex(submitter => submitter.user_id === update.id); const userIndex = this.motion.submitters.findIndex(submitter => submitter.user_id === update.id);
this.submitters[userIndex] = update; this.submitters[userIndex] = update;
@ -502,7 +513,7 @@ export class ViewMotion extends BaseAgendaViewModel implements Searchable {
* *
* @param mediafile * @param mediafile
*/ */
public updateAttachments(mediafile: ViewMediafile): void { private updateAttachments(mediafile: ViewMediafile): void {
if (this.attachments_id && this.attachments_id.includes(mediafile.id)) { if (this.attachments_id && this.attachments_id.includes(mediafile.id)) {
const attachmentIndex = this.attachments.findIndex(_mediafile => _mediafile.id === mediafile.id); const attachmentIndex = this.attachments.findIndex(_mediafile => _mediafile.id === mediafile.id);
if (attachmentIndex < 0) { if (attachmentIndex < 0) {
@ -513,7 +524,7 @@ export class ViewMotion extends BaseAgendaViewModel implements Searchable {
} }
} }
public updateTags(tag: ViewTag): void { private updateTags(tag: ViewTag): void {
if (this.tags_id && this.tags_id.includes(tag.id)) { if (this.tags_id && this.tags_id.includes(tag.id)) {
const tagIndex = this.tags.findIndex(_tag => _tag.id === tag.id); const tagIndex = this.tags.findIndex(_tag => _tag.id === tag.id);
if (tagIndex < 0) { if (tagIndex < 0) {
@ -524,12 +535,23 @@ export class ViewMotion extends BaseAgendaViewModel implements Searchable {
} }
} }
public updateParent(parent: ViewMotion): void { private updateParent(parent: ViewMotion): void {
if (this.parent_id && this.parent_id === parent.id) { if (this.parent_id && this.parent_id === parent.id) {
this._parent = parent; this._parent = parent;
} }
} }
private updateChangeRecommendation(cr: ViewMotionChangeRecommendation): void {
if (cr.motion_id === this.id) {
const index = this.changeRecommendations.findIndex(_cr => _cr.id === cr.id);
if (index < 0) {
this.changeRecommendations.push(cr);
} else {
this.changeRecommendations[index] = cr;
}
}
}
public hasSupporters(): boolean { public hasSupporters(): boolean {
return !!(this.supporters && this.supporters.length > 0); return !!(this.supporters && this.supporters.length > 0);
} }
@ -561,26 +583,30 @@ export class ViewMotion extends BaseAgendaViewModel implements Searchable {
return this.amendment_paragraphs.length > 0; return this.amendment_paragraphs.length > 0;
} }
public getSlide(): ProjectorElementBuildDeskriptor { public getSlide(configService: ConfigService): ProjectorElementBuildDeskriptor {
return { const slideOptions = [];
getBasicProjectorElement: options => ({
name: Motion.COLLECTIONSTRING, if (this.changeRecommendations && this.changeRecommendations.length) {
id: this.id, slideOptions.push({
getIdentifiers: () => ['name', 'id']
}),
slideOptions: [
{
key: 'mode', key: 'mode',
displayName: 'Change recommendations', displayName: 'Which version?',
default: 'original', default: configService.instant('motions_recommendation_text_mode'),
choices: [ choices: [
{ value: 'original', displayName: 'Original version' }, { value: 'original', displayName: 'Original version' },
{ value: 'changed', displayName: 'Changed version' }, { value: 'changed', displayName: 'Changed version' },
{ value: 'diff', displayName: 'Diff version' }, { value: 'diff', displayName: 'Diff version' },
{ value: 'agreed', displayName: 'Final version' } { value: 'agreed', displayName: 'Final version' }
] ]
});
} }
],
return {
getBasicProjectorElement: options => ({
name: Motion.COLLECTIONSTRING,
id: this.id,
getIdentifiers: () => ['name', 'id']
}),
slideOptions: slideOptions,
projectionDefaultName: 'motions', projectionDefaultName: 'motions',
getDialogTitle: this.getAgendaTitle getDialogTitle: this.getAgendaTitle
}; };