Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found
Select Git revision
  • 169-add-date-to-examtype
  • 233-make-exam-a-many-to-many-field-on-studentinfo-model
  • 236-improve-importer-experience
  • 243-replace-toggle-buttons-with-switches
  • 250-update-vuetify
  • 258-add-markdown-viewer
  • 265-fix-selection-changing-on-window-switching
  • 272-reviewers-should-be-able-to-assign-exercise-groups-to-tutors
  • 276-create-new-yarn-lockfile
  • 279-tutor-overview-no-scrolling
  • 282-copy-button-does-not-work-when-reviewing-corrections
  • 286-fix-misalignment-of-hide-show-sidebar-buttons
  • 287-build-test-image-constantly-failing
  • 288-add-dropdown-to-participantspage-to-set-students-groups
  • 289-fix-change-log-card
  • 291-revise-to-old-export-scheme
  • 292-update-gitlab-ci-config-for-new-runner
  • 292-update-gitlab-ci-config-for-new-runner-2
  • add-exercise-util-script
  • document-frontend-components
  • grady-exam
  • jakob.dieterle-master-patch-13835
  • master
  • parallel-test
  • test-233-branch-remove-examtype-foreign-key-on-group
  • update-export-dialogs
  • 0.0.1
  • 0.1
  • 0.2
  • 0.3
  • 0.4
  • 0.4.1
  • 0.4.2
  • 0.5.0
  • 0.5.1
  • 1.0.0
  • 1.1.0
  • 2.0.0
  • 2.0.1
  • 2.1.0
  • 2.1.1
  • 2.2.0
  • 3.0.0
  • 3.0.1
  • 4.0.0
  • 4.1.0
  • 4.2.0
  • 4.3.0
  • 4.4.0
  • 4.4.1
  • 5.0.0
  • 5.0.1
  • 5.1.0
  • 5.1.1
  • 5.1.2
  • 5.1.3
  • 5.1.4
  • 5.1.5
  • 5.1.6
  • 5.1.7
  • 5.2.0
  • 5.3.0
  • 5.3.1
  • 5.3.2
  • 5.4.0
  • 5.4.1
  • 5.4.2
  • 6.0.0
  • 6.1.0
  • legacy
70 results

Target

Select target project
  • j.michal/grady
1 result
Select Git revision
  • 169-add-date-to-examtype
  • 233-make-exam-a-many-to-many-field-on-studentinfo-model
  • 236-improve-importer-experience
  • 243-replace-toggle-buttons-with-switches
  • 250-update-vuetify
  • 258-add-markdown-viewer
  • 265-fix-selection-changing-on-window-switching
  • 272-reviewers-should-be-able-to-assign-exercise-groups-to-tutors
  • 276-create-new-yarn-lockfile
  • 279-tutor-overview-no-scrolling
  • 282-copy-button-does-not-work-when-reviewing-corrections
  • 286-fix-misalignment-of-hide-show-sidebar-buttons
  • 287-build-test-image-constantly-failing
  • 288-add-dropdown-to-participantspage-to-set-students-groups
  • 289-fix-change-log-card
  • 291-revise-to-old-export-scheme
  • 292-update-gitlab-ci-config-for-new-runner
  • 292-update-gitlab-ci-config-for-new-runner-2
  • add-exercise-util-script
  • document-frontend-components
  • grady-exam
  • jakob.dieterle-master-patch-13835
  • master
  • parallel-test
  • test-233-branch-remove-examtype-foreign-key-on-group
  • update-export-dialogs
  • 0.0.1
  • 0.1
  • 0.2
  • 0.3
  • 0.4
  • 0.4.1
  • 0.4.2
  • 0.5.0
  • 0.5.1
  • 1.0.0
  • 1.1.0
  • 2.0.0
  • 2.0.1
  • 2.1.0
  • 2.1.1
  • 2.2.0
  • 3.0.0
  • 3.0.1
  • 4.0.0
  • 4.1.0
  • 4.2.0
  • 4.3.0
  • 4.4.0
  • 4.4.1
  • 5.0.0
  • 5.0.1
  • 5.1.0
  • 5.1.1
  • 5.1.2
  • 5.1.3
  • 5.1.4
  • 5.1.5
  • 5.1.6
  • 5.1.7
  • 5.2.0
  • 5.3.0
  • 5.3.1
  • 5.3.2
  • 5.4.0
  • 5.4.1
  • 5.4.2
  • 6.0.0
  • 6.1.0
  • legacy
70 results
Show changes
Commits on Source (4)
Showing
with 137 additions and 7 deletions
......@@ -163,6 +163,11 @@ class FeedbackSerializer(DynamicFieldsModelSerializer):
for comment in data.get('feedback_lines', {}):
lines_in_submission = len(submission.text.split('\n'))
if comment['text'] == '' and 'labels' not in comment:
raise serializers.ValidationError(
"Cannot create feedback with an empty comment attached to it"
)
if not 0 < comment['of_line'] <= lines_in_submission:
raise serializers.ValidationError(
"Cannot comment line number %d of %d" % (
......
......@@ -86,3 +86,11 @@ class TutorReviewerCanChangePasswordTests(APITestCase):
ret = self.client.login(username=student.username,
password='chompreviver0.')
self.assertFalse(ret)
def test_reviewer_cannot_revoke_own_access(self):
user_pk = self.reviewer.pk
url = f"/api/user/{user_pk}/change_active/"
data = {'is_active': False}
self.client.force_authenticate(user=self.reviewer)
res = self.client.patch(url, data)
self.assertEqual(status.HTTP_403_FORBIDDEN, res.status_code)
......@@ -243,6 +243,9 @@ class UserAccountViewSet(viewsets.ReadOnlyModelViewSet):
if active is None:
error_msg = "You need to provide an 'active' field"
return Response({'Error': error_msg}, status.HTTP_400_BAD_REQUEST)
if req_user.is_reviewer() and req_user == user:
error_msg = "As a reviewer, you cannot revoke your own access."
return Response({'Error': error_msg}, status.HTTP_403_FORBIDDEN)
if (req_user.is_student() or req_user.is_tutor()) and req_user != user:
return Response(status.HTTP_403_FORBIDDEN)
user.is_active = active
......
......@@ -12,6 +12,8 @@
"test:unit": "vue-cli-service test:unit --require mock-local-storage"
},
"dependencies": {
"@sentry/browser": "^5.6.3",
"@sentry/integrations": "^5.6.1",
"axios": "^0.18.0",
"file-saver": "^2.0.2",
"highlight.js": "^9.12.0",
......
......@@ -4,7 +4,6 @@
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link href='https://fonts.googleapis.com/css?family=Roboto:300,400,500,700|Material+Icons' rel="stylesheet">
<title>Grady</title>
</head>
<body>
......
......@@ -9,6 +9,10 @@
</template>
<script>
// load fonts and icons so that webpack processes them
import "@/assets/material-icons.css"
import "@/assets/fonts.css"
import { UI } from '@/store/modules/ui'
import AutoLogout from '@/components/AutoLogout'
......
/* latin */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 100;
src: local('Roboto Thin'), local('Roboto-Thin'), url(fonts/roboto-thin.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* latin */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 300;
src: local('Roboto Light'), local('Roboto-Light'), url(fonts/roboto-light.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* latin */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 400;
src: local('Roboto'), local('Roboto-Regular'), url(fonts/roboto-regular.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* latin */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 500;
src: local('Roboto Medium'), local('Roboto-Medium'), url(fonts/roboto-medium.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* latin */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 700;
src: local('Roboto Bold'), local('Roboto-Bold'), url(fonts/roboto-bold.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* latin */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 900;
src: local('Roboto Black'), local('Roboto-Black'), url(fonts/roboto-black.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
File added
File added
File added
File added
File added
File added
File added
/* fallback */
@font-face {
font-family: 'Material Icons';
font-style: normal;
font-weight: 400;
src: url(fonts/Material-Icons.woff2) format('woff2');
}
.material-icons {
font-family: 'Material Icons';
font-weight: normal;
font-style: normal;
font-size: 24px;
line-height: 1;
letter-spacing: normal;
text-transform: none;
display: inline-block;
white-space: nowrap;
word-wrap: normal;
direction: ltr;
-moz-font-feature-settings: 'liga';
-moz-osx-font-smoothing: grayscale;
}
\ No newline at end of file
......@@ -71,6 +71,16 @@ export default class AutoLogout extends Vue {
}
mounted () {
// show notification on unload if logged in
window.onbeforeunload = event => {
if (Authentication.isLoggedIn) {
// return something to trigger the confirmation dialog
// since firefox 44 it is no longer possible to show a custom confirmation message
event.preventDefault()
event.returnValue = ''
}
}
this.timer = setInterval(() => {
const timeDialogAppearsBeforeLogout = Math.min(600 * 1e3,
this.jwtTimeDelta ? this.jwtTimeDelta * 0.3 : Infinity)
......
......@@ -104,11 +104,18 @@ export default class CommentForm extends mixins(commentLabelSelector) {
}
submitFeedback () {
const text = this.currentFeedback
const labels = this.labelsUnchanged.concat(this.labelsAdded)
if (text === "" && labels.length === 0) {
return this.collapseTextField()
}
const payload = {
lineNo: Number(this.lineNo),
comment: {
text: this.currentFeedback,
labels: this.labelsUnchanged.concat(this.labelsAdded),
text: text,
labels: labels,
}
}
......
......@@ -12,7 +12,6 @@
>
{{ lineNo }}
</v-btn>
</v-btn>
</td>
<td class="code-cell-content pl-2">
<span v-html="code" class="code-line"></span>
......
<template>
<v-flex xs5>
<v-flex lg7 xl5>
<v-card>
<v-card-title class="title">
Tutors
......@@ -27,7 +27,10 @@
</v-tooltip>
</td>
<td class="text-xs-right">
<v-btn icon @click="changeActiveStatus(props.item)">
<v-btn
v-if="canRevokeAccess(props.item.username)"
icon @click="changeActiveStatus(props.item)"
>
<v-tooltip top>
<template slot="activator">
<v-icon small v-if="!props.item.isActive">lock</v-icon>
......@@ -49,6 +52,7 @@ import Vue from 'vue'
import Component from 'vue-class-component'
import { changeActiveForUser } from '@/api'
import { actions } from '@/store/actions'
import { Authentication } from '@/store/modules/authentication'
import { TutorOverview } from '@/store/modules/tutor-overview'
import { Tutor } from '@/models'
......@@ -113,6 +117,10 @@ export default class TutorList extends Vue {
TutorOverview.getTutors()
TutorOverview.getActiveAssignments()
}
canRevokeAccess (username: string) {
return Authentication.state.user.username !== username
}
}
</script>
......
......@@ -8,7 +8,8 @@ import router from './router/index'
import Vuetify from 'vuetify'
import Notifications from 'vue-notification'
import Clipboard from 'v-clipboard'
import * as Sentry from '@sentry/browser'
import * as Integrations from '@sentry/integrations'
import 'vuetify/dist/vuetify.min.css'
import 'highlight.js/styles/atom-one-light.css'
......@@ -17,6 +18,19 @@ Vue.use(Vuetify)
Vue.use(Clipboard)
Vue.use(Notifications)
if (process.env.NODE_ENV === 'test') {
Sentry.init({
dsn: 'https://c86a983153da412b8aec8b8df9ce51ba@robin-in.space/2',
integrations: [new Integrations.Vue({Vue, attachProps: true, logErrors: true})],
})
} else if (process.env.NODE_ENV == 'production') {
Sentry.init({
dsn: 'https://874b896335564d8c9c49137391f8e3f1@robin-in.space/3',
integrations: [new Integrations.Vue({Vue, attachProps: true, logErrors: true})],
})
}
Vue.config.productionTip = false
const el = process.env.NODE_ENV === 'test' ? undefined : '#app'
......