Merge pull request #3869 from FinnStutzenstein/modelInterface

Introduce more basemodel functionality used by the agenda, generic views
This commit is contained in:
Sean 2018-09-14 11:06:30 +02:00 committed by GitHub
commit 9d59da1352
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
52 changed files with 346 additions and 185 deletions

View File

@ -1,4 +1,4 @@
import { ModelConstructor, BaseModel } from '../../shared/models/base.model';
import { ModelConstructor, BaseModel } from '../../shared/models/base/base-model';
/**
* Registeres the mapping of collection strings <--> actual types. Every Model should register itself here.

View File

@ -1,6 +1,6 @@
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { BaseModel } from '../../shared/models/base.model';
import { BaseModel } from '../../shared/models/base/base-model';
import { Observable } from 'rxjs';
import { tap } from 'rxjs/operators';

View File

@ -1,7 +1,7 @@
import { Injectable } from '@angular/core';
import { Observable, Subject } from 'rxjs';
import { BaseModel, ModelConstructor } from 'app/shared/models/base.model';
import { BaseModel, ModelConstructor } from '../../shared/models/base/base-model';
import { CacheService } from './cache.service';
import { CollectionStringModelMapperService } from './collectionStringModelMapper.service';

View File

@ -2,7 +2,7 @@
<mat-select [formControl]="formControl" placeholder="{{listname}}" multiple="{{multiple}}" #thisSelector>
<ngx-mat-select-search [formControl]="filterControl"></ngx-mat-select-search>
<mat-option *ngFor="let selectedItem of filteredItems | async" [value]="selectedItem">
{{selectedItem.toString()}}
{{selectedItem.getTitle(translate)}}
</mat-option>
</mat-select>
</mat-form-field>

View File

@ -2,8 +2,9 @@ import { Component, OnInit, Input, ViewChild } from '@angular/core';
import { FormControl, FormGroup } from '@angular/forms';
import { ReplaySubject, Subject } from 'rxjs';
import { MatSelect } from '@angular/material';
import { SelectorItem } from './search-value-selector.interfaces';
import { takeUntil } from 'rxjs/operators';
import { Displayable } from '../../models/base/displayable';
import { TranslateService } from '@ngx-translate/core';
/**
* Reusable Searchable Value Selector
@ -27,17 +28,6 @@ import { takeUntil } from 'rxjs/operators';
* </os-search-value-selector>
* ```
*
* ### Declaration of a Selector provided as `[InputListValues]=myListValues`:
*
* Every Class that enherits of BaseModel implements the SelectorItem Interface and can
* therefore be used directly in the Selector Component.
*
* ```ts
* import { SelectorItem } from '../../shared/components/search-value-selector/search-value-selector.interfaces';
*
* const myListValues: SelectorItem[];
* myListValues = this.DS.get(User);
* ```
*/
@Component({
@ -60,7 +50,7 @@ export class SearchValueSelectorComponent implements OnInit {
/**
* List of the filtered content, when entering somithing in the search bar
*/
public filteredItems: ReplaySubject<SelectorItem[]> = new ReplaySubject<SelectorItem[]>(1);
public filteredItems: ReplaySubject<Displayable[]> = new ReplaySubject<Displayable[]>(1);
/**
* Decide if this should be a single or multi-select-field
@ -72,7 +62,7 @@ export class SearchValueSelectorComponent implements OnInit {
* The Input List Values
*/
@Input()
public InputListValues: SelectorItem[];
public InputListValues: Displayable[];
/**
* Placeholder of the List
@ -115,7 +105,7 @@ export class SearchValueSelectorComponent implements OnInit {
/**
* Empty constructor
*/
public constructor() {}
public constructor(public translate: TranslateService) {}
/**
* onInit with filter ans subscription on filter
@ -164,7 +154,7 @@ export class SearchValueSelectorComponent implements OnInit {
* places, but can't reflect the changes in both places. Until this can be done this will be unused code
* @param item the selected item to be removed
*/
public remove(item: SelectorItem): void {
public remove(item: Displayable): void {
const myArr = this.thisSelector.value;
const index = myArr.indexOf(item, 0);
// my model was the form according to fix

View File

@ -1,10 +0,0 @@
/**
* Inteface for the Multi-Value-Selector Component to display and use
* the given values.
*/
export interface SelectorItem {
/**
* translates the displayable part of the function to a String
*/
toString(): string;
}

View File

@ -1,6 +1,10 @@
import { BaseModel } from '../base.model';
import { ProjectableBaseModel } from '../base/projectable-base-model';
import { Speaker } from './speaker';
/**
* The representation of the content object for agenda items. The unique combination
* of the collection and id is given.
*/
interface ContentObject {
id: number;
collection: string;
@ -10,11 +14,11 @@ interface ContentObject {
* Representations of agenda Item
* @ignore
*/
export class Item extends BaseModel {
export class Item extends ProjectableBaseModel {
public id: number;
public item_number: string;
public title: string;
public list_view_title: string;
public title_with_type: string;
public comment: string;
public closed: boolean;
public type: number;
@ -30,6 +34,23 @@ export class Item extends BaseModel {
super('agenda/item', input);
}
// Note: This has to be used in the agenda repository
/*public get contentObject(): AgendaBaseModel {
const contentObject = this.DS.get<BaseModel>(this.content_object.collection, this.content_object.id);
if (!contentObject) {
return null;
}
if (contentObject instanceof AgendaBaseModel) {
return contentObject as AgendaBaseModel;
} else {
throw new Error(
`The content object (${this.content_object.collection}, ${this.content_object.id}) of item ${
this.id
} is not a BaseProjectableModel.`
);
}
}*/
public deserialize(input: any): void {
Object.assign(this, input);
@ -40,9 +61,32 @@ export class Item extends BaseModel {
}
}
public toString(): string {
// The repository has to check for the content object and choose which title to use.
// The code below is belongs to the repository
public getTitle(): string {
/*const contentObject: AgendaBaseModel = this.contentObject;
if (contentObject) {
return contentObject.getAgendaTitle();
} else {
return this.title;
}*/
return this.title;
}
// Same here. See comment for getTitle()
public getListTitle(): string {
/*const contentObject: AgendaBaseModel = this.contentObject;
if (contentObject) {
return contentObject.getAgendaTitleWithType();
} else {
return this.title_with_type;
}*/
return this.title_with_type;
}
public getProjectorTitle(): string {
return this.getListTitle();
}
}
BaseModel.registerCollectionElement('agenda/item', Item);
ProjectableBaseModel.registerCollectionElement('agenda/item', Item);

View File

@ -1,4 +1,4 @@
import { Deserializer } from '../deserializer.model';
import { Deserializer } from '../base/deserializer';
/**
* Representation of a speaker in an agenda item

View File

@ -1,4 +1,4 @@
import { Deserializer } from '../deserializer.model';
import { Deserializer } from '../base/deserializer';
/**
* Content of the 'assignment_related_users' property

View File

@ -1,12 +1,12 @@
import { BaseModel } from '../base.model';
import { AssignmentUser } from './assignment-user';
import { Poll } from './poll';
import { AgendaBaseModel } from '../base/agenda-base-model';
/**
* Representation of an assignment.
* @ignore
*/
export class Assignment extends BaseModel {
export class Assignment extends AgendaBaseModel {
public id: number;
public title: string;
public description: string;
@ -19,7 +19,7 @@ export class Assignment extends BaseModel {
public tags_id: number[];
public constructor(input?: any) {
super('assignments/assignment', input);
super('assignments/assignment', 'Assignment', input);
}
public get candidateIds(): number[] {
@ -48,9 +48,13 @@ export class Assignment extends BaseModel {
}
}
public toString(): string {
public getTitle(): string {
return this.title;
}
public getDetailStateURL(): string {
return 'TODO';
}
}
BaseModel.registerCollectionElement('assignments/assignment', Assignment);
AgendaBaseModel.registerCollectionElement('assignments/assignment', Assignment);

View File

@ -1,4 +1,4 @@
import { Deserializer } from '../deserializer.model';
import { Deserializer } from '../base/deserializer';
/**
* Representation of a poll option

View File

@ -1,5 +1,5 @@
import { PollOption } from './poll-option';
import { Deserializer } from '../deserializer.model';
import { Deserializer } from '../base/deserializer';
/**
* Content of the 'polls' property of assignments

View File

@ -0,0 +1,37 @@
import { AgendaInformation } from './agenda-information';
import { ProjectableBaseModel } from './projectable-base-model';
/**
* A base model for models, that can be content objects in the agenda. Provides title and navigation
* information for the agenda.
*/
export abstract class AgendaBaseModel extends ProjectableBaseModel implements AgendaInformation {
protected verboseName: string;
/**
* A Model that inherits from this class should provide a verbose name. It's used by creating
* the agenda title with type.
* @param collectionString
* @param verboseName
* @param input
*/
protected constructor(collectionString: string, verboseName: string, input?: any) {
super(collectionString, input);
this.verboseName = verboseName;
}
public getAgendaTitle(): string {
return this.getTitle();
}
public getAgendaTitleWithType(): string {
// Return the agenda title with the model's verbose name appended
return this.getAgendaTitle() + ' (' + this.verboseName + ')';
}
/**
* Should return the URL to the detail view. Used for the agenda, that the
* user can navigate to the content object.
*/
public abstract getDetailStateURL(): string;
}

View File

@ -0,0 +1,19 @@
/**
* An Interface for all extra information needed for content objects of items.
*/
export interface AgendaInformation {
/**
* Should return the title for the agenda list view.
*/
getAgendaTitle(): string;
/**
* Should return the title for the list of speakers view.
*/
getAgendaTitleWithType(): string;
/**
* Get the url for the detail view, so in the agenda the user can navigate to it.
*/
getDetailStateURL(): string;
}

View File

@ -1,7 +1,7 @@
import { OpenSlidesComponent } from 'app/openslides.component';
import { Deserializable } from './deserializable.model';
import { CollectionStringModelMapperService } from '../../core/services/collectionStringModelMapper.service';
import { SelectorItem } from '../components/search-value-selector/search-value-selector.interfaces';
import { Deserializable } from './deserializable';
import { CollectionStringModelMapperService } from '../../../core/services/collectionStringModelMapper.service';
import { Displayable } from './displayable';
export interface ModelConstructor<T extends BaseModel> {
new (...args: any[]): T;
@ -10,7 +10,7 @@ export interface ModelConstructor<T extends BaseModel> {
/**
* Abstract parent class to set rules and functions for all models.
*/
export abstract class BaseModel extends OpenSlidesComponent implements Deserializable, SelectorItem {
export abstract class BaseModel extends OpenSlidesComponent implements Deserializable, Displayable {
/**
* Register the collection string to the type.
* @param collectionString
@ -56,10 +56,16 @@ export abstract class BaseModel extends OpenSlidesComponent implements Deseriali
}
});
}
/**
* force children to have a toString() method
*/
public abstract toString(): string;
public abstract getTitle(): string;
public getListTitle(): string {
return this.getTitle();
}
public toString(): string {
return this.getTitle();
}
/**
* returns the collectionString.

View File

@ -1,4 +1,4 @@
import { Deserializable } from './deserializable.model';
import { Deserializable } from './deserializable';
/**
* Abstract base class for a basic implementation of Deserializable.

View File

@ -0,0 +1,14 @@
/**
* Every displayble object should have the given functions to give the object's title.
*/
export interface Displayable {
/**
* Should return the title. Alway used except for list view, the agenda and in the projector.
*/
getTitle(): string;
/**
* Should return the title for the list view.
*/
getListTitle(): string;
}

View File

@ -0,0 +1,17 @@
import { BaseModel } from './base-model';
import { Projectable } from './projectable';
export abstract class ProjectableBaseModel extends BaseModel implements Projectable {
protected constructor(collectionString: string, input?: any) {
super(collectionString, input);
}
/**
* This is a Dummy, which should be changed if the projector gets implemented.
*/
public project(): void {}
public getProjectorTitle(): string {
return this.getTitle();
}
}

View File

@ -0,0 +1,14 @@
/**
* Interface for every model, that should be projectable.
*/
export interface Projectable {
/**
* Should return the title for the projector.
*/
getProjectorTitle(): string;
/**
* Dummy. I don't know how the projctor system will be, so this function may change
*/
project(): void;
}

View File

@ -1,4 +1,4 @@
import { BaseModel } from '../base.model';
import { BaseModel } from '../base/base-model';
/**
* Representation of chat messages.
@ -14,8 +14,8 @@ export class ChatMessage extends BaseModel {
super('core/chat-message', input);
}
public toString(): string {
return this.message;
public getTitle(): string {
return 'Chatmessage';
}
}

View File

@ -1,4 +1,4 @@
import { BaseModel } from '../base.model';
import { BaseModel } from '../base/base-model';
/**
* Representation of a config variable
@ -13,7 +13,7 @@ export class Config extends BaseModel {
super('core/config', input);
}
public toString(): string {
public getTitle(): string {
return this.key;
}
}

View File

@ -1,10 +1,10 @@
import { BaseModel } from '../base.model';
import { ProjectableBaseModel } from '../base/projectable-base-model';
/**
* Representation of a countdown
* @ignore
*/
export class Countdown extends BaseModel {
export class Countdown extends ProjectableBaseModel {
public id: number;
public description: string;
public default_time: number;
@ -15,9 +15,9 @@ export class Countdown extends BaseModel {
super('core/countdown');
}
public toString(): string {
public getTitle(): string {
return this.description;
}
}
BaseModel.registerCollectionElement('core/countdown', Countdown);
ProjectableBaseModel.registerCollectionElement('core/countdown', Countdown);

View File

@ -1,10 +1,10 @@
import { BaseModel } from '../base.model';
import { ProjectableBaseModel } from '../base/projectable-base-model';
/**
* Representation of a projector message.
* @ignore
*/
export class ProjectorMessage extends BaseModel {
export class ProjectorMessage extends ProjectableBaseModel {
public id: number;
public message: string;
@ -12,9 +12,9 @@ export class ProjectorMessage extends BaseModel {
super('core/projector-message', input);
}
public toString(): string {
return this.message;
public getTitle(): string {
return 'Projectormessage';
}
}
BaseModel.registerCollectionElement('core/projector-message', ProjectorMessage);
ProjectableBaseModel.registerCollectionElement('core/projector-message', ProjectorMessage);

View File

@ -1,4 +1,4 @@
import { BaseModel } from '../base.model';
import { BaseModel } from '../base/base-model';
/**
* Representation of a projector. Has the nested property "projectiondefaults"
@ -19,7 +19,7 @@ export class Projector extends BaseModel {
super('core/projector', input);
}
public toString(): string {
public getTitle(): string {
return this.name;
}
}

View File

@ -1,4 +1,4 @@
import { BaseModel } from '../base.model';
import { BaseModel } from '../base/base-model';
/**
* Representation of a tag.
@ -12,7 +12,7 @@ export class Tag extends BaseModel {
super('core/tag', input);
}
public toString(): string {
public getTitle(): string {
return this.name;
}
}

View File

@ -1,4 +1,4 @@
import { Deserializer } from '../deserializer.model';
import { Deserializer } from '../base/deserializer';
/**
* The name and the type of a mediaFile.

View File

@ -1,11 +1,11 @@
import { BaseModel } from '../base.model';
import { File } from './file';
import { ProjectableBaseModel } from '../base/projectable-base-model';
/**
* Representation of MediaFile. Has the nested property "File"
* @ignore
*/
export class Mediafile extends BaseModel {
export class Mediafile extends ProjectableBaseModel {
public id: number;
public title: string;
public mediafile: File;
@ -24,9 +24,9 @@ export class Mediafile extends BaseModel {
this.mediafile = new File(input.mediafile);
}
public toString(): string {
public getTitle(): string {
return this.title;
}
}
BaseModel.registerCollectionElement('amediafiles/mediafile', Mediafile);
ProjectableBaseModel.registerCollectionElement('amediafiles/mediafile', Mediafile);

View File

@ -1,4 +1,4 @@
import { BaseModel } from '../base.model';
import { BaseModel } from '../base/base-model';
/**
* Representation of a motion category. Has the nested property "File"
@ -13,7 +13,7 @@ export class Category extends BaseModel {
super('motions/category', input);
}
public toString(): string {
public getTitle(): string {
return this.prefix + ' - ' + this.name;
}
}

View File

@ -1,22 +1,25 @@
import { BaseModel } from '../base.model';
import { Item } from '../agenda/item';
import { AgendaBaseModel } from '../base/agenda-base-model';
/**
* Representation of a motion block.
* @ignore
*/
export class MotionBlock extends BaseModel {
export class MotionBlock extends AgendaBaseModel {
public id: number;
public title: string;
public agenda_item_id: number;
public constructor(input?: any) {
super('motions/motion-block', input);
super('motions/motion-block', 'Motion block', input);
}
public toString(): string {
public getTitle(): string {
return this.title;
}
public getDetailStateURL(): string {
return 'TODO';
}
}
BaseModel.registerCollectionElement('motions/motion-block', MotionBlock);
AgendaBaseModel.registerCollectionElement('motions/motion-block', MotionBlock);

View File

@ -1,4 +1,4 @@
import { BaseModel } from '../base.model';
import { BaseModel } from '../base/base-model';
/**
* Representation of a motion change recommendation.
@ -19,8 +19,8 @@ export class MotionChangeReco extends BaseModel {
super('motions/motion-change-recommendation', input);
}
public toString(): string {
return this.text;
public getTitle(): string {
return 'Changerecommendation';
}
}

View File

@ -1,4 +1,4 @@
import { BaseModel } from '../base.model';
import { BaseModel } from '../base/base-model';
/**
* Representation of a motion category. Has the nested property "File"
@ -14,7 +14,7 @@ export class MotionCommentSection extends BaseModel {
super('motions/motion-comment-section', input);
}
public toString(): string {
public getTitle(): string {
return this.name;
}
}

View File

@ -1,4 +1,4 @@
import { Deserializer } from '../deserializer.model';
import { Deserializer } from '../base/deserializer';
/**
* Representation of a Motion Comment.

View File

@ -1,4 +1,4 @@
import { Deserializer } from '../deserializer.model';
import { Deserializer } from '../base/deserializer';
/**
* Representation of a Motion Log.

View File

@ -1,4 +1,4 @@
import { Deserializer } from '../deserializer.model';
import { Deserializer } from '../base/deserializer';
import { User } from '../users/user';
/**

View File

@ -1,9 +1,9 @@
import { BaseModel } from '../base.model';
import { MotionSubmitter } from './motion-submitter';
import { MotionLog } from './motion-log';
import { Category } from './category';
import { MotionComment } from './motion-comment';
import { Workflow } from './workflow';
import { AgendaBaseModel } from '../base/agenda-base-model';
/**
* Representation of Motion.
@ -12,7 +12,7 @@ import { Workflow } from './workflow';
*
* @ignore
*/
export class Motion extends BaseModel {
export class Motion extends AgendaBaseModel {
public id: number;
public identifier: string;
public title: string;
@ -36,12 +36,12 @@ export class Motion extends BaseModel {
public recommendation_extension: string;
public tags_id: number[];
public attachments_id: number[];
public polls: BaseModel[];
public polls: Object[];
public agenda_item_id: number;
public log_messages: MotionLog[];
public constructor(input?: any) {
super('motions/motion', input);
super('motions/motion', 'Motion', input);
}
/**
@ -62,13 +62,32 @@ export class Motion extends BaseModel {
.map((submitter: MotionSubmitter) => submitter.user_id);
}
/**
* returns the Motion name
*/
public toString(): string {
public getTitle(): string {
return this.title;
}
public getAgendaTitle(): string {
// if the identifier is set, the title will be 'Motion <identifier>'.
if (this.identifier) {
return 'Motion ' + this.identifier;
} else {
return this.getTitle();
}
}
public getAgendaTitleWithType(): string {
// Append the verbose name only, if not the special format 'Motion <identifier>' is used.
if (this.identifier) {
return 'Motion ' + this.identifier;
} else {
return this.getTitle() + ' (' + this.verboseName + ')';
}
}
public getDetailStateURL(): string {
return 'TODO';
}
public deserialize(input: any): void {
Object.assign(this, input);
@ -91,6 +110,6 @@ export class Motion extends BaseModel {
/**
* Hack to get them loaded at last
*/
BaseModel.registerCollectionElement('motions/motion', Motion);
BaseModel.registerCollectionElement('motions/category', Category);
BaseModel.registerCollectionElement('motions/workflow', Workflow);
AgendaBaseModel.registerCollectionElement('motions/motion', Motion);
AgendaBaseModel.registerCollectionElement('motions/category', Category);
AgendaBaseModel.registerCollectionElement('motions/workflow', Workflow);

View File

@ -1,4 +1,4 @@
import { Deserializer } from '../deserializer.model';
import { Deserializer } from '../base/deserializer';
import { Workflow } from './workflow';
/**

View File

@ -1,4 +1,4 @@
import { BaseModel } from '../base.model';
import { BaseModel } from '../base/base-model';
import { WorkflowState } from './workflow-state';
/**
@ -54,7 +54,7 @@ export class Workflow extends BaseModel {
}
}
public toString(): string {
public getTitle(): string {
return this.name;
}
}

View File

@ -1,10 +1,10 @@
import { BaseModel } from '../base.model';
import { AgendaBaseModel } from '../base/agenda-base-model';
/**
* Representation of a topic.
* @ignore
*/
export class Topic extends BaseModel {
export class Topic extends AgendaBaseModel {
public id: number;
public title: string;
public text: string;
@ -12,12 +12,21 @@ export class Topic extends BaseModel {
public agenda_item_id: number;
public constructor(input?: any) {
super('topics/topic', input);
super('topics/topic', 'Topic', input);
}
public toString(): string {
public getTitle(): string {
return this.title;
}
public getAgendaTitleWithType(): string {
// Do not append ' (Topic)' to the title.
return this.getAgendaTitle();
}
public getDetailStateURL(): string {
return 'TODO';
}
}
BaseModel.registerCollectionElement('topics/topic', Topic);
AgendaBaseModel.registerCollectionElement('topics/topic', Topic);

View File

@ -1,4 +1,4 @@
import { BaseModel } from '../base.model';
import { BaseModel } from '../base/base-model';
/**
* Representation of user group.
@ -13,7 +13,7 @@ export class Group extends BaseModel {
super('users/group', input);
}
public toString(): string {
public getTitle(): string {
return this.name;
}
}

View File

@ -1,5 +1,4 @@
import { BaseModel } from '../base.model';
import { User } from './user';
import { BaseModel } from '../base/base-model';
/**
* Representation of users personal note.
@ -14,8 +13,8 @@ export class PersonalNote extends BaseModel {
super('users/personal-note', input);
}
public toString(): string {
return this.notes.toString();
public getTitle(): string {
return 'Personal note';
}
}

View File

@ -1,10 +1,10 @@
import { BaseModel } from '../base.model';
import { ProjectableBaseModel } from '../base/projectable-base-model';
/**
* Representation of a user in contrast to the operator.
* @ignore
*/
export class User extends BaseModel {
export class User extends ProjectableBaseModel {
public id: number;
public username: string;
public title: string;
@ -77,9 +77,13 @@ export class User extends BaseModel {
return shortName.trim();
}
public toString(): string {
public getTitle(): string {
return this.full_name;
}
public getListViewTitle(): string {
return this.short_name;
}
}
BaseModel.registerCollectionElement('users/user', User);
ProjectableBaseModel.registerCollectionElement('users/user', User);

View File

@ -1,7 +1,7 @@
import { OpenSlidesComponent } from '../openslides.component';
import { BehaviorSubject, Observable } from 'rxjs';
import { BaseViewModel } from './base-view-model';
import { BaseModel, ModelConstructor } from '../shared/models/base.model';
import { BaseModel, ModelConstructor } from '../shared/models/base/base-model';
import { CollectionStringModelMapperService } from '../core/services/collectionStringModelMapper.service';
import { DataStoreService } from '../core/services/data-store.service';

View File

@ -1,39 +1,19 @@
import { TranslateService } from '@ngx-translate/core';
import { BaseModel } from '../shared/models/base.model';
import { BaseModel } from '../shared/models/base/base-model';
import { Displayable } from '../shared/models/base/displayable';
/**
* Base class for view models. alls view models should have titles.
*/
export abstract class BaseViewModel {
export abstract class BaseViewModel implements Displayable {
public abstract updateValues(update: BaseModel): void;
/**
* Should return the title for the detail view.
* @param translate
*/
public abstract getTitle(translate: TranslateService): string;
public abstract getTitle(): string;
/**
* Should return the title for the list view.
* @param translate
*/
public getListTitle(translate: TranslateService): string {
return this.getTitle(translate);
public getListTitle(): string {
return this.getTitle();
}
/**
* Should return the title for the projector.
* @param translate
*/
public getProjector(translate: TranslateService): string {
return this.getTitle(translate);
}
/**
* Should return the title for the agenda list view.
* @param translate
*/
public getAgendaTitle(translate: TranslateService): string {
return this.getTitle(translate);
public toString(): string {
return this.getTitle();
}
}

View File

@ -153,8 +153,8 @@
</div>
<div *ngIf="!editMotion || !newMotion">
<h3 translate>Submitters</h3>
<ul *ngFor="let submitters of motion.submitters">
<li>{{submitters}}</li>
<ul *ngFor="let submitter of motion.submitters">
<li>{{submitter.full_name}}</li>
</ul>
</div>
</div>
@ -169,8 +169,8 @@
</div>
<div *ngIf="!editMotion && motion.hasSupporters()">
<h3 translate>Supporters</h3>
<ul *ngFor="let supporters of motion.supporters">
<li>{{supporters}}</li>
<ul *ngFor="let supporter of motion.supporters">
<li>{{supporter.full_name}}</li>
</ul>
</div>
</div>

View File

@ -3,7 +3,7 @@ import { Category } from '../../../shared/models/motions/category';
import { User } from '../../../shared/models/users/user';
import { Workflow } from '../../../shared/models/motions/workflow';
import { WorkflowState } from '../../../shared/models/motions/workflow-state';
import { BaseModel } from '../../../shared/models/base.model';
import { BaseModel } from '../../../shared/models/base/base-model';
import { BaseViewModel } from '../../base-view-model';
import { TranslateService } from '@ngx-translate/core';

View File

@ -296,14 +296,14 @@ class Item(RESTModelMixin, models.Model):
'method on your related model.')
@property
def list_view_title(self):
def title_with_type(self):
"""
Return get_agenda_list_view_title() from the content_object.
Return get_agenda_title_with_type() from the content_object.
"""
try:
return self.content_object.get_agenda_list_view_title()
return self.content_object.get_agenda_title_with_type()
except AttributeError:
raise NotImplementedError('You have to provide a get_agenda_list_view_title '
raise NotImplementedError('You have to provide a get_agenda_title_with_type '
'method on your related model.')
def is_internal(self):

View File

@ -45,7 +45,7 @@ class ItemSerializer(ModelSerializer):
'id',
'item_number',
'title',
'list_view_title',
'title_with_type',
'comment',
'closed',
'type',

View File

@ -338,16 +338,17 @@ class Assignment(RESTModelMixin, models.Model):
agenda_item_update_information: Dict[str, Any] = {}
def get_agenda_title(self):
"""
Returns the title for the agenda.
"""
return str(self)
def get_agenda_list_view_title(self):
def get_agenda_title_with_type(self):
"""
Return a title string for the agenda list view.
Contains agenda item number, title and assignment verbose name.
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 '%s (%s)' % (self.title, _(self._meta.verbose_name))
return '%s (%s)' % (self.get_agenda_title(), _(self._meta.verbose_name))
@property
def agenda_item(self):

View File

@ -436,26 +436,30 @@ class Motion(RESTModelMixin, models.Model):
def get_agenda_title(self):
"""
Return a simple title string for the agenda.
Return the title string for the agenda.
Returns only the motion title so that you have only agenda item number
and title in the agenda.
"""
return str(self)
def get_agenda_list_view_title(self):
"""
Return a title string for the agenda list view.
Returns only the motion title so that you have agenda item number,
title and motion identifier in 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:
string = '%s %s' % (_(self._meta.verbose_name), self.identifier)
title = '%s %s' % (_(self._meta.verbose_name), self.identifier)
else:
string = '%s (%s)' % (_(self._meta.verbose_name), self.title)
return string
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 = '%s %s' % (_(self._meta.verbose_name), self.identifier)
else:
title = '%s (%s)' % (self.title, _(self._meta.verbose_name))
return title
@property
def agenda_item(self):
@ -781,6 +785,7 @@ class MotionBlock(RESTModelMixin, models.Model):
agenda_items = GenericRelation(Item, related_name='topics')
class Meta:
verbose_name = ugettext_noop('Motion block')
default_permissions = ()
def __str__(self):
@ -821,8 +826,8 @@ class MotionBlock(RESTModelMixin, models.Model):
def get_agenda_title(self):
return self.title
def get_agenda_list_view_title(self):
return self.title
def get_agenda_title_with_type(self):
return '%s (%s)' % (self.get_agenda_title(), _(self._meta.verbose_name))
class MotionLog(RESTModelMixin, models.Model):

View File

@ -78,7 +78,13 @@ class Topic(RESTModelMixin, models.Model):
return self.agenda_item.pk
def get_agenda_title(self):
"""
Returns the title for the agenda.
"""
return self.title
def get_agenda_list_view_title(self):
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()

View File

@ -69,7 +69,7 @@ class RetrieveItem(TestCase):
'content_object',)))
forbidden_keys = (
'item_number',
'list_view_title',
'title_with_type',
'comment',
'closed',
'type',