Skip to content
Merged
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
9 changes: 8 additions & 1 deletion etc/env.json.example
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,12 @@
"cs": "Univerzitní login",
"en": "University login"
},
"EXTERNAL_AUTH_HELPDESK_URL": "mailto:cas@some.other.domain"
"EXTERNAL_AUTH_HELPDESK_URL": "mailto:cas@some.other.domain",
"KNOWN_IPS": {
"127.0.0.1": "localhost",
"192.168.1.1": {
"cs": "Cože? Privátní adresa?",
"en": "What? A private IP?"
}
}
}
4 changes: 2 additions & 2 deletions recodex-web.spec
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
%define name recodex-web
%define short_name web-app
%define version 2.16.0
%define unmangled_version a8b7b1575915ce7b21b0c63f132f46a6454ce75d
%define release 1
%define unmangled_version 88668c73629efc6fad6cce6117f4db7b20a168fe
%define release 2

Summary: ReCodEx web-app component
Name: %{name}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -222,11 +222,11 @@ const AssignmentDetails = ({
<Icon icon="percent" />
</td>
<th>
<FormattedMessage id="app.assignment.corectnessThreshold" defaultMessage="Correctness threshold" />:
<Explanation id="corectnessThreshold">
<FormattedMessage id="app.assignment.correctnessThreshold" defaultMessage="Correctness threshold" />:
<Explanation id="correctnessThreshold">
<FormattedMessage
id="app.assignment.corectnessThresholdExplanation"
defaultMessage="Minimal correctness of solutions required for awardnig any points. Solutions below this threshold are granted no points."
id="app.assignment.correctnessThresholdExplanation"
defaultMessage="Minimal correctness of solutions required for awarding any points. Solutions below this threshold are granted no points."
/>
</Explanation>
</th>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ const AssignmentStatusIcon = ({ id, submission = null, accepted = false, isBestS
tooltipId={id}
tooltip={
<FormattedMessage
id="app.assignemntStatusIcon.solutionMissingSubmission"
id="app.assignmentStatusIcon.solutionMissingSubmission"
defaultMessage="The solution was not submitted for evaluation probably due to an error. You may need to resubmit it."
/>
}
Expand All @@ -29,7 +29,7 @@ const AssignmentStatusIcon = ({ id, submission = null, accepted = false, isBestS
tooltipId={id}
tooltip={
<FormattedMessage
id="app.assignemntStatusIcon.inProgress"
id="app.assignmentStatusIcon.inProgress"
defaultMessage="Assignment solution is being evaluated."
/>
}
Expand Down Expand Up @@ -59,12 +59,12 @@ const AssignmentStatusIcon = ({ id, submission = null, accepted = false, isBestS
tooltip={
<>
<FormattedMessage
id="app.assignemntStatusIcon.ok"
id="app.assignmentStatusIcon.ok"
defaultMessage="Assignment is successfully completed."
/>
{isBestSolution && !accepted && (
<FormattedMessage
id="app.assignemntStatusIcon.isBestSolution"
id="app.assignmentStatusIcon.isBestSolution"
defaultMessage="This is the best solution of the author submitted so far."
/>
)}
Expand All @@ -87,12 +87,12 @@ const AssignmentStatusIcon = ({ id, submission = null, accepted = false, isBestS
tooltip={
<>
<FormattedMessage
id="app.assignemntStatusIcon.failed"
id="app.assignmentStatusIcon.failed"
defaultMessage="No correct solution was submitted yet."
/>
{isBestSolution && !accepted && (
<FormattedMessage
id="app.assignemntStatusIcon.isBestSolution"
id="app.assignmentStatusIcon.isBestSolution"
defaultMessage="This is the best solution of the author submitted so far."
/>
)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -262,7 +262,7 @@ class AssignmentsTable extends Component {
{showGroups ? (
<FormattedMessage
id="app.assignmentsTable.noAssignmentsInAnyGroup"
defaultMessage="This exercise has no assigments in any of the groups you can see."
defaultMessage="This exercise has no assignments in any of the groups you can see."
/>
) : (
<FormattedMessage
Expand Down
2 changes: 1 addition & 1 deletion src/components/Groups/GroupExamPending/GroupExamPending.js
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,7 @@ class GroupExamPending extends Component {
{examLockStrict ? (
<FormattedMessage
id="app.groupExams.lockStrictExplanation"
defaultMessage="Users taking the exam will not be allowed to access any other group, not even for reading (so thet are cut of source codes they submitted before the exam)."
defaultMessage="Users taking the exam will not be allowed to access any other group, not even for reading (so that are cut of source codes they submitted before the exam)."
/>
) : (
<FormattedMessage
Expand Down
2 changes: 1 addition & 1 deletion src/components/Groups/GroupExamStatus/GroupExamStatus.js
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ class GroupExamStatus extends Component {
{group.privateData.examLockStrict ? (
<FormattedMessage
id="app.groupExams.lockStrictExplanation"
defaultMessage="Users taking the exam will not be allowed to access any other group, not even for reading (so thet are cut of source codes they submitted before the exam)."
defaultMessage="Users taking the exam will not be allowed to access any other group, not even for reading (so that are cut of source codes they submitted before the exam)."
/>
) : (
<FormattedMessage
Expand Down
5 changes: 2 additions & 3 deletions src/components/Groups/GroupInvitations/GroupInvitations.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import classnames from 'classnames';
import UsersNameContainer from '../../../containers/UsersNameContainer';
import DateTime from '../../widgets/DateTime';
import Button, { TheButtonGroup } from '../../widgets/TheButton';
import Icon, { CopyIcon, DeleteIcon, EditIcon } from '../../icons';
import Icon, { CopyIcon, CopySuccessIcon, DeleteIcon, EditIcon } from '../../icons';
import withLinks from '../../../helpers/withLinks.js';

const GroupInvitations = ({
Expand Down Expand Up @@ -55,8 +55,7 @@ const GroupInvitations = ({
<code className={hasExpired ? 'text-body-secondary small' : 'small'}>{uri}</code>
{!hasExpired &&
(copiedInvitation === invitation.id ? (
<Icon
icon="clipboard-check"
<CopySuccessIcon
gapLeft={2}
className="text-success"
onClick={() => setCopiedInvitation(null)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { Table } from 'react-bootstrap';
import { lruMemoize } from 'reselect';

import UsersName from '../../Users/UsersName';
import IpAddress from '../../widgets/IpAddress/IpAddress';
import ExamUnlockButtonContainer from '../../../containers/ExamUnlockButtonContainer';
import { createUserNameComparator } from '../../helpers/users.js';

Expand All @@ -17,17 +18,18 @@ const LockedStudentsTable = ({ groupId, lockedStudents, currentUser, intl: { loc
lockedStudents && lockedStudents.length > 0 ? (
<Table className="m-0" hover>
<tbody>
{sortStudents(lockedStudents).map(student => (
{sortStudents(lockedStudents, locale).map(student => (
<tr key={student.id}>
<td>
<UsersName
{...student}
currentUserId={currentUser.id}
showEmail="full"
showEmail="icon"
showExternalIdentifiers
listItem
/>
</td>
<td>{student.privateData?.ipLock && <IpAddress ip={student.privateData.ipLock} />}</td>
<td className="text-end">
<ExamUnlockButtonContainer groupId={groupId} userId={student.id} size="xs" />
</td>
Expand Down
5 changes: 2 additions & 3 deletions src/components/Groups/LocksTable/LocksTable.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { lruMemoize } from 'reselect';

import UsersNameContainer from '../../../containers/UsersNameContainer';
import DateTime from '../../widgets/DateTime';
import IpAddress from '../../widgets/IpAddress/IpAddress';
import { LockIcon, UnlockIcon } from '../../icons';

const sortLocks = lruMemoize(locks => [...locks].sort(({ createdAt: c1 }, { createdAt: c2 }) => c1 - c2));
Expand All @@ -31,9 +32,7 @@ const LocksTable = ({ locks }) =>
</>
)}
</td>
<td className="shrink-col text-nowrap">
<code>{lock.remoteAddr}</code>
</td>
<td className="shrink-col text-nowrap">{lock.remoteAddr && <IpAddress ip={lock.remoteAddr} />}</td>
</tr>
))}
</tbody>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import React from 'react';
import PropTypes from 'prop-types';
import { FormattedMessage, injectIntl } from 'react-intl';
import { Table } from 'react-bootstrap';
import { lruMemoize } from 'reselect';

import UsersName from '../../Users/UsersName';
import DateTime from '../../widgets/DateTime/DateTime';
import Icon from '../../icons';
import { createUserNameComparator } from '../../helpers/users.js';

const sortStudents = lruMemoize((notLockedStudents, locale) => {
const sorted = [...notLockedStudents];
return sorted.sort(createUserNameComparator(locale));
});

const NotLockedStudentsTable = ({ notLockedStudents, currentUser, intl: { locale } }) =>
notLockedStudents && notLockedStudents.length > 0 ? (
<Table className="m-0" hover>
<tbody>
{sortStudents(notLockedStudents, locale).map(student => (
<tr key={student.id}>
<td>
<UsersName
{...student}
currentUserId={currentUser.id}
showEmail="icon"
showExternalIdentifiers
listItem
/>
</td>
<td className="text-nowrap shrink-col small text-body-secondary align-middle">
{student.privateData?.lastAuthenticationAt && (
<>
<Icon
icon="right-to-bracket"
gapRight
tooltipId={student.id}
tooltipPlacement="bottom"
tooltip={
<FormattedMessage
id="app.notLockedStudentsTable.lastAuthentication"
defaultMessage="Last time the user was authenticated"
/>
}
/>
<DateTime unixts={student.privateData.lastAuthenticationAt} showSeconds />
</>
)}
</td>
</tr>
))}
</tbody>
</Table>
) : (
<div className="text-center text-body-secondary p-4">
<em>
<FormattedMessage
id="app.notLockedStudentsTable.noNotLockedStudents"
defaultMessage="There are no remaining unlocked students."
/>
</em>
</div>
);

NotLockedStudentsTable.propTypes = {
notLockedStudents: PropTypes.array,
currentUser: PropTypes.shape({
id: PropTypes.string.isRequired,
}),
intl: PropTypes.object.isRequired,
};

export default injectIntl(NotLockedStudentsTable);
2 changes: 2 additions & 0 deletions src/components/Groups/NotLockedStudentsTable/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
import NotLockedStudentsTable from './NotLockedStudentsTable.js';
export default NotLockedStudentsTable;
2 changes: 1 addition & 1 deletion src/components/Instances/InstancesTable/InstancesTable.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ const InstancesTable = ({ instances, links: { INSTANCE_URI_FACTORY }, intl }) =>
<FormattedMessage id="app.instancesTable.admin" defaultMessage="Admin" />
</th>
<th>
<FormattedMessage id="app.instancesTable.validLicence" defaultMessage="Has valid licence" />
<FormattedMessage id="app.instancesTable.validLicense" defaultMessage="Has valid license" />
</th>
</tr>
</thead>
Expand Down
14 changes: 7 additions & 7 deletions src/components/Instances/LicencesTable/LicencesTable.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@ import Box from '../../widgets/Box';
import DateTime from '../../widgets/DateTime';

const LicencesTable = ({ instance, licences }) => (
<Box title={<FormattedMessage id="app.instance.licencesTitle" defaultMessage="Licences" />}>
<Box title={<FormattedMessage id="app.instance.licensesTitle" defaultMessage="Licenses" />}>
<>
<p>
<FormattedMessage
id="app.instance.hasValidLicence"
defaultMessage="{name} has a valid licence: "
id="app.instance.hasValidLicense"
defaultMessage="{name} has a valid license: "
values={{ name: instance.name }}
/>
&nbsp;
Expand All @@ -23,13 +23,13 @@ const LicencesTable = ({ instance, licences }) => (
<thead>
<tr>
<th>
<FormattedMessage id="app.licencesTable.note" defaultMessage="Note" />
<FormattedMessage id="app.licensesTable.note" defaultMessage="Note" />
</th>
<th>
<FormattedMessage id="app.licencesTable.isValid" defaultMessage="Without revocation" />
<FormattedMessage id="app.licensesTable.isValid" defaultMessage="Without revocation" />
</th>
<th>
<FormattedMessage id="app.licencesTable.validUntil" defaultMessage="Valid until" />
<FormattedMessage id="app.licensesTable.validUntil" defaultMessage="Valid until" />
</th>
</tr>
</thead>
Expand All @@ -51,7 +51,7 @@ const LicencesTable = ({ instance, licences }) => (
{licences.length === 0 && (
<tr>
<td colSpan={3}>
<FormattedMessage id="app.licencesTable.noLicences" defaultMessage="There are no licences." />
<FormattedMessage id="app.licensesTable.noLicenses" defaultMessage="There are no licenses." />
</td>
</tr>
)}
Expand Down
6 changes: 3 additions & 3 deletions src/components/Pipelines/BoxForm/BoxForm.js
Original file line number Diff line number Diff line change
Expand Up @@ -78,8 +78,8 @@ class BoxForm extends Component {
<h5>
{editing ? (
<FormattedMessage
id="app.pipelines.boxForm.titleEditting"
defaultMessage="Editting Box <strong>{editing}</strong>"
id="app.pipelines.boxForm.titleEditing"
defaultMessage="Editing Box <strong>{editing}</strong>"
values={{ editing, strong: content => <strong className="ms-1">{content}</strong> }}
/>
) : (
Expand Down Expand Up @@ -287,7 +287,7 @@ const validate = (
if (name && name.trim() !== editing && boxes && boxes.find(v => v.name === name.trim())) {
errors.name = (
<FormattedMessage
id="app.pipelines.boxForm.duplicitName"
id="app.pipelines.boxForm.duplicateName"
defaultMessage="This name is already taken by another box."
/>
);
Expand Down
2 changes: 1 addition & 1 deletion src/components/Pipelines/ParametersList/ParametersList.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ const pipelineParams = {
hasSuccessExitCodes: (
<FormattedMessage
id="app.pipelineParams.hasSuccessExitCodes"
defaultMessage="Configurable exit-codes that are accepted as a success when tested soution is executed"
defaultMessage="Configurable exit-codes that are accepted as a success when tested solution is executed"
/>
),
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ const PipelineExercisesList = ({
{pipelineExercises.size > 5 && (
<small className="text-body-secondary">
<FormattedMessage
id="app.pipelineExercisessList.totalCount"
id="app.pipelineExercisesList.totalCount"
defaultMessage="Total exercises: {count}"
values={{ count: pipelineExercises.size }}
/>
Expand Down Expand Up @@ -140,7 +140,7 @@ const PipelineExercisesList = ({
<tr>
<td className="text-center text-body-secondary" colSpan={3}>
<FormattedMessage
id="app.pipelineExercisessList.empty"
id="app.pipelineExercisesList.empty"
defaultMessage="There are no exercises using this pipeline at the moment."
/>
</td>
Expand All @@ -160,7 +160,7 @@ const PipelineExercisesList = ({
ev.preventDefault();
}}>
<FormattedMessage
id="app.pipelineExercisessList.collapse"
id="app.pipelineExercisesList.collapse"
defaultMessage="Collapse the list and show only short preview."
/>
</a>
Expand All @@ -172,7 +172,7 @@ const PipelineExercisesList = ({
ev.preventDefault();
}}>
<FormattedMessage
id="app.pipelineExercisessList.expand"
id="app.pipelineExercisesList.expand"
defaultMessage="Expand the list and show all {count} exercises."
values={{ count: pipelineExercises.size }}
/>
Expand Down
Loading