Skip to content

Commit

Permalink
Merge pull request #6358 from mishaschwartz/v2.1.7
Browse files Browse the repository at this point in the history
V2.1.7
  • Loading branch information
mishaschwartz authored Dec 1, 2022
2 parents e56474c + ca4b82c commit 5417db6
Show file tree
Hide file tree
Showing 59 changed files with 6,823 additions and 10,459 deletions.
4 changes: 4 additions & 0 deletions Changelog.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Changelog

## [v2.1.7]
- Switch from jquery-ui-timepicker-addon to flatpickr for datetime inputs (#6158)
- Allow results to be made available only through unique tokens (#6244)

## [v2.1.6]
- Fix bug where TAs were able to access urls for results they haven't been assigned to (#6321)
- Fix bug where the marking state was left as "complete" after a new criterion is created (#6303)
Expand Down
2 changes: 1 addition & 1 deletion app/MARKUS_VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
VERSION=v2.1.6,PATCH_LEVEL=DEV
VERSION=v2.1.7,PATCH_LEVEL=DEV
1 change: 1 addition & 0 deletions app/assets/config/manifest.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@
//
//= link_tree ../images
//= link_tree ../builds
//= link flatpickr/dist/flatpickr.css
175 changes: 175 additions & 0 deletions app/assets/javascripts/Components/Modals/release_urls_modal.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
import React from "react";
import Modal from "react-modal";
import ReactTable from "react-table";
import Flatpickr from "react-flatpickr";

class ReleaseUrlsModal extends React.Component {
constructor() {
super();
this.state = {loading: false};
}

componentDidMount() {
Modal.setAppElement("body");
}

refreshViewTokens = result_ids => {
if (!confirm(I18n.t("submissions.refresh_token_confirmation"))) {
return;
}
this.setState({loading: true}, () => {
$.ajax({
type: "PUT",
url: Routes.refresh_view_tokens_course_assignment_results_url(
this.props.course_id,
this.props.assignment_id,
{result_ids: result_ids}
),
}).then(res => this.props.refreshViewTokens(res, () => this.setState({loading: false})));
});
};

refreshViewTokenExpiry = (expiry_datetime, result_ids) => {
if (
result_ids.length > 1 &&
!confirm(
expiry_datetime
? I18n.t("submissions.update_all_token_expiry_confirmation", {date: expiry_datetime})
: I18n.t("submissions.clear_all_token_expiry_confirmation")
)
) {
return;
}
this.setState({loading: true}, () => {
$.ajax({
type: "PUT",
url: Routes.update_view_token_expiry_course_assignment_results_url(
this.props.course_id,
this.props.assignment_id,
{result_ids: result_ids, expiry_datetime: expiry_datetime}
),
}).then(res => this.props.refreshViewTokenExpiry(res, () => this.setState({loading: false})));
});
};

render() {
return (
<Modal
className="react-modal"
isOpen={this.props.isOpen}
onRequestClose={this.props.onRequestClose}
>
<div className="rt-action-box">
<button onClick={() => this.refreshViewTokens(this.props.data.map(d => d["result_id"]))}>
{I18n.t("submissions.refresh_all_tokens")}
</button>
<Flatpickr
placeholder={I18n.t("submissions.update_all_token_expiry")}
onClose={selectedDates => {
this.refreshViewTokenExpiry(
selectedDates[0],
this.props.data.map(d => d["result_id"])
);
}}
options={{
altInput: true,
altFormat: I18n.t("time.format_string.flatpickr"),
dateFormat: "Z",
}}
/>
<a
className={"button"}
href={Routes.download_view_tokens_course_assignment_results_url(
this.props.course_id,
this.props.assignment_id,
{result_ids: this.props.data.map(d => d["result_id"])}
)}
>
{I18n.t("download")}
</a>
</div>
<ReactTable
data={this.props.data}
filterable
defaultSorted={[{id: "group_name"}]}
loading={this.state.loading}
columns={[
{
show: false,
accessor: "_id",
id: "_id",
},
{
Header: I18n.t("activerecord.models.group.one"),
accessor: "group_name",
id: "group_name",
Cell: this.props.groupNameWithMembers,
minWidth: 250,
filterMethod: this.props.groupNameFilter,
},
{
Header: I18n.t("submissions.release_token"),
id: "result_view_token",
filterable: false,
sortable: false,
minWidth: 250,
Cell: row => {
return (
<div>
<a
href="#"
className="refresh"
onClick={() => this.refreshViewTokens([row.original.result_id])}
title={I18n.t("refresh")}
/>
{row.original.result_view_token}
</div>
);
},
},
{
Header: I18n.t("submissions.release_token_expires"),
id: "result_view_token_expiry",
filterable: false,
sortable: false,
minWidth: 200,
Cell: row => {
return (
<Flatpickr
value={row.original.result_view_token_expiry}
onClose={selectedDates =>
this.refreshViewTokenExpiry(selectedDates[0], [row.original.result_id])
}
options={{
altInput: true,
altFormat: I18n.t("time.format_string.flatpickr"),
dateFormat: "Z",
}}
/>
);
},
},
]}
SubComponent={row => {
if (row.original.result_view_token) {
const url = Routes.view_marks_course_result_url(
this.props.course_id,
row.original.result_id,
{view_token: row.original.result_view_token}
);
return (
<div style={{whiteSpace: "pre-wrap"}}>
{I18n.t("submissions.release_url_with_token", {url: url})}
</div>
);
} else {
return "";
}
}}
/>
</Modal>
);
}
}

export default ReleaseUrlsModal;
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import React from "react";
import {render} from "react-dom";
import Modal from "react-modal";

class SubmitViewTokenModal extends React.Component {
constructor() {
super();
this.state = {
isOpen: false,
token: null,
};
}

componentDidMount() {
Modal.setAppElement("body");
}

onSubmit = () => {
$.ajax({
url: Routes.view_token_check_course_result_path(this.props.course_id, this.props.result_id, {
view_token: this.state.token,
}),
}).then(
() => {
window.location = Routes.view_marks_course_result_path(
this.props.course_id,
this.props.result_id,
{view_token: this.state.token}
);
},
() => this.setState({isOpen: false, token: null})
);
};

render() {
return (
<Modal
className="react-modal"
isOpen={this.state.isOpen}
onRequestClose={() => this.setState({isOpen: false, token: null})}
>
<p>{I18n.t("results.view_token_submit")}</p>
<form onSubmit={this.onSubmit}>
<div className={"modal-container-vertical"}>
<div className={"modal-container"}>
<input onChange={e => this.setState({token: e.target.value})} />
</div>
<div className={"modal-container"}>
<input type="submit" value={I18n.t("results.submit_token")} />
</div>
</div>
</form>
</Modal>
);
}
}

export function makeSubmitViewTokenModal(elem, props) {
return render(<SubmitViewTokenModal {...props} />, elem);
}
1 change: 0 additions & 1 deletion app/assets/javascripts/Components/Result/left_pane.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,6 @@ export class LeftPane extends React.Component {
<div id="remark_request_tab">
<RemarkPanel
result_id={this.props.result_id}
submission_id={this.props.submission_id}
assignment_id={this.props.assignment_id}
assignmentRemarkMessage={this.props.assignment_remark_message}
updateOverallComment={this.props.update_overall_comment}
Expand Down
6 changes: 3 additions & 3 deletions app/assets/javascripts/Components/Result/remark_panel.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,9 @@ export class RemarkPanel extends React.Component {
let data = {submission: {remark_request: value}};
data[name] = "true";
return $.ajax({
url: Routes.update_remark_request_course_submission_results_path(
this.props.course_id, // TODO: pass this prop to this component
this.props.submission_id
url: Routes.update_remark_request_course_result_path(
this.props.course_id,
this.props.result_id
),
method: "PATCH",
data: data,
Expand Down
21 changes: 14 additions & 7 deletions app/assets/javascripts/Components/autotest_manager.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ import React from "react";
import {render} from "react-dom";
import FileManager from "./markus_file_manager";
import Form from "@rjsf/core";
import Datepicker from "./date_picker";
import Flatpickr from "react-flatpickr";
import labelPlugin from "flatpickr/dist/plugins/labelPlugin/labelPlugin";
import FileUploadModal from "./Modals/file_upload_modal";
import AutotestSpecsUploadModal from "./Modals/autotest_specs_upload_modal";

Expand Down Expand Up @@ -169,8 +170,9 @@ class AutotestManager extends React.Component {
);
};

handleTokenStartDateChange = date => {
this.setState({token_start_date: date}, () => this.toggleFormChanged(true));
handleTokenStartDateChange = selectedDates => {
const newDate = selectedDates[0] || "";
this.setState({token_start_date: newDate}, () => this.toggleFormChanged(true));
};

handleTokensPerPeriodChange = event => {
Expand Down Expand Up @@ -298,12 +300,17 @@ class AutotestManager extends React.Component {
<label className="inline_label" htmlFor="token_start_date">
{I18n.t("activerecord.attributes.assignment.token_start_date")}
</label>
<Datepicker
<Flatpickr
id="token_start_date"
warn_before_now={true}
date={this.state.token_start_date}
value={this.state.token_start_date}
onChange={this.handleTokenStartDateChange}
disabled={!this.state.enable_test || !this.state.enable_student_tests}
options={{
altInput: true,
altFormat: I18n.t("time.format_string.flatpickr"),
dateFormat: "Z",
disabled: !this.state.enable_test || !this.state.enable_student_tests,
plugins: [labelPlugin()], // Ensure id is applied to visible input
}}
/>
<label className="inline_label" htmlFor="token_period">
{I18n.t("activerecord.attributes.assignment.token_period")}
Expand Down
Loading

0 comments on commit 5417db6

Please sign in to comment.