Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Path: blob/master/src/packages/frontend/course/configuration/customize-student-project-functionality.tsx
Views: 687
/*1* This file is part of CoCalc: Copyright © 2020 Sagemath, Inc.2* License: MS-RSL – see LICENSE.md for details3*/45import { Button, Card, Checkbox } from "antd";6import { isEqual } from "lodash";7import { useEffect, useRef, useState } from "react";8import { defineMessage, FormattedMessage, useIntl } from "react-intl";910import {11redux,12useIsMountedRef,13useTypedRedux,14} from "@cocalc/frontend/app-framework";15import { Icon, Paragraph, Tip } from "@cocalc/frontend/components";16import { course, IntlMessage, labels } from "@cocalc/frontend/i18n";17import { R_IDE } from "@cocalc/util/consts/ui";18import type { StudentProjectFunctionality } from "@cocalc/util/db-schema/projects";1920export type { StudentProjectFunctionality };2122interface Option {23name: string;24title: IntlMessage;25description: IntlMessage;26isCoCalcCom?: boolean;27notImplemented?: boolean;28}2930const OPTIONS: Option[] = [31{32name: "disableActions",33title: defineMessage({34id: "course.customize-student-project-functionality.disableActions.title",35defaultMessage: "Disable file actions",36}),37description: defineMessage({38id: "course.customize-student-project-functionality.disableActions.description",39defaultMessage:40"Make it so students can't delete, download, copy, publish, etc., files in their project. See the Disable Publish sharing option below if you just want to disable publishing.",41}),42},43{44name: "disableJupyterToggleReadonly",45title: defineMessage({46id: "course.customize-student-project-functionality.disableJupyterToggleReadonly.title",47defaultMessage:48"Disable toggling whether cells are editable or deletable",49}),50description: defineMessage({51id: "course.customize-student-project-functionality.disableJupyterToggleReadonly.description",52defaultMessage:53"Make it so that in Jupyter notebooks, students can't toggle whether cells are editable or deletable, and also disables the RAW Json Editor and the Jupyter command list dialog. If you set this, you should probably disable all of the JupyterLab and Jupyter classic options too.",54}),55},56{57name: "disableJupyterClassicServer",58title: defineMessage({59id: "course.customize-student-project-functionality.disableJupyterClassicServer.title",60defaultMessage: "Disable Jupyter Classic notebook server",61}),62description: defineMessage({63id: "course.customize-student-project-functionality.disableJupyterClassicServer.description",64defaultMessage:65"Disable the user interface for running a Jupyter classic server in student projects. This is important, since Jupyter classic provides its own extensive download and edit functionality; moreover, you may want to disable Jupyter classic to reduce confusion if you don't plan to use it.",66}),67},68{69name: "disableJupyterLabServer",70title: defineMessage({71id: "course.customize-student-project-functionality.disableJupyterLabServer.title",72defaultMessage: "Disable JupyterLab notebook server",73}),74description: defineMessage({75id: "course.customize-student-project-functionality.disableJupyterLabServer.description",76defaultMessage:77"Disable the user interface for running a JupyterLab server in student projects. This is important, since JupyterLab it provides its own extensive download and edit functionality; moreover, you may want to disable JupyterLab to reduce confusion if you don't plan to use it.",78}),79},80{81name: "disableVSCodeServer",82title: defineMessage({83id: "course.customize-student-project-functionality.disableVSCodeServer.title",84defaultMessage: "Disable VS Code IDE Server",85}),86description: defineMessage({87id: "course.customize-student-project-functionality.disableVSCodeServer.description",88defaultMessage:89"Disable the VS Code IDE Server, which lets you run VS Code in a project with one click.",90}),91},92{93name: "disablePlutoServer",94title: defineMessage({95id: "course.customize-student-project-functionality.disablePlutoServer.title",96defaultMessage: "Disable Pluto Julia notebook server",97}),98description: defineMessage({99id: "course.customize-student-project-functionality.disablePlutoServer.description",100defaultMessage:101"Disable the user interface for running a pluto server in student projects. Pluto lets you run Julia notebooks from a project.",102}),103},104{105name: "disableRServer",106title: defineMessage({107id: "course.customize-student-project-functionality.disableRServer.title",108defaultMessage: "{R_IDE}",109}),110description: defineMessage({111id: "course.customize-student-project-functionality.disableRServer.description",112defaultMessage: `Disable the user interface for running the {R_IDE} server in student projects. This is an IDE for coding in R.`,113}),114},115{116name: "disableTerminals",117title: defineMessage({118id: "course.customize-student-project-functionality.disableTerminals.title",119defaultMessage: "Disable command line terminal",120}),121description: defineMessage({122id: "course.customize-student-project-functionality.disableTerminals.description",123defaultMessage:124"Disables opening or running command line terminals in student projects.",125}),126},127{128name: "disableUploads",129title: defineMessage({130id: "course.customize-student-project-functionality.disableUploads.title",131defaultMessage: "Disable file uploads",132}),133description: defineMessage({134id: "course.customize-student-project-functionality.disableUploads.description",135defaultMessage:136"Blocks uploading files to the student project via drag-n-drop or the Upload button.",137}),138},139{140name: "disableLibrary",141title: defineMessage({142id: "course.customize-student-project-functionality.disableLibrary.title",143defaultMessage: "Disable Library",144}),145description: defineMessage({146id: "course.customize-student-project-functionality.disableLibrary.description",147defaultMessage:148"In the file explorer there is a library button for browsing and copying books and tutorials into a project. Disable this to simplify the interface.",149}),150},151{152name: "disableCollaborators",153title: defineMessage({154id: "course.customize-student-project-functionality.disableCollaborators.title",155defaultMessage: "Disable adding or removing collaborators",156}),157description: defineMessage({158id: "course.customize-student-project-functionality.disableCollaborators.description",159defaultMessage:160"Removes the user interface for adding or removing collaborators from student projects.",161}),162},163// {164// notImplemented: true,165// name: "disableAPI",166// title: "Disable API keys",167// description:168// "Makes it so the HTTP API is blocked from accessing the student project. A student might use the API to get around various other restrictions.",169// },170{171name: "disableNetwork",172title: defineMessage({173id: "course.customize-student-project-functionality.disableNetwork.title",174defaultMessage: "Disable outgoing network access",175}),176description: defineMessage({177id: "course.customize-student-project-functionality.disableNetwork.description",178defaultMessage:179"Blocks all outgoing network connections from the student projects.",180}),181isCoCalcCom: true,182},183{184name: "disableNetworkWarningBanner",185title: defineMessage({186id: "course.customize-student-project-functionality.disableNetworkWarningBanner.title",187defaultMessage: "Disable outgoing network access warning banner",188}),189description: defineMessage({190id: "course.customize-student-project-functionality.disableNetworkWarningBanner.description",191defaultMessage:192"Disables the banner at the top of the screen that warns students that network access is disabled.",193}),194isCoCalcCom: true,195},196{197name: "disableSSH",198title: defineMessage({199id: "course.customize-student-project-functionality.disableSSH.title",200defaultMessage: "Disable SSH access to project",201}),202description: defineMessage({203id: "course.customize-student-project-functionality.disableSSH.description",204defaultMessage: "Makes any attempt to ssh to a student project fail.",205}),206isCoCalcCom: true,207},208{209name: "disableChatGPT",210title: defineMessage({211id: "course.customize-student-project-functionality.disableChatGPT.title",212defaultMessage: "Disable all AI integration (ChatGPT & co.)",213}),214description: defineMessage({215id: "course.customize-student-project-functionality.disableChatGPT.description",216defaultMessage:217"Remove *all* AI integrations (ChatGPT & co.) from the student projects. This is a hint for honest students, since of course students can still use copy/paste to accomplish the same thing.",218}),219},220{221name: "disableSomeChatGPT",222title: defineMessage({223id: "course.customize-student-project-functionality.disableSomeChatGPT.title",224defaultMessage: "Disable some AI integration (ChatGPT & co.)",225}),226description: defineMessage({227id: "course.customize-student-project-functionality.disableSomeChatGPT.description",228defaultMessage:229"Disable AI integration (ChatGPT & co.) except that 'Help me fix' and 'Explain' buttons. Use this if you only want the students to use AI assistance to get unstuck.",230}),231},232{233name: "disableSharing",234title: defineMessage({235id: "course.customize-student-project-functionality.disableSharing.title",236defaultMessage: "Disable Public sharing",237}),238description: defineMessage({239id: "course.customize-student-project-functionality.disableSharing.description",240defaultMessage:241"Disable public sharing of files from the student projects. This is a hint for honest students, since of course students can still download files or even copy them to another project and share them. This does not change the share status of any files that are currently shared.",242}),243},244] as const;245246interface Props {247functionality: StudentProjectFunctionality;248onChange: (StudentProjectFunctionality) => Promise<void>;249}250251export function CustomizeStudentProjectFunctionality({252functionality,253onChange,254}: Props) {255const intl = useIntl();256const isCoCalcCom = useTypedRedux("customize", "is_cocalc_com");257const [state, setState] =258useState<StudentProjectFunctionality>(functionality);259const [saving, setSaving] = useState<boolean>(false);260261function onChangeState(obj: StudentProjectFunctionality) {262const newState = { ...state };263for (const key in obj) {264newState[key] = obj[key];265}266setState(newState);267}268269const isMountedRef = useIsMountedRef();270271const lastFunctionalityRef =272useRef<StudentProjectFunctionality>(functionality);273useEffect(() => {274if (isEqual(functionality, lastFunctionalityRef.current)) {275return;276}277// some sort of upstream change278lastFunctionalityRef.current = functionality;279setState(functionality);280}, [functionality]);281282function renderOption(option: Option) {283const { name } = option;284const description = intl.formatMessage(option.description, { R_IDE });285286let title = intl.formatMessage(option.title, { R_IDE });287if (option.notImplemented) {288const msg = intl.formatMessage(labels.not_implemented).toUpperCase();289title += ` (${msg})`;290}291292return (293<Tip key={name} title={title} tip={description}>294<Checkbox295disabled={saving}296checked={state[name]}297onChange={(e) =>298onChangeState({299[name]: (e.target as any).checked,300})301}302>303{title}304</Checkbox>305<br />306</Tip>307);308}309310const options: JSX.Element[] = [];311for (const option of OPTIONS) {312if (option.isCoCalcCom && !isCoCalcCom) continue;313options.push(renderOption(option));314}315316const title = intl.formatMessage(course.restrict_student_projects);317318return (319<Card320title={321<>322<Icon name="lock" /> {title}323</>324}325>326<Paragraph type="secondary">327<FormattedMessage328id="course.customize-student-project-functionality.description"329defaultMessage={`Check any of the boxes below330to remove the corresponding functionality from all student projects.331Hover over an option for more information about what it disables.332This is useful to reduce student confusion and keep the students more focused,333e.g., during an exam.334<i>335Do not gain a false sense of security and expect these to prevent all forms of cheating.336</i>`}337/>338</Paragraph>339<hr />340<div341style={{342border: "1px solid lightgrey",343padding: "10px",344borderRadius: "5px",345}}346>347{options}348<div style={{ marginTop: "8px" }}>349<Button350type="primary"351disabled={saving || isEqual(functionality, state)}352onClick={async () => {353setSaving(true);354await onChange(state);355if (isMountedRef.current) {356setSaving(false);357}358}}359>360{intl.formatMessage(labels.save_changes)}361</Button>362</div>363</div>364</Card>365);366}367368export function completeStudentProjectFunctionality(369x: StudentProjectFunctionality,370) {371const y = { ...x };372for (const { name } of OPTIONS) {373if (y[name] == null) {374y[name] = false;375}376}377return y;378}379380// NOTE: we allow project_id to be undefined for convenience since some clients381// were written with that unlikely assumption on their knowledge of project_id.382type Hook = (project_id?: string) => StudentProjectFunctionality;383export const useStudentProjectFunctionality: Hook = (project_id?: string) => {384const project_map = useTypedRedux("projects", "project_map") as any;385const [state, setState] = useState<StudentProjectFunctionality>(386project_map387?.getIn([project_id ?? "", "course", "student_project_functionality"])388?.toJS() ?? {},389);390useEffect(() => {391setState(392project_map393?.getIn([project_id ?? "", "course", "student_project_functionality"])394?.toJS() ?? {},395);396return;397}, [398project_map?.getIn([399project_id ?? "",400"course",401"student_project_functionality",402]),403]);404405return state;406};407408// Getting the information known right now about studnet project functionality.409// Similar to the above hook, but just a point in time snapshot. Use this410// for old components that haven't been converted to react hooks yet.411export function getStudentProjectFunctionality(412project_id?: string,413): StudentProjectFunctionality {414return (415redux416.getStore("projects")417?.getIn([418"project_map",419project_id ?? "",420"course",421"student_project_functionality",422])423?.toJS() ?? {}424);425}426427428