diff --git a/src/components/Assignments/AdminAssignmentsTable/AdminAssignmentsTableRow.js b/src/components/Assignments/AdminAssignmentsTable/AdminAssignmentsTableRow.js index 0065cf131..a06368f76 100644 --- a/src/components/Assignments/AdminAssignmentsTable/AdminAssignmentsTableRow.js +++ b/src/components/Assignments/AdminAssignmentsTable/AdminAssignmentsTableRow.js @@ -8,7 +8,7 @@ import { LinkContainer } from 'react-router-bootstrap'; import DeleteAssignmentButtonContainer from '../../../containers/DeleteAssignmentButtonContainer'; import withLinks from '../../../hoc/withLinks'; -import { getLocalizedName } from '../../../helpers/getLocalizedData'; +import LocalizedExerciseName from '../../helpers/LocalizedExerciseName'; import { EditIcon, MaybePublicIcon, @@ -24,7 +24,6 @@ const AdminAssignmentTableRow = ({ firstDeadline, secondDeadline, isBonus, - locale, links: { ASSIGNMENT_DETAIL_URI_FACTORY: detail, ASSIGNMENT_EDIT_URI_FACTORY: edit @@ -37,7 +36,7 @@ const AdminAssignmentTableRow = ({ - {getLocalizedName({ name, localizedTexts }, locale)} + @@ -80,8 +79,7 @@ AdminAssignmentTableRow.propTypes = { allowSecondDeadline: PropTypes.bool.isRequired, secondDeadline: PropTypes.number.isRequired, groupId: PropTypes.string, - links: PropTypes.object, - locale: PropTypes.string.isRequired + links: PropTypes.object }; export default withLinks(AdminAssignmentTableRow); diff --git a/src/components/Assignments/Assignment/AssignmentTableRow/AssignmentTableRow.js b/src/components/Assignments/Assignment/AssignmentTableRow/AssignmentTableRow.js index ac6b6a90b..3143def0c 100644 --- a/src/components/Assignments/Assignment/AssignmentTableRow/AssignmentTableRow.js +++ b/src/components/Assignments/Assignment/AssignmentTableRow/AssignmentTableRow.js @@ -5,7 +5,7 @@ import AssignmentStatusIcon from '../AssignmentStatusIcon/AssignmentStatusIcon'; import { FormattedDate, FormattedTime } from 'react-intl'; import withLinks from '../../../../hoc/withLinks'; -import { getLocalizedName } from '../../../../helpers/getLocalizedData'; +import LocalizedExerciseName from '../../../helpers/LocalizedExerciseName'; import { MaybeBonusAssignmentIcon } from '../../../icons'; const AssignmentTableRow = ({ @@ -23,7 +23,6 @@ const AssignmentTableRow = ({ }, status, userId, - locale, links: { ASSIGNMENT_DETAIL_URI_FACTORY, ASSIGNMENT_DETAIL_SPECIFIC_USER_URI_FACTORY @@ -42,7 +41,7 @@ const AssignmentTableRow = ({ : ASSIGNMENT_DETAIL_URI_FACTORY(id) } > - {getLocalizedName({ name, localizedTexts }, locale)} + {showGroup && @@ -77,8 +76,7 @@ AssignmentTableRow.propTypes = { }), status: PropTypes.string, userId: PropTypes.string, - links: PropTypes.object, - locale: PropTypes.string.isRequired + links: PropTypes.object }; export default withLinks(AssignmentTableRow); diff --git a/src/components/Exercises/ExerciseDetail/ExerciseDetail.js b/src/components/Exercises/ExerciseDetail/ExerciseDetail.js index 14249f93a..d07387935 100644 --- a/src/components/Exercises/ExerciseDetail/ExerciseDetail.js +++ b/src/components/Exercises/ExerciseDetail/ExerciseDetail.js @@ -17,10 +17,8 @@ import UsersNameContainer from '../../../containers/UsersNameContainer'; import GroupsNameContainer from '../../../containers/GroupsNameContainer'; import styles from './ExerciseDetail.less'; import { MaybeSucceededIcon } from '../../icons'; -import { - getLocalizedName, - getLocalizedDescription -} from '../../../helpers/getLocalizedData'; +import { getLocalizedDescription } from '../../../helpers/getLocalizedData'; +import LocalizedExerciseName from '../../helpers/LocalizedExerciseName'; const ExerciseDetail = ({ id, @@ -40,7 +38,10 @@ const ExerciseDetail = ({ locale, links: { EXERCISE_URI_FACTORY } }) => - + } + noPadding + > diff --git a/src/components/Exercises/ExercisesListItem/ExercisesListItem.js b/src/components/Exercises/ExercisesListItem/ExercisesListItem.js index 9f2a434d2..a6cebac50 100644 --- a/src/components/Exercises/ExercisesListItem/ExercisesListItem.js +++ b/src/components/Exercises/ExercisesListItem/ExercisesListItem.js @@ -8,7 +8,7 @@ import GroupsNameContainer from '../../../containers/GroupsNameContainer'; import { Link } from 'react-router'; import withLinks from '../../../hoc/withLinks'; -import { getLocalizedName } from '../../../helpers/getLocalizedData'; +import LocalizedExerciseName from '../../helpers/LocalizedExerciseName'; import { MaybeLockedExerciseIcon } from '../../icons'; const ExercisesListItem = ({ @@ -32,7 +32,7 @@ const ExercisesListItem = ({ - {getLocalizedName({ name, localizedTexts }, locale)} + diff --git a/src/components/Exercises/ExercisesName/ExercisesName.js b/src/components/Exercises/ExercisesName/ExercisesName.js index 736ce29c4..06c64582e 100644 --- a/src/components/Exercises/ExercisesName/ExercisesName.js +++ b/src/components/Exercises/ExercisesName/ExercisesName.js @@ -3,7 +3,7 @@ import PropTypes from 'prop-types'; import { Link } from 'react-router'; import withLinks from '../../../hoc/withLinks'; -import { getLocalizedName } from '../../../helpers/getLocalizedData'; +import LocalizedExerciseName from '../../helpers/LocalizedExerciseName'; import { MaybeLockedExerciseIcon } from '../../icons'; @@ -13,17 +13,16 @@ const ExercisesName = ({ localizedTexts, isLocked, noLink, - locale, links: { EXERCISE_URI_FACTORY } }) => {noLink ? - {getLocalizedName({ name, localizedTexts }, locale)} + : - {getLocalizedName({ name, localizedTexts }, locale)} + } ; @@ -33,8 +32,7 @@ ExercisesName.propTypes = { localizedTexts: PropTypes.array.isRequired, isLocked: PropTypes.bool.isRequired, noLink: PropTypes.bool, - links: PropTypes.object, - locale: PropTypes.string.isRequired + links: PropTypes.object }; export default withLinks(ExercisesName); diff --git a/src/components/Exercises/ExercisesSimpleListItem/ExercisesSimpleListItem.js b/src/components/Exercises/ExercisesSimpleListItem/ExercisesSimpleListItem.js index e165ef199..3725bf80c 100644 --- a/src/components/Exercises/ExercisesSimpleListItem/ExercisesSimpleListItem.js +++ b/src/components/Exercises/ExercisesSimpleListItem/ExercisesSimpleListItem.js @@ -5,7 +5,7 @@ import UsersNameContainer from '../../../containers/UsersNameContainer'; import { Link } from 'react-router'; import withLinks from '../../../hoc/withLinks'; -import { getLocalizedName } from '../../../helpers/getLocalizedData'; +import LocalizedExerciseName from '../../helpers/LocalizedExerciseName'; import { MaybeLockedExerciseIcon } from '../../icons'; const ExercisesSimpleListItem = ({ @@ -24,7 +24,7 @@ const ExercisesSimpleListItem = ({ - {getLocalizedName({ name, localizedTexts }, locale)} + diff --git a/src/components/Groups/ResultsTable/ResultsTable.js b/src/components/Groups/ResultsTable/ResultsTable.js index b7d11b20e..33eabdc15 100644 --- a/src/components/Groups/ResultsTable/ResultsTable.js +++ b/src/components/Groups/ResultsTable/ResultsTable.js @@ -3,21 +3,20 @@ import PropTypes from 'prop-types'; import { List } from 'immutable'; import { Table } from 'react-bootstrap'; import { Link } from 'react-router'; -import { FormattedMessage, injectIntl } from 'react-intl'; +import { FormattedMessage } from 'react-intl'; import ResultsTableRow from './ResultsTableRow'; import LoadingResultsTableRow from './LoadingResultsTableRow'; import NoResultsAvailableRow from './NoResultsAvailableRow'; import withLinks from '../../../hoc/withLinks'; -import { getLocalizedName } from '../../../helpers/getLocalizedData'; import ResourceRenderer from '../../helpers/ResourceRenderer'; +import LocalizedExerciseName from '../../helpers/LocalizedExerciseName'; import styles from './ResultsTable.less'; const ResultsTable = ({ assignments = List(), users = [], submissions, - intl: { locale }, links: { SUPERVISOR_STATS_URI_FACTORY } }) => { const assignmentsArray = assignments.sort( @@ -34,7 +33,7 @@ const ResultsTable = ({
- {getLocalizedName(assignment, locale)} +
@@ -79,8 +78,7 @@ ResultsTable.propTypes = { assignments: PropTypes.array.isRequired, users: PropTypes.array.isRequired, submissions: PropTypes.object.isRequired, - links: PropTypes.object, - intl: PropTypes.shape({ locale: PropTypes.string.isRequired }).isRequired + links: PropTypes.object }; -export default injectIntl(withLinks(ResultsTable)); +export default withLinks(ResultsTable); diff --git a/src/components/forms/EditAssignmentForm/EditAssignmentForm.js b/src/components/forms/EditAssignmentForm/EditAssignmentForm.js index e71055e58..7fecff7fc 100644 --- a/src/components/forms/EditAssignmentForm/EditAssignmentForm.js +++ b/src/components/forms/EditAssignmentForm/EditAssignmentForm.js @@ -1,7 +1,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import { reduxForm, Field, FieldArray, touch } from 'redux-form'; -import { FormattedMessage, injectIntl } from 'react-intl'; +import { FormattedMessage } from 'react-intl'; import { Alert, HelpBlock } from 'react-bootstrap'; import isNumeric from 'validator/lib/isNumeric'; @@ -11,7 +11,7 @@ import LocalizedTextsFormField from '../LocalizedTextsFormField'; import SubmitButton from '../SubmitButton'; import { validateAssignment } from '../../../redux/modules/assignments'; -import { getLocalizedName } from '../../../helpers/getLocalizedData'; +import LocalizedExerciseName from '../../helpers/LocalizedExerciseName'; const EditAssignmentForm = ({ initialValues: assignment, @@ -22,8 +22,7 @@ const EditAssignmentForm = ({ submitSucceeded: hasSucceeded, asyncValidating, invalid, - formValues: { firstDeadline, allowSecondDeadline, localizedTexts } = {}, - intl: { locale } + formValues: { firstDeadline, allowSecondDeadline, localizedTexts } = {} }) =>
}} /> } successful={hasSucceeded} @@ -233,8 +232,7 @@ EditAssignmentForm.propTypes = { PropTypes.string ]), localizedTexts: PropTypes.array - }), - intl: PropTypes.shape({ locale: PropTypes.string.isRequired }).isRequired + }) }; const isNonNegativeInteger = n => @@ -441,10 +439,8 @@ const asyncValidate = (values, dispatch, { assignment: { id, version } }) => .catch(errors => reject(errors)) ); -export default injectIntl( - reduxForm({ - form: 'editAssignment', - validate, - asyncValidate - })(EditAssignmentForm) -); +export default reduxForm({ + form: 'editAssignment', + validate, + asyncValidate +})(EditAssignmentForm); diff --git a/src/components/forms/EditExerciseForm/EditExerciseForm.js b/src/components/forms/EditExerciseForm/EditExerciseForm.js index c7a6c9856..0a8afbf6e 100644 --- a/src/components/forms/EditExerciseForm/EditExerciseForm.js +++ b/src/components/forms/EditExerciseForm/EditExerciseForm.js @@ -18,9 +18,9 @@ import FormBox from '../../widgets/FormBox'; import SubmitButton from '../SubmitButton'; import Button from '../../widgets/FlatButton'; import LocalizedTextsFormField from '../LocalizedTextsFormField'; +import LocalizedExerciseName from '../../helpers/LocalizedExerciseName'; import { validateExercise } from '../../../redux/modules/exercises'; import withLinks from '../../../hoc/withLinks'; -import { getLocalizedName } from '../../../helpers/getLocalizedData'; if (canUseDOM) { require('codemirror/mode/yaml/yaml'); @@ -59,7 +59,7 @@ const EditExerciseForm = ({ }} /> } succeeded={hasSucceeded} diff --git a/src/components/helpers/LocalizedExerciseName/LocalizedExerciseName.js b/src/components/helpers/LocalizedExerciseName/LocalizedExerciseName.js new file mode 100644 index 000000000..c46ece4f5 --- /dev/null +++ b/src/components/helpers/LocalizedExerciseName/LocalizedExerciseName.js @@ -0,0 +1,43 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { injectIntl } from 'react-intl'; +import { OverlayTrigger, Tooltip } from 'react-bootstrap'; +import Icon from 'react-fontawesome'; + +import { + getLocalizedName, + getOtherLocalizedNames +} from '../../../helpers/getLocalizedData'; + +const LocalizedExerciseName = ({ entity, intl: { locale } }) => { + const otherNames = getOtherLocalizedNames(entity, locale); + return ( + + {getLocalizedName(entity, locale)} + {otherNames.length > 0 && + +   n.name).join(', ')}> + {otherNames.map(name => +
+ {name.name} [{name.locale}] +
+ )} + + } + > + +
  +
} +
+ ); +}; + +LocalizedExerciseName.propTypes = { + entity: PropTypes.object, + intl: PropTypes.shape({ locale: PropTypes.string.isRequired }).isRequired +}; + +export default injectIntl(LocalizedExerciseName); diff --git a/src/components/helpers/LocalizedExerciseName/index.js b/src/components/helpers/LocalizedExerciseName/index.js new file mode 100644 index 000000000..d9cd439e2 --- /dev/null +++ b/src/components/helpers/LocalizedExerciseName/index.js @@ -0,0 +1 @@ +export default from './LocalizedExerciseName'; diff --git a/src/helpers/getLocalizedData.js b/src/helpers/getLocalizedData.js index e692486b7..1de68cd7b 100644 --- a/src/helpers/getLocalizedData.js +++ b/src/helpers/getLocalizedData.js @@ -7,3 +7,10 @@ const getLocalizedX = field => (entity, locale) => { export const getLocalizedName = getLocalizedX('name'); export const getLocalizedDescription = getLocalizedX('description'); + +export const getOtherLocalizedNames = (entity, locale) => { + const name = getLocalizedName(entity, locale); + return entity.localizedTexts + .filter(text => text.name && text.name !== name) + .map(({ name, locale }) => ({ name, locale })); +}; diff --git a/src/pages/Assignment/Assignment.js b/src/pages/Assignment/Assignment.js index f1a763b35..d588ca906 100644 --- a/src/pages/Assignment/Assignment.js +++ b/src/pages/Assignment/Assignment.js @@ -2,8 +2,9 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; import ImmutablePropTypes from 'react-immutable-proptypes'; import { connect } from 'react-redux'; -import { FormattedMessage, injectIntl } from 'react-intl'; +import { FormattedMessage } from 'react-intl'; import { Col, Row, Alert } from 'react-bootstrap'; + import Button from '../../components/widgets/FlatButton'; import { LinkContainer } from 'react-router-bootstrap'; @@ -34,6 +35,7 @@ import { runtimeEnvironmentSelector } from '../../redux/selectors/runtimeEnviron import Page from '../../components/layout/Page'; import ResourceRenderer from '../../components/helpers/ResourceRenderer'; +import LocalizedExerciseName from '../../components/helpers/LocalizedExerciseName'; import UsersNameContainer from '../../containers/UsersNameContainer'; import { ResubmitAllSolutionsContainer } from '../../containers/ResubmitSolutionContainer'; import HierarchyLineContainer from '../../containers/HierarchyLineContainer'; @@ -45,7 +47,6 @@ import SubmitSolutionContainer from '../../containers/SubmitSolutionContainer'; import SubmissionsTableContainer from '../../containers/SubmissionsTableContainer'; import withLinks from '../../hoc/withLinks'; -import { getLocalizedName } from '../../helpers/getLocalizedData'; class Assignment extends Component { static loadAsync = ({ assignmentId }, dispatch) => @@ -82,17 +83,13 @@ class Assignment extends Component { canSubmit, runtimeEnvironments, exerciseSync, - intl: { locale }, links: { ASSIGNMENT_EDIT_URI_FACTORY, SUPERVISOR_STATS_URI_FACTORY } } = this.props; return ( - - {getLocalizedName(assignment, locale)} - } + title={assignment => } description={ { - const assignmentSelector = getAssignment(assignmentId); - const environments = runtimeEnvironmentsSelector(assignmentId)( - state - ).toJS(); - const loggedInUserId = loggedInUserIdSelector(state); - userId = userId || loggedInUserId; - return { - assignment: assignmentSelector(state), - submitting: isSubmitting(state), - runtimeEnvironments: environments.map(i => - runtimeEnvironmentSelector(i)(state) - ), - userId, - loggedInUserId, - isSuperAdmin: isSuperAdmin(loggedInUserId)(state), - isStudentOf: groupId => isStudentOf(loggedInUserId, groupId)(state), - isSupervisorOf: groupId => - isSupervisorOf(loggedInUserId, groupId)(state), - canSubmit: canSubmitSolution(assignmentId)(state) - }; - }, - (dispatch, { params: { assignmentId } }) => ({ - init: userId => () => dispatch(init(userId, assignmentId)), - loadAsync: () => Assignment.loadAsync({ assignmentId }, dispatch), - exerciseSync: () => dispatch(syncWithExercise(assignmentId)) - }) - )(Assignment) - ) +export default withLinks( + connect( + (state, { params: { assignmentId, userId } }) => { + const assignmentSelector = getAssignment(assignmentId); + const environments = runtimeEnvironmentsSelector(assignmentId)( + state + ).toJS(); + const loggedInUserId = loggedInUserIdSelector(state); + userId = userId || loggedInUserId; + return { + assignment: assignmentSelector(state), + submitting: isSubmitting(state), + runtimeEnvironments: environments.map(i => + runtimeEnvironmentSelector(i)(state) + ), + userId, + loggedInUserId, + isSuperAdmin: isSuperAdmin(loggedInUserId)(state), + isStudentOf: groupId => isStudentOf(loggedInUserId, groupId)(state), + isSupervisorOf: groupId => + isSupervisorOf(loggedInUserId, groupId)(state), + canSubmit: canSubmitSolution(assignmentId)(state) + }; + }, + (dispatch, { params: { assignmentId } }) => ({ + init: userId => () => dispatch(init(userId, assignmentId)), + loadAsync: () => Assignment.loadAsync({ assignmentId }, dispatch), + exerciseSync: () => dispatch(syncWithExercise(assignmentId)) + }) + )(Assignment) ); diff --git a/src/pages/EditExercise/EditExercise.js b/src/pages/EditExercise/EditExercise.js index 8f3515e6c..16381e4fc 100644 --- a/src/pages/EditExercise/EditExercise.js +++ b/src/pages/EditExercise/EditExercise.js @@ -12,6 +12,7 @@ import Box from '../../components/widgets/Box'; import EditExerciseForm from '../../components/forms/EditExerciseForm'; import AdditionalExerciseFilesTableContainer from '../../containers/AdditionalExerciseFilesTableContainer'; import DeleteExerciseButtonContainer from '../../containers/DeleteExerciseButtonContainer'; +import LocalizedExerciseName from '../../components/helpers/LocalizedExerciseName'; import { fetchExerciseIfNeeded, @@ -50,7 +51,7 @@ class EditExercise extends Component { return ( getLocalizedName(exercise, locale)} + title={exercise => } description={ getLocalizedName(exercise, locale)} + title={exercise => } description={ getLocalizedName(exercise, locale)} + title={exercise => } resource={exercise} description={ @@ -49,14 +49,13 @@ class Submission extends Component { assignment, submission, params: { assignmentId }, - isSupervisorOrMore, - intl: { locale } + isSupervisorOrMore } = this.props; return ( getLocalizedName(assignment, locale)} + title={assignment => } description={ ({ - submission: getSubmission(submissionId)(state), - assignment: getAssignment(assignmentId)(state), - isSupervisorOrMore: groupId => - isSupervisorOf(loggedInUserIdSelector(state), groupId)(state) || - isAdminOf(loggedInUserIdSelector(state), groupId)(state) || - isSuperAdmin(loggedInUserIdSelector(state))(state) - }), - (dispatch, { params }) => ({ - loadAsync: () => Submission.loadAsync(params, dispatch) - }) - )(Submission) -); +export default connect( + (state, { params: { submissionId, assignmentId } }) => ({ + submission: getSubmission(submissionId)(state), + assignment: getAssignment(assignmentId)(state), + isSupervisorOrMore: groupId => + isSupervisorOf(loggedInUserIdSelector(state), groupId)(state) || + isAdminOf(loggedInUserIdSelector(state), groupId)(state) || + isSuperAdmin(loggedInUserIdSelector(state))(state) + }), + (dispatch, { params }) => ({ + loadAsync: () => Submission.loadAsync(params, dispatch) + }) +)(Submission);