Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 0 additions & 7 deletions packages/tdev/brython-code/models/Script.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,13 +78,6 @@ export default class Script extends iCode<'script'> {
return this.code.split('\n').length;
}

@computed
get data(): TypeDataMapping['script'] {
return {
code: this.code
};
}

@action
toggleScriptExecution() {
if (this.isExecuting) {
Expand Down
2 changes: 1 addition & 1 deletion packages/tdev/brython-code/models/ScriptMeta.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,6 @@ export const createModel: Factory = (data, store) => {

export class ScriptMeta extends iScriptMeta<'script'> {
constructor(props: Partial<Omit<CodeEditorProps, 'id' | 'className'>>) {
super(props, 'script');
super('script', props);
}
}
4 changes: 3 additions & 1 deletion packages/tdev/excalidoc/model/ModelMeta.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,17 @@ export interface MetaInit {

export class ModelMeta extends TypeMeta<'excalidoc'> {
readonly type = 'excalidoc';
readonly props: Partial<MetaInit>;
readonly defaultElements: readonly ExcalidrawElement[];
readonly defaultFiles: BinaryFiles;
readonly defaultImage: string;

constructor(props: Partial<MetaInit>) {
super('excalidoc', props.readonly ? Access.RO_User : undefined);
super('excalidoc', props);
this.defaultElements = props.defaultElements || [];
this.defaultFiles = props.defaultFiles || {};
this.defaultImage = props.defaultImage || '';
this.props = props;
}

get defaultData(): TypeDataMapping['excalidoc'] {
Expand Down
2 changes: 1 addition & 1 deletion packages/tdev/netpbm-graphic/model/ModelMeta.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export class ModelMeta extends TypeMeta<'netpbm_graphic'> {
readonly default?: string;

constructor(props: Partial<MetaInit>) {
super('netpbm_graphic', props.readonly ? Access.RO_User : undefined);
super('netpbm_graphic', props);
/**
* the default data can be either provided as a string or as a child element.
* If it is provided as a child element, the relevant data is extracted by the
Expand Down
2 changes: 1 addition & 1 deletion packages/tdev/page-read-check/model/ModelMeta.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export class ModelMeta extends TypeMeta<'page_read_check'> {
readonly minReadTime: number;

constructor(props: Partial<MetaInit>) {
super('page_read_check', props.readonly ? Access.RO_User : undefined);
super('page_read_check', props);
this.minReadTime = props.minReadTime || 10;
}

Expand Down
2 changes: 1 addition & 1 deletion packages/tdev/pyodide-code/models/ModelMeta.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,6 @@ export interface MetaInit {

export class ModelMeta extends iCodeMeta<'pyodide_code'> {
constructor(props: Partial<MetaInit>) {
super({ lang: 'py', title: 'Python', ...props }, 'pyodide_code');
super('pyodide_code', { lang: 'py', title: 'Python', ...props });
}
}
17 changes: 0 additions & 17 deletions packages/tdev/pyodide-code/models/PyodideCode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,17 +98,6 @@ class PyodideCode extends iCode<'pyodide_code'> {
this.messages.push({ ...message });
}

@action
setData(data: TypeDataMapping['pyodide_code'], from: Source, updatedAt?: Date): void {
this.code = data.code;
if (from === Source.LOCAL) {
this.save();
}
if (updatedAt) {
this.updatedAt = new Date(updatedAt);
}
}

@action
runCode() {
this.pyodideStore.run(this);
Expand All @@ -119,12 +108,6 @@ class PyodideCode extends iCode<'pyodide_code'> {
this.pyodideStore.recreatePyWorker();
}

get data(): TypeDataMapping['pyodide_code'] {
return {
code: this.code
};
}

@computed
get meta(): ModelMeta {
if (this.root?.type === 'pyodide_code') {
Expand Down
2 changes: 1 addition & 1 deletion packages/tdev/text-message/models/SimpleChat/ModelMeta.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export class ModelMeta extends ContainerMeta<'simple_chat'> {

constructor(props: Partial<MetaInit>) {
super('simple_chat', {
access: props.readonly ? Access.RO_User : undefined,
...props,
description: 'Ein simpler Chat zum Senden und Empfangen von Textnachrichten.'
});
this.defaultName = props.name || 'Simple Chat';
Expand Down
2 changes: 1 addition & 1 deletion packages/tdev/text-message/models/TextMessage/ModelMeta.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export class ModelMeta extends TypeMeta<'text_message'> {
readonly type = 'text_message';

constructor(props: Partial<MetaInit>) {
super('text_message', props.readonly ? Access.RO_User : undefined);
super('text_message', props);
}

get defaultData(): TypeDataMapping['text_message'] {
Expand Down
11 changes: 6 additions & 5 deletions src/api/IoEventTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,13 +45,14 @@ export interface ChangedRecord<T extends RecordType> {
record: TypeRecordMap[T];
}

export interface ChangedDocument {
export interface ChangedDocument<T = any> {
id: string;
data: Object;
updatedAt: string;
data: Record<string, any>;
updatedAt: Date;
meta?: T;
}

export interface StreamedDocument extends ChangedDocument {
export interface StreamedDynamicDocument<T = any> extends ChangedDocument<T> {
roomId: string;
}

Expand Down Expand Up @@ -137,7 +138,7 @@ export interface ClientToServerEvents {
[IoClientEvent.JOIN_ROOM]: (roomId: string, callback: (joined: boolean) => void) => void;
[IoClientEvent.LEAVE_ROOM]: (roomId: string, callback: (left: boolean) => void) => void;
[IoClientEvent.ACTION]: (action: Action, callback: (ok: boolean) => void) => void;
[IoClientEvent.STREAM_UPDATE]: (payload: StreamedDocument) => void;
[IoClientEvent.STREAM_UPDATE]: (payload: StreamedDynamicDocument) => void;
}

export const RecordStoreMap: { [key in RecordType]: keyof typeof rootStore | `viewStore#${string}` } = {
Expand Down
3 changes: 1 addition & 2 deletions src/api/document.ts
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,6 @@ type KeysWithCode<T> = {
}[keyof Omit<T, 'script_version'>];

export type CodeType = KeysWithCode<TypeDataMapping>;

export interface ContainerTypeModelMapping {
['_container_placeholder_']: iDocumentContainer<ContainerType>; // placeholder to avoid empty interface error
}
Expand Down Expand Up @@ -203,7 +202,7 @@ export type Factory<Type extends DocumentType = DocumentType> = (
export function find<Type extends DocumentType>(
id: string,
signal: AbortSignal
): AxiosPromise<Document<Type>> {
): AxiosPromise<{ document: Document<Type>; highestPermission: Access }> {
return api.get(`/documents/${id}`, { signal });
}

Expand Down
11 changes: 11 additions & 0 deletions src/api/studentGroup.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,21 @@
import { type Access, type Document, type DocumentType } from '@tdev-api/document';
import api from './base';
import { AxiosPromise } from 'axios';
import { TypeMeta } from '@tdev-models/DocumentRoot';

export interface DocumentPresentation<Type extends DocumentType = DocumentType> {
document: Document<Type>;
meta: TypeMeta<Type>;
access: Access;
sharedAccess: Access;
}

export interface StudentGroup {
id: string;
name: string;
description: string;
canPresent: boolean;
presentedDocument: DocumentPresentation | null;
userIds: string[];
adminIds: string[];

Expand Down
31 changes: 31 additions & 0 deletions src/components/Admin/StudentGroupPanel/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import _ from 'es-toolkit/compat';
import { action } from 'mobx';
import Icon from '@mdi/react';
import TextInput from '@tdev-components/shared/TextInput';
import DefinitionList from '@tdev-components/DefinitionList';
import Badge from '@tdev-components/shared/Badge';

const StudentGroupPanel = observer(() => {
const userStore = useStore('userStore');
Expand All @@ -20,6 +22,7 @@ const StudentGroupPanel = observer(() => {
if (!current?.hasElevatedAccess) {
return null;
}
const presentableGroups = groupStore.managedStudentGroups.filter((g) => g.canPresent);
return (
<div>
<div className={clsx(styles.controls)}>
Expand Down Expand Up @@ -59,6 +62,34 @@ const StudentGroupPanel = observer(() => {
</div>
</div>
</div>
<div className={clsx(styles.streamableGroups)}>
<DefinitionList>
<dt>Präsentationsberechtigte Gruppen</dt>
<dd>
<Badge color={presentableGroups.length > 0 ? 'red' : 'lightBlue'}>
{presentableGroups.length} Gruppen
</Badge>
</dd>
<dd>
<i>
Während Prüfungen sollten diese Berechtigungen deaktiviert werden, da sonst
potenziell sensible Informationen ausgetauscht werden können.
</i>
</dd>
<dt>Aktionen</dt>
<dd>
<Button
onClick={() => {
presentableGroups.forEach((g) => g.setCanPresent(false));
}}
icon={mdiRestore}
iconSide="left"
color="secondary"
text="Alle Berechtigungen deaktivieren"
/>
</dd>
</DefinitionList>
</div>
<div className={clsx(styles.studentGroups)}>
{(() => {
let searchRegex;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import React from 'react';
import clsx from 'clsx';
import { observer } from 'mobx-react-lite';
import { useStore } from '@tdev-hooks/useStore';
import AccessSelector from '.';
import { Access } from '@tdev-api/document';
import StudentGroup from '@tdev-models/StudentGroup';

interface Props {
group: StudentGroup;
mark?: Access | Access[] | Set<Access>;
className?: string;
}

const GroupAccessSelector = observer((props: Props) => {
const { group } = props;
const permissionStore = useStore('permissionStore');
const groupPermission = permissionStore
.groupPermissionsByDocumentRoot(group.presentedDocument?.documentRootId)
.find((p) => p.groupId === group.id)?.access;

return (
<div className={clsx(props.className)}>
<AccessSelector
accessTypes={[Access.None_StudentGroup, Access.RO_StudentGroup, Access.RW_StudentGroup]}
access={groupPermission}
onChange={(access) => {
const currentPermission = group.presentedDocument!.root!.groupPermissions.find(
(gp) => gp.groupId === group.id
);
if (currentPermission) {
currentPermission.setAccess(access);
} else {
permissionStore.createGroupPermission(
group.presentedDocumentProps?.document.documentRootId!,
group,
access
);
}
}}
mark={props.mark}
/>
</div>
);
});

export default GroupAccessSelector;
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import React from 'react';
import clsx from 'clsx';
import { observer } from 'mobx-react-lite';
import { useStore } from '@tdev-hooks/useStore';
import AccessSelector from '.';
import { Access, DocumentType } from '@tdev-api/document';
import DocumentRoot from '@tdev-models/DocumentRoot';

interface Props {
documentRoot: DocumentRoot<DocumentType>;
maxAccess?: Access;
className?: string;
mark?: Access | Access[] | Set<Access>;
}

const RootAccessSelector = observer((props: Props) => {
const { documentRoot } = props;

return (
<AccessSelector
accessTypes={[Access.None_DocumentRoot, Access.RO_DocumentRoot, Access.RW_DocumentRoot]}
access={documentRoot.rootAccess}
onChange={(access) => {
documentRoot.setRootAccess(access);
}}
maxAccess={props.maxAccess}
mark={props.mark}
/>
);
});

export default RootAccessSelector;
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import React from 'react';
import clsx from 'clsx';
import { observer } from 'mobx-react-lite';
import { useStore } from '@tdev-hooks/useStore';
import AccessSelector from '.';
import { Access, DocumentType } from '@tdev-api/document';
import DocumentRoot from '@tdev-models/DocumentRoot';

interface Props {
documentRoot: DocumentRoot<DocumentType>;
maxAccess?: Access;
className?: string;
mark?: Access | Access[] | Set<Access>;
}

const SharedAccessSelector = observer((props: Props) => {
const { documentRoot } = props;

return (
<AccessSelector
accessTypes={[Access.None_DocumentRoot, Access.RO_DocumentRoot, Access.RW_DocumentRoot]}
access={documentRoot.sharedAccess}
onChange={(access) => {
documentRoot.setSharedAccess(access);
}}
maxAccess={props.maxAccess}
mark={props.mark}
/>
);
});

export default SharedAccessSelector;
Loading
Loading