Merge pull request #4227 from FinnStutzenstein/clos
Projector reference for CLOS
This commit is contained in:
commit
44851af172
@ -56,6 +56,7 @@ export class Projector extends BaseModel<Projector> {
|
|||||||
public name: string;
|
public name: string;
|
||||||
public width: number;
|
public width: number;
|
||||||
public height: number;
|
public height: number;
|
||||||
|
public reference_projector_id: number;
|
||||||
public projectiondefaults: ProjectionDefault[];
|
public projectiondefaults: ProjectionDefault[];
|
||||||
|
|
||||||
public constructor(input?: any) {
|
public constructor(input?: any) {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { BehaviorSubject, Observable } from 'rxjs';
|
import { BehaviorSubject, Observable, Subject } from 'rxjs';
|
||||||
|
|
||||||
import { OpenSlidesComponent } from '../../openslides.component';
|
import { OpenSlidesComponent } from '../../openslides.component';
|
||||||
import { BaseViewModel } from './base-view-model';
|
import { BaseViewModel } from './base-view-model';
|
||||||
@ -24,6 +24,11 @@ export abstract class BaseRepository<V extends BaseViewModel, M extends BaseMode
|
|||||||
*/
|
*/
|
||||||
protected readonly viewModelListSubject: BehaviorSubject<V[]> = new BehaviorSubject<V[]>([]);
|
protected readonly viewModelListSubject: BehaviorSubject<V[]> = new BehaviorSubject<V[]>([]);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Observable subject for any changes of view models.
|
||||||
|
*/
|
||||||
|
protected readonly generalViewModelSubject: Subject<V> = new Subject<V>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construction routine for the base repository
|
* Construction routine for the base repository
|
||||||
*
|
*
|
||||||
@ -149,6 +154,13 @@ export abstract class BaseRepository<V extends BaseViewModel, M extends BaseMode
|
|||||||
return this.viewModelListSubject.asObservable().pipe(auditTime(1));
|
return this.viewModelListSubject.asObservable().pipe(auditTime(1));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This observable fires every time an object is changed in the repository.
|
||||||
|
*/
|
||||||
|
public getGeneralViewModelObservable(): Observable<V> {
|
||||||
|
return this.generalViewModelSubject.asObservable();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates the ViewModel observable using a ViewModel corresponding to the id
|
* Updates the ViewModel observable using a ViewModel corresponding to the id
|
||||||
*/
|
*/
|
||||||
@ -156,6 +168,7 @@ export abstract class BaseRepository<V extends BaseViewModel, M extends BaseMode
|
|||||||
if (this.viewModelSubjects[id]) {
|
if (this.viewModelSubjects[id]) {
|
||||||
this.viewModelSubjects[id].next(this.viewModelStore[id]);
|
this.viewModelSubjects[id].next(this.viewModelStore[id]);
|
||||||
}
|
}
|
||||||
|
this.generalViewModelSubject.next(this.viewModelStore[id]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -73,6 +73,20 @@
|
|||||||
<span translate>Required</span>
|
<span translate>Required</span>
|
||||||
</mat-hint>
|
</mat-hint>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
|
|
||||||
|
<!-- Reference projector for the current list of speakers -->
|
||||||
|
<h3 translate>Current list fo speakers reference</h3>
|
||||||
|
<mat-form-field>
|
||||||
|
<mat-select formControlName="reference_projector_id" placeholder="{{ 'Reference projector' | translate }}">
|
||||||
|
<mat-option [value]="projector.id">
|
||||||
|
<span translate>self</span>
|
||||||
|
</mat-option>
|
||||||
|
<mat-option *ngFor="let refProjector of getReferenceProjectorsFor(projector)" [value]="refProjector.id">
|
||||||
|
<span>{{ refProjector.getTitle() | translate }}</span>
|
||||||
|
</mat-option>
|
||||||
|
</mat-select>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
<h3 translate>Resolution and size</h3>
|
<h3 translate>Resolution and size</h3>
|
||||||
<!-- Aspect ratio field -->
|
<!-- Aspect ratio field -->
|
||||||
<mat-radio-group formControlName="aspectRatio" [name]="projector.id">
|
<mat-radio-group formControlName="aspectRatio" [name]="projector.id">
|
||||||
@ -82,6 +96,8 @@
|
|||||||
</mat-radio-group>
|
</mat-radio-group>
|
||||||
<mat-slider [thumbLabel]="true" formControlName="width" min="800" max="3840" step="10"></mat-slider>
|
<mat-slider [thumbLabel]="true" formControlName="width" min="800" max="3840" step="10"></mat-slider>
|
||||||
{{ updateForm.value.width }}
|
{{ updateForm.value.width }}
|
||||||
|
|
||||||
|
<!-- Clock -->
|
||||||
<div>
|
<div>
|
||||||
<mat-checkbox formControlName="clock">
|
<mat-checkbox formControlName="clock">
|
||||||
<span translate>Show clock</span>
|
<span translate>Show clock</span>
|
||||||
|
@ -91,7 +91,8 @@ export class ProjectorListComponent extends BaseViewComponent implements OnInit
|
|||||||
name: ['', Validators.required],
|
name: ['', Validators.required],
|
||||||
aspectRatio: ['', Validators.required],
|
aspectRatio: ['', Validators.required],
|
||||||
width: [0, Validators.required],
|
width: [0, Validators.required],
|
||||||
clock: [true]
|
clock: [true],
|
||||||
|
reference_projector_id: []
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -184,11 +185,16 @@ export class ProjectorListComponent extends BaseViewComponent implements OnInit
|
|||||||
}
|
}
|
||||||
this.editId = projector.id;
|
this.editId = projector.id;
|
||||||
this.updateForm.reset();
|
this.updateForm.reset();
|
||||||
|
|
||||||
|
const reference_projector_id = projector.reference_projector_id
|
||||||
|
? projector.reference_projector_id
|
||||||
|
: projector.id;
|
||||||
this.updateForm.patchValue({
|
this.updateForm.patchValue({
|
||||||
name: projector.name,
|
name: projector.name,
|
||||||
aspectRatio: this.getAspectRatioKey(projector),
|
aspectRatio: this.getAspectRatioKey(projector),
|
||||||
width: projector.width,
|
width: projector.width,
|
||||||
clock: this.clockSlideService.isProjectedOn(projector)
|
clock: this.clockSlideService.isProjectedOn(projector),
|
||||||
|
reference_projector_id: reference_projector_id
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -215,7 +221,8 @@ export class ProjectorListComponent extends BaseViewComponent implements OnInit
|
|||||||
const updateProjector: Partial<Projector> = {
|
const updateProjector: Partial<Projector> = {
|
||||||
name: this.updateForm.value.name,
|
name: this.updateForm.value.name,
|
||||||
width: this.updateForm.value.width,
|
width: this.updateForm.value.width,
|
||||||
height: Math.round(this.updateForm.value.width / aspectRatios[this.updateForm.value.aspectRatio])
|
height: Math.round(this.updateForm.value.width / aspectRatios[this.updateForm.value.aspectRatio]),
|
||||||
|
reference_projector_id: this.updateForm.value.reference_projector_id
|
||||||
};
|
};
|
||||||
try {
|
try {
|
||||||
await this.clockSlideService.setProjectedOn(projector, this.updateForm.value.clock);
|
await this.clockSlideService.setProjectedOn(projector, this.updateForm.value.clock);
|
||||||
@ -237,4 +244,14 @@ export class ProjectorListComponent extends BaseViewComponent implements OnInit
|
|||||||
this.repo.delete(projector).then(null, this.raiseError);
|
this.repo.delete(projector).then(null, this.raiseError);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all available reference projectors for the given projector. These
|
||||||
|
* projectors are all existing projectors exluding the given projector
|
||||||
|
*
|
||||||
|
* @returns all available reference projectors
|
||||||
|
*/
|
||||||
|
public getReferenceProjectorsFor(projector: ViewProjector): ViewProjector[] {
|
||||||
|
return this.repo.getViewModelList().filter(p => p.id !== projector.id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -48,6 +48,10 @@ export class ViewProjector extends BaseViewModel {
|
|||||||
return this.projector ? this.projector.scroll : null;
|
return this.projector ? this.projector.scroll : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public get reference_projector_id(): number {
|
||||||
|
return this.projector ? this.projector.reference_projector_id : null;
|
||||||
|
}
|
||||||
|
|
||||||
public constructor(projector?: Projector) {
|
public constructor(projector?: Projector) {
|
||||||
super();
|
super();
|
||||||
this._projector = projector;
|
this._projector = projector;
|
||||||
|
@ -2,6 +2,10 @@ import { Injectable } from '@angular/core';
|
|||||||
import { ProjectorService } from 'app/core/services/projector.service';
|
import { ProjectorService } from 'app/core/services/projector.service';
|
||||||
import { ViewProjector } from '../models/view-projector';
|
import { ViewProjector } from '../models/view-projector';
|
||||||
import { IdentifiableProjectorElement } from 'app/shared/models/core/projector';
|
import { IdentifiableProjectorElement } from 'app/shared/models/core/projector';
|
||||||
|
import { ProjectorRepositoryService } from './projector-repository.service';
|
||||||
|
import { BehaviorSubject, Observable } from 'rxjs';
|
||||||
|
import { SlideManager } from 'app/slides/services/slide-manager.service';
|
||||||
|
import { AgendaBaseModel } from 'app/shared/models/base/agenda-base-model';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*/
|
*/
|
||||||
@ -9,8 +13,28 @@ import { IdentifiableProjectorElement } from 'app/shared/models/core/projector';
|
|||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
})
|
})
|
||||||
export class CurrentListOfSpeakersSlideService {
|
export class CurrentListOfSpeakersSlideService {
|
||||||
public constructor(private projectorService: ProjectorService) {}
|
private currentItemIds: { [projectorId: number]: BehaviorSubject<number | null> } = {};
|
||||||
|
|
||||||
|
public constructor(
|
||||||
|
private projectorService: ProjectorService,
|
||||||
|
private projectorRepo: ProjectorRepositoryService,
|
||||||
|
private slideManager: SlideManager
|
||||||
|
) {
|
||||||
|
this.projectorRepo.getGeneralViewModelObservable().subscribe(projector => {
|
||||||
|
const itemId = this.getCurrentAgendaItemIdForProjector(projector);
|
||||||
|
if (this.currentItemIds[projector.id]) {
|
||||||
|
this.currentItemIds[projector.id].next(itemId);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the basic projector element for the CLOS slide. If overlay=True, the projector element
|
||||||
|
* will be the overlay instead of the slide.
|
||||||
|
*
|
||||||
|
* @param overlay Wether to have a slide or overlay
|
||||||
|
* @returns the identifiable CLOS projector element.
|
||||||
|
*/
|
||||||
private getCurrentListOfSpeakersProjectorElement(overlay: boolean): IdentifiableProjectorElement {
|
private getCurrentListOfSpeakersProjectorElement(overlay: boolean): IdentifiableProjectorElement {
|
||||||
return {
|
return {
|
||||||
name: overlay ? 'agenda/current-list-of-speakers-overlay' : 'agenda/current-list-of-speakers',
|
name: overlay ? 'agenda/current-list-of-speakers-overlay' : 'agenda/current-list-of-speakers',
|
||||||
@ -19,6 +43,51 @@ export class CurrentListOfSpeakersSlideService {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an observable for the agenda item id of the currently projected element on the
|
||||||
|
* given projector.
|
||||||
|
*
|
||||||
|
* @param projector The projector to observe.
|
||||||
|
* @returns An observalbe for the agenda item id. Null, if no element with an agenda item is shown.
|
||||||
|
*/
|
||||||
|
public getAgendaItemIdObservable(projector: ViewProjector): Observable<number | null> {
|
||||||
|
if (!this.currentItemIds[projector.id]) {
|
||||||
|
const itemId = this.getCurrentAgendaItemIdForProjector(projector);
|
||||||
|
this.currentItemIds[projector.id] = new BehaviorSubject<number | null>(itemId);
|
||||||
|
}
|
||||||
|
return this.currentItemIds[projector.id].asObservable();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tries to get the agenda item id for one non stable element on the projector.
|
||||||
|
*
|
||||||
|
* @param projector The projector
|
||||||
|
* @returns The agenda item id or null, if there is no such projector element.
|
||||||
|
*/
|
||||||
|
private getCurrentAgendaItemIdForProjector(projector: ViewProjector): number | null {
|
||||||
|
const nonStableElements = projector.elements.filter(element => !element.stable);
|
||||||
|
if (nonStableElements.length > 0) {
|
||||||
|
const nonStableElement = this.slideManager.getIdentifialbeProjectorElement(nonStableElements[0]); // The normal case is just one non stable slide
|
||||||
|
try {
|
||||||
|
const model = this.projectorService.getModelFromProjectorElement(nonStableElement);
|
||||||
|
if (model instanceof AgendaBaseModel) {
|
||||||
|
// TODO: Use repositories associated to models
|
||||||
|
return (<any>model).agenda_item_id;
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
// make TypeScript silent.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Queries, if the slide/overlay is projected on the given projector.
|
||||||
|
*
|
||||||
|
* @param projector The projector
|
||||||
|
* @param overlay True, if we query for an overlay instead of the slide
|
||||||
|
* @returns if the slide/overlay is projected on the projector
|
||||||
|
*/
|
||||||
public isProjectedOn(projector: ViewProjector, overlay: boolean): boolean {
|
public isProjectedOn(projector: ViewProjector, overlay: boolean): boolean {
|
||||||
return this.projectorService.isProjectedOn(
|
return this.projectorService.isProjectedOn(
|
||||||
this.getCurrentListOfSpeakersProjectorElement(overlay),
|
this.getCurrentListOfSpeakersProjectorElement(overlay),
|
||||||
@ -26,6 +95,12 @@ export class CurrentListOfSpeakersSlideService {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Toggle the projection state of the slide/overlay on the given projector
|
||||||
|
*
|
||||||
|
* @param projector The projector
|
||||||
|
* @param overlay Slide or overlay
|
||||||
|
*/
|
||||||
public async toggleOn(projector: ViewProjector, overlay: boolean): Promise<void> {
|
public async toggleOn(projector: ViewProjector, overlay: boolean): Promise<void> {
|
||||||
const isClosProjected = this.isProjectedOn(projector, overlay);
|
const isClosProjected = this.isProjectedOn(projector, overlay);
|
||||||
if (isClosProjected) {
|
if (isClosProjected) {
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import { Component, OnInit } from '@angular/core';
|
import { Component } from '@angular/core';
|
||||||
|
|
||||||
import { BaseSlideComponent } from 'app/slides/base-slide-component';
|
import { BaseSlideComponent } from 'app/slides/base-slide-component';
|
||||||
import { HttpService } from 'app/core/services/http.service';
|
|
||||||
import { AgendaCurrentListOfSpeakersSlideData } from '../base/agenda-current-list-of-speakers-slide-data';
|
import { AgendaCurrentListOfSpeakersSlideData } from '../base/agenda-current-list-of-speakers-slide-data';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@ -9,15 +8,10 @@ import { AgendaCurrentListOfSpeakersSlideData } from '../base/agenda-current-lis
|
|||||||
templateUrl: './agenda-current-list-of-speakers-overlay-slide.component.html',
|
templateUrl: './agenda-current-list-of-speakers-overlay-slide.component.html',
|
||||||
styleUrls: ['./agenda-current-list-of-speakers-overlay-slide.component.scss']
|
styleUrls: ['./agenda-current-list-of-speakers-overlay-slide.component.scss']
|
||||||
})
|
})
|
||||||
export class AgendaCurrentListOfSpeakersOverlaySlideComponent
|
export class AgendaCurrentListOfSpeakersOverlaySlideComponent extends BaseSlideComponent<
|
||||||
extends BaseSlideComponent<AgendaCurrentListOfSpeakersSlideData>
|
AgendaCurrentListOfSpeakersSlideData
|
||||||
implements OnInit {
|
> {
|
||||||
public constructor(private http: HttpService) {
|
public constructor() {
|
||||||
super();
|
super();
|
||||||
console.log(this.http);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ngOnInit(): void {
|
|
||||||
console.log('Hello from current list of speakers overlay');
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,5 @@ export class AgendaCurrentListOfSpeakersSlideComponent extends BaseSlideComponen
|
|||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
public ngOnInit(): void {
|
public ngOnInit(): void {}
|
||||||
console.log('Hello from current list of speakers slide');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,20 +1,14 @@
|
|||||||
import { Component, OnInit } from '@angular/core';
|
import { Component } from '@angular/core';
|
||||||
import { BaseSlideComponent } from 'app/slides/base-slide-component';
|
import { BaseSlideComponent } from 'app/slides/base-slide-component';
|
||||||
import { CoreCountdownSlideData } from './core-countdown-slide-data';
|
import { CoreCountdownSlideData } from './core-countdown-slide-data';
|
||||||
import { HttpService } from 'app/core/services/http.service';
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'os-core-countdown-slide',
|
selector: 'os-core-countdown-slide',
|
||||||
templateUrl: './core-countdown-slide.component.html',
|
templateUrl: './core-countdown-slide.component.html',
|
||||||
styleUrls: ['./core-countdown-slide.component.scss']
|
styleUrls: ['./core-countdown-slide.component.scss']
|
||||||
})
|
})
|
||||||
export class CoreCountdownSlideComponent extends BaseSlideComponent<CoreCountdownSlideData> implements OnInit {
|
export class CoreCountdownSlideComponent extends BaseSlideComponent<CoreCountdownSlideData> {
|
||||||
public constructor(private http: HttpService) {
|
public constructor() {
|
||||||
super();
|
super();
|
||||||
console.log(this.http);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ngOnInit(): void {
|
|
||||||
console.log('Hello from countdown slide');
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { Component, OnInit } from '@angular/core';
|
import { Component } from '@angular/core';
|
||||||
import { BaseSlideComponent } from 'app/slides/base-slide-component';
|
import { BaseSlideComponent } from 'app/slides/base-slide-component';
|
||||||
import { MotionsMotionSlideData } from './motions-motion-slide-data';
|
import { MotionsMotionSlideData } from './motions-motion-slide-data';
|
||||||
|
|
||||||
@ -7,12 +7,8 @@ import { MotionsMotionSlideData } from './motions-motion-slide-data';
|
|||||||
templateUrl: './motions-motion-slide.component.html',
|
templateUrl: './motions-motion-slide.component.html',
|
||||||
styleUrls: ['./motions-motion-slide.component.scss']
|
styleUrls: ['./motions-motion-slide.component.scss']
|
||||||
})
|
})
|
||||||
export class MotionsMotionSlideComponent extends BaseSlideComponent<MotionsMotionSlideData> implements OnInit {
|
export class MotionsMotionSlideComponent extends BaseSlideComponent<MotionsMotionSlideData> {
|
||||||
public constructor() {
|
public constructor() {
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
public ngOnInit(): void {
|
|
||||||
console.log('Hello from motion slide');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { Component, OnInit } from '@angular/core';
|
import { Component } from '@angular/core';
|
||||||
import { BaseSlideComponent } from 'app/slides/base-slide-component';
|
import { BaseSlideComponent } from 'app/slides/base-slide-component';
|
||||||
import { UsersUserSlideData } from './users-user-slide-data';
|
import { UsersUserSlideData } from './users-user-slide-data';
|
||||||
|
|
||||||
@ -7,12 +7,8 @@ import { UsersUserSlideData } from './users-user-slide-data';
|
|||||||
templateUrl: './users-user-slide.component.html',
|
templateUrl: './users-user-slide.component.html',
|
||||||
styleUrls: ['./users-user-slide.component.scss']
|
styleUrls: ['./users-user-slide.component.scss']
|
||||||
})
|
})
|
||||||
export class UsersUserSlideComponent extends BaseSlideComponent<UsersUserSlideData> implements OnInit {
|
export class UsersUserSlideComponent extends BaseSlideComponent<UsersUserSlideData> {
|
||||||
public constructor() {
|
public constructor() {
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
public ngOnInit(): void {
|
|
||||||
console.log('Hello from user slide');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,24 @@
|
|||||||
|
# Generated by Django 2.1.5 on 2019-01-31 10:24
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
import openslides.utils.models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [("core", "0015_auto_20190122_1216")]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="projector",
|
||||||
|
name="reference_projector",
|
||||||
|
field=models.ForeignKey(
|
||||||
|
blank=True,
|
||||||
|
null=True,
|
||||||
|
on_delete=openslides.utils.models.SET_NULL_AND_AUTOUPDATE,
|
||||||
|
related_name="references",
|
||||||
|
to="core.Projector",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
]
|
@ -84,6 +84,14 @@ class Projector(RESTModelMixin, models.Model):
|
|||||||
|
|
||||||
name = models.CharField(max_length=255, unique=True, blank=True)
|
name = models.CharField(max_length=255, unique=True, blank=True)
|
||||||
|
|
||||||
|
reference_projector = models.ForeignKey(
|
||||||
|
"self",
|
||||||
|
on_delete=SET_NULL_AND_AUTOUPDATE,
|
||||||
|
null=True,
|
||||||
|
blank=True,
|
||||||
|
related_name="references",
|
||||||
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
"""
|
"""
|
||||||
Contains general permissions that can not be placed in a specific app.
|
Contains general permissions that can not be placed in a specific app.
|
||||||
|
@ -97,6 +97,7 @@ class ProjectorSerializer(ModelSerializer):
|
|||||||
"name",
|
"name",
|
||||||
"width",
|
"width",
|
||||||
"height",
|
"height",
|
||||||
|
"reference_projector",
|
||||||
"projectiondefaults",
|
"projectiondefaults",
|
||||||
)
|
)
|
||||||
read_only_fields = ("scale", "scroll")
|
read_only_fields = ("scale", "scroll")
|
||||||
|
Loading…
Reference in New Issue
Block a user