diff --git a/src/components/Groups/AdminsView/AdminsView.js b/src/components/Groups/AdminsView/AdminsView.js index 469ca95c9..7a04ea093 100644 --- a/src/components/Groups/AdminsView/AdminsView.js +++ b/src/components/Groups/AdminsView/AdminsView.js @@ -33,7 +33,10 @@ const AdminsView = ({ group, addSubgroup, formValues, intl: { locale } }) => /> } > - + diff --git a/src/components/Groups/GroupDetail/GroupDetail.js b/src/components/Groups/GroupDetail/GroupDetail.js index dd82b846f..52ba1f02f 100644 --- a/src/components/Groups/GroupDetail/GroupDetail.js +++ b/src/components/Groups/GroupDetail/GroupDetail.js @@ -1,6 +1,5 @@ import React from 'react'; import PropTypes from 'prop-types'; -import ImmutablePropTypes from 'react-immutable-proptypes'; import { FormattedMessage, FormattedNumber, injectIntl } from 'react-intl'; import { Row, Col, Table } from 'react-bootstrap'; import ReactMarkdown from 'react-remarkable'; @@ -20,16 +19,17 @@ const GroupDetail = ({ externalId, name, localizedTexts, - description, - threshold, - parentGroupId, - isPublic = false, - childGroups, primaryAdminsIds, - ...group + childGroups, + privateData: { + description, + threshold, + parentGroupId, + isPublic = false, + ...privateGroup + } }, groups, - publicGroups, supervisors, isAdmin, intl: { locale } @@ -120,7 +120,7 @@ const GroupDetail = ({ deletable={false} isAdmin={isAdmin} isOpen - groups={publicGroups} + groups={groups} level={1} /> @@ -147,7 +147,7 @@ const GroupDetail = ({ users={supervisors} isAdmin={isAdmin} primaryAdminsIds={primaryAdminsIds} - isLoaded={supervisors.length === group.supervisors.length} + isLoaded={supervisors.length === privateGroup.supervisors.length} /> @@ -158,19 +158,20 @@ GroupDetail.propTypes = { group: PropTypes.shape({ id: PropTypes.string.isRequired, name: PropTypes.string.isRequired, - description: PropTypes.string.isRequired, parentGroupId: PropTypes.string, childGroups: PropTypes.shape({ all: PropTypes.array, public: PropTypes.array.isRequired }), threshold: PropTypes.number, - isPublic: PropTypes.bool, - supervisors: PropTypes.array.isRequired, - primaryAdminsIds: PropTypes.array.isRequired + primaryAdminsIds: PropTypes.array.isRequired, + privateData: PropTypes.shape({ + description: PropTypes.string.isRequired, + isPublic: PropTypes.bool, + supervisors: PropTypes.array.isRequired + }) }), groups: PropTypes.object.isRequired, - publicGroups: ImmutablePropTypes.map.isRequired, supervisors: PropTypes.array.isRequired, isAdmin: PropTypes.bool, intl: PropTypes.shape({ locale: PropTypes.string.isRequired }).isRequired diff --git a/src/components/Groups/GroupTree/GroupTree.js b/src/components/Groups/GroupTree/GroupTree.js index 2d2f0ade9..82cbef8f5 100644 --- a/src/components/Groups/GroupTree/GroupTree.js +++ b/src/components/Groups/GroupTree/GroupTree.js @@ -61,9 +61,9 @@ class GroupTree extends Component { const { name, localizedTexts, - admins, + canView, childGroups: { all: allChildGroups, public: publicChildGroups }, - canView + primaryAdminsIds } = getJsData(group); return ( @@ -79,7 +79,7 @@ class GroupTree extends Component { /> } level={level} - admins={admins} + admins={primaryAdminsIds} isOpen={currentGroupId === id || isOpen} actions={ currentGroupId !== id && canView diff --git a/src/components/Groups/SupervisorsView/SupervisorsView.js b/src/components/Groups/SupervisorsView/SupervisorsView.js index 85da5a1d5..7e492a435 100644 --- a/src/components/Groups/SupervisorsView/SupervisorsView.js +++ b/src/components/Groups/SupervisorsView/SupervisorsView.js @@ -91,7 +91,10 @@ const SupervisorsView = ({ } isOpen > - +    ( - {admins - .map(a => a.name.firstName + ' ' + a.name.lastName) - .join(', ')} + {admins.map(a => + + )} ) } diff --git a/src/containers/GroupsNameContainer/GroupsNameContainer.js b/src/containers/GroupsNameContainer/GroupsNameContainer.js index 1da3f2f4d..9c2a6db80 100644 --- a/src/containers/GroupsNameContainer/GroupsNameContainer.js +++ b/src/containers/GroupsNameContainer/GroupsNameContainer.js @@ -3,8 +3,8 @@ import PropTypes from 'prop-types'; import ImmutablePropTypes from 'react-immutable-proptypes'; import { connect } from 'react-redux'; -import { fetchPublicGroupIfNeeded } from '../../redux/modules/publicGroups'; -import { publicGroupSelector } from '../../redux/selectors/publicGroups'; +import { fetchGroupIfNeeded } from '../../redux/modules/groups'; +import { groupSelector } from '../../redux/selectors/groups'; import ResourceRenderer from '../../components/helpers/ResourceRenderer'; import GroupsName, { LoadingGroupsName @@ -22,7 +22,7 @@ class GroupsNameContainer extends Component { } static loadAsync = ({ groupId }, dispatch) => { - dispatch(fetchPublicGroupIfNeeded(groupId)); + dispatch(fetchGroupIfNeeded(groupId)); }; render() { @@ -44,7 +44,7 @@ GroupsNameContainer.propTypes = { export default connect( (state, { groupId }) => ({ - group: publicGroupSelector(groupId)(state) + group: groupSelector(groupId)(state) }), (dispatch, { groupId }) => ({ loadAsync: () => GroupsNameContainer.loadAsync({ groupId }, dispatch) diff --git a/src/containers/HierarchyLineContainer/HierarchyLineContainer.js b/src/containers/HierarchyLineContainer/HierarchyLineContainer.js index 1155592bd..42a714b46 100644 --- a/src/containers/HierarchyLineContainer/HierarchyLineContainer.js +++ b/src/containers/HierarchyLineContainer/HierarchyLineContainer.js @@ -30,7 +30,7 @@ class HierarchyLineContainer extends Component { {group => } ); diff --git a/src/containers/MakeRemoveSupervisorButtonContainer/MakeRemoveSupervisorButtonContainer.js b/src/containers/MakeRemoveSupervisorButtonContainer/MakeRemoveSupervisorButtonContainer.js index bfb200352..8fdbfab66 100644 --- a/src/containers/MakeRemoveSupervisorButtonContainer/MakeRemoveSupervisorButtonContainer.js +++ b/src/containers/MakeRemoveSupervisorButtonContainer/MakeRemoveSupervisorButtonContainer.js @@ -2,7 +2,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import { connect } from 'react-redux'; import { makeSupervisor, removeSupervisor } from '../../redux/modules/groups'; -import { fetchProfileIfNeeded } from '../../redux/modules/publicProfiles'; +import { fetchUserIfNeeded } from '../../redux/modules/users'; import { isSupervisorOf } from '../../redux/selectors/users'; import MakeSupervisorButton from '../../components/Groups/MakeSupervisorButton'; @@ -14,7 +14,7 @@ const MakeRemoveSupervisorButtonContainer = ({ groupId, makeSupervisor, removeSupervisor, - fetchProfileIfNeeded, + fetchUserIfNeeded, ...props }) => isSupervisor @@ -26,9 +26,7 @@ const MakeRemoveSupervisorButtonContainer = ({ : { - fetchProfileIfNeeded(userId).then(() => - makeSupervisor(groupId, userId) - ); + fetchUserIfNeeded(userId).then(() => makeSupervisor(groupId, userId)); }} bsSize="xs" />; @@ -39,7 +37,7 @@ MakeRemoveSupervisorButtonContainer.propTypes = { isSupervisor: PropTypes.bool.isRequired, makeSupervisor: PropTypes.func.isRequired, removeSupervisor: PropTypes.func.isRequired, - fetchProfileIfNeeded: PropTypes.func.isRequired + fetchUserIfNeeded: PropTypes.func.isRequired }; const mapStateToProps = (state, { groupId, userId }) => ({ @@ -49,7 +47,7 @@ const mapStateToProps = (state, { groupId, userId }) => ({ const mapDispatchToProps = { makeSupervisor, removeSupervisor, - fetchProfileIfNeeded + fetchUserIfNeeded }; export default connect(mapStateToProps, mapDispatchToProps)( diff --git a/src/containers/SisSupervisorGroupsContainer/SisSupervisorGroupsContainer.js b/src/containers/SisSupervisorGroupsContainer/SisSupervisorGroupsContainer.js index 748f8a1d9..a38787407 100644 --- a/src/containers/SisSupervisorGroupsContainer/SisSupervisorGroupsContainer.js +++ b/src/containers/SisSupervisorGroupsContainer/SisSupervisorGroupsContainer.js @@ -9,7 +9,7 @@ import Button from '../../components/widgets/FlatButton'; import { LinkContainer } from 'react-router-bootstrap'; import Icon from 'react-fontawesome'; -import { fetchPublicGroupsIfNeeded } from '../../redux/modules/publicGroups'; +import { fetchGroupsIfNeeded } from '../../redux/modules/groups'; import { fetchSisStatusIfNeeded } from '../../redux/modules/sisStatus'; import { fetchSisSupervisedCourses, @@ -21,7 +21,7 @@ import { sisPossibleParentsSelector } from '../../redux/selectors/sisPossiblePar import { sisStateSelector } from '../../redux/selectors/sisStatus'; import { sisSupervisedCoursesSelector } from '../../redux/selectors/sisSupervisedCourses'; import { loggedInUserIdSelector } from '../../redux/selectors/auth'; -import { publicGroupDataAccessorSelector } from '../../redux/selectors/publicGroups'; +import { groupDataAccessorSelector } from '../../redux/selectors/groups'; import UsersNameContainer from '../UsersNameContainer'; import ResourceRenderer from '../../components/helpers/ResourceRenderer'; @@ -73,7 +73,7 @@ class SisSupervisorGroupsContainer extends Component { .then(parents => parents.map(parent => dispatch( - fetchPublicGroupsIfNeeded( + fetchGroupsIfNeeded( parent.id, ...parent.parentGroupsIds ) @@ -346,7 +346,7 @@ export default injectIntl( currentUserId, sisCourses: sisSupervisedCoursesSelector(state), sisPossibleParents: sisPossibleParentsSelector(state), - groupsAccessor: publicGroupDataAccessorSelector(state) + groupsAccessor: groupDataAccessorSelector(state) }; }, dispatch => ({ diff --git a/src/containers/StudentsListContainer/StudentsListContainer.js b/src/containers/StudentsListContainer/StudentsListContainer.js index 66c8263c5..95edd9cdc 100644 --- a/src/containers/StudentsListContainer/StudentsListContainer.js +++ b/src/containers/StudentsListContainer/StudentsListContainer.js @@ -24,14 +24,13 @@ class StudentsListContainer extends Component { const { group, students, stats, ...props } = this.props; return ( - {group => ( + {group => - )} + />} ); } diff --git a/src/containers/UsersNameContainer/UsersNameContainer.css b/src/containers/UsersNameContainer/UsersNameContainer.css new file mode 100644 index 000000000..c28d7074b --- /dev/null +++ b/src/containers/UsersNameContainer/UsersNameContainer.css @@ -0,0 +1,5 @@ +.simpleName { + margin-left: 4px; + margin-right: 4px; + white-space: nowrap; +} diff --git a/src/containers/UsersNameContainer/UsersNameContainer.js b/src/containers/UsersNameContainer/UsersNameContainer.js index 02d63ba3e..13a62e0ad 100644 --- a/src/containers/UsersNameContainer/UsersNameContainer.js +++ b/src/containers/UsersNameContainer/UsersNameContainer.js @@ -3,8 +3,8 @@ import PropTypes from 'prop-types'; import ImmutablePropTypes from 'react-immutable-proptypes'; import { connect } from 'react-redux'; -import { fetchProfileIfNeeded } from '../../redux/modules/publicProfiles'; -import { getProfile } from '../../redux/selectors/publicProfiles'; +import { fetchUserIfNeeded } from '../../redux/modules/users'; +import { getUser } from '../../redux/selectors/users'; import { loggedInUserIdSelector } from '../../redux/selectors/auth'; import ResourceRenderer from '../../components/helpers/ResourceRenderer'; import UsersName, { @@ -12,6 +12,8 @@ import UsersName, { FailedUsersName } from '../../components/Users/UsersName'; +import './UsersNameContainer.css'; + class UsersNameContainer extends Component { componentWillMount() { this.props.loadAsync(); @@ -24,11 +26,11 @@ class UsersNameContainer extends Component { } static loadAsync = ({ userId }, dispatch) => { - dispatch(fetchProfileIfNeeded(userId)); + dispatch(fetchUserIfNeeded(userId)); }; render() { - const { user, large, noLink, currentUserId } = this.props; + const { user, large, noLink, currentUserId, isSimple = false } = this.props; const size = large ? 45 : 22; return ( } > {user => - } + isSimple + ? + {user.name.firstName} {user.name.lastName} + + : } ); } @@ -55,16 +61,17 @@ UsersNameContainer.propTypes = { large: PropTypes.bool, user: ImmutablePropTypes.map, noLink: PropTypes.bool, - loadAsync: PropTypes.func.isRequired + loadAsync: PropTypes.func.isRequired, + isSimple: PropTypes.bool }; export default connect( (state, { userId }) => ({ - user: getProfile(userId)(state), + user: getUser(userId)(state), currentUserId: loggedInUserIdSelector(state) }), (dispatch, { userId }) => ({ - loadProfileIfNeeded: () => dispatch(fetchProfileIfNeeded(userId)), + loadProfileIfNeeded: () => dispatch(fetchUserIfNeeded(userId)), loadAsync: () => UsersNameContainer.loadAsync({ userId }, dispatch) }) )(UsersNameContainer); diff --git a/src/pages/Dashboard/Dashboard.js b/src/pages/Dashboard/Dashboard.js index 0f8585d0f..e99a0fd9f 100644 --- a/src/pages/Dashboard/Dashboard.js +++ b/src/pages/Dashboard/Dashboard.js @@ -21,9 +21,8 @@ import { fetchAssignmentsForGroup } from '../../redux/modules/assignments'; import { fetchUserIfNeeded } from '../../redux/modules/users'; import { fetchGroupsIfNeeded, - fetchInstanceGroupsIfNeeded + fetchInstanceGroups } from '../../redux/modules/groups'; -import { fetchPublicGroupsIfNeeded } from '../../redux/modules/publicGroups'; import { getUser, @@ -74,14 +73,16 @@ class Dashboard extends Component { dispatch(fetchUserIfNeeded(userId)).then(() => { const state = getState(); const user = getJsData(getUser(userId)(state)); - const groups = user.groups.studentOf.concat(user.groups.supervisorOf); + const groups = user.privateData.groups.studentOf.concat( + user.privateData.groups.supervisorOf + ); const isAdmin = isLoggedAsSuperAdmin(state); return dispatch(fetchGroupsIfNeeded(...groups)).then(groups => Promise.all( [ isAdmin - ? dispatch(fetchInstanceGroupsIfNeeded(user.instanceId)) + ? dispatch(fetchInstanceGroups(user.privateData.instanceId)) : Promise.resolve() ].concat( groups.map(({ value: group }) => @@ -89,10 +90,7 @@ class Dashboard extends Component { dispatch(fetchAssignmentsForGroup(group.id)), dispatch(fetchGroupsStatsIfNeeded(group.id)), dispatch( - fetchPublicGroupsIfNeeded( - group.id, - ...group.parentGroupsIds - ) + fetchGroupsIfNeeded(...group.privateData.parentGroupsIds) ) ]) ) @@ -307,37 +305,33 @@ class Dashboard extends Component { {groups.map(group => - { - - {statistics => - } - collapsable - noPadding - isOpen - footer={ -

- - - -

- } - > - -
} -
- } + + {statistics => + } + collapsable + noPadding + isOpen + footer={ +

+ + + +

+ } + > + +
} +
)} diff --git a/src/pages/EditGroup/EditGroup.js b/src/pages/EditGroup/EditGroup.js index 5e786398e..d91893e4d 100644 --- a/src/pages/EditGroup/EditGroup.js +++ b/src/pages/EditGroup/EditGroup.js @@ -99,14 +99,14 @@ class EditGroup extends Component { 0) } onDeleted={() => - push(GROUP_URI_FACTORY(group.parentGroupId))} + push(GROUP_URI_FACTORY(group.privateData.parentGroupId))} /> - {group.parentGroupId === null && + {group.privateData.parentGroupId === null && } diff --git a/src/pages/Group/Group.js b/src/pages/Group/Group.js index 95c570a5b..5573bd455 100644 --- a/src/pages/Group/Group.js +++ b/src/pages/Group/Group.js @@ -24,7 +24,7 @@ import { LocalizedGroupName } from '../../components/helpers/LocalizedNames'; import { createGroup, fetchGroupIfNeeded, - fetchInstanceGroupsIfNeeded, + fetchInstanceGroups, fetchSubgroups } from '../../redux/modules/groups'; import { fetchGroupsStats } from '../../redux/modules/stats'; @@ -37,7 +37,6 @@ import { fetchGroupExercises, create as createExercise } from '../../redux/modules/exercises'; -import { fetchInstancePublicGroups } from '../../redux/modules/publicGroups'; import { loggedInUserIdSelector } from '../../redux/selectors/auth'; import { isStudentOf, @@ -55,7 +54,6 @@ import { groupsAllAssignmentsSelector } from '../../redux/selectors/groups'; import { getExercisesForGroup } from '../../redux/selectors/exercises'; -import { publicGroupsSelectors } from '../../redux/selectors/publicGroups'; import { getStatusesForLoggedUser } from '../../redux/selectors/stats'; @@ -67,40 +65,40 @@ import { getBestSubmissionsForLoggedInUser } from '../../redux/selectors/groupRe class Group extends Component { static isAdminOrSupervisorOf = (group, userId) => - group.admins.indexOf(userId) >= 0 || group.supervisors.indexOf(userId) >= 0; + group.privateData.admins.indexOf(userId) >= 0 || + group.privateData.supervisors.indexOf(userId) >= 0; static isMemberOf = (group, userId) => Group.isAdminOrSupervisorOf(group, userId) || - group.students.indexOf(userId) >= 0; + group.privateData.students.indexOf(userId) >= 0; static loadAsync = ({ groupId }, dispatch, userId, isSuperAdmin) => Promise.all([ - dispatch(fetchGroupIfNeeded(groupId)).then(res => res.value).then(group => - Promise.all([ - dispatch(fetchSupervisors(groupId)), - dispatch(fetchInstancePublicGroups(group.instanceId)), - Group.isAdminOrSupervisorOf(group, userId) || isSuperAdmin - ? Promise.all([ - dispatch(fetchInstanceGroupsIfNeeded(group.instanceId)), // for group traversal finding group exercises - dispatch(fetchGroupExercises(groupId)) - ]) - : Promise.resolve(), - Group.isMemberOf(group, userId) || isSuperAdmin - ? Promise.all([ - dispatch(fetchAssignmentsForGroup(groupId)), - dispatch(fetchStudents(groupId)), - dispatch(fetchGroupsStats(groupId)) - ]) - : Promise.resolve(), - group.students.indexOf(userId) >= 0 - ? Promise.all( - group.assignments.all.map(assignmentId => - dispatch(fetchBestSubmission(userId, assignmentId)) + dispatch(fetchGroupIfNeeded(groupId)) + .then(res => res.value) + .then(group => + Promise.all([ + dispatch(fetchSupervisors(groupId)), + dispatch(fetchInstanceGroups(group.privateData.instanceId)), + Group.isAdminOrSupervisorOf(group, userId) || isSuperAdmin + ? Promise.all([dispatch(fetchGroupExercises(groupId))]) + : Promise.resolve(), + Group.isMemberOf(group, userId) || isSuperAdmin + ? Promise.all([ + dispatch(fetchAssignmentsForGroup(groupId)), + dispatch(fetchStudents(groupId)), + dispatch(fetchGroupsStats(groupId)) + ]) + : Promise.resolve(), + group.privateData.students.indexOf(userId) >= 0 + ? Promise.all( + group.privateData.assignments.all.map(assignmentId => + dispatch(fetchBestSubmission(userId, assignmentId)) + ) ) - ) - : Promise.resolve() - ]) - ), + : Promise.resolve() + ]) + ), dispatch(fetchSubgroups(groupId)) ]); @@ -122,8 +120,8 @@ class Group extends Component { } if (isReady(this.props.group) && isReady(newProps.group)) { - const thisData = this.props.group.toJS().data; - const newData = newProps.group.toJS().data; + const thisData = this.props.group.toJS().data.privateData; + const newData = newProps.group.toJS().data.privateData; if (thisData.supervisors.length !== newData.supervisors.length) { newProps.refetchSupervisors(); } @@ -141,7 +139,7 @@ class Group extends Component { iconName: 'university', breadcrumb: data => ({ link: ({ INSTANCE_URI_FACTORY }) => - INSTANCE_URI_FACTORY(data.instanceId), + INSTANCE_URI_FACTORY(data.privateData.instanceId), text: 'Instance' }) }, @@ -180,7 +178,6 @@ class Group extends Component { group, userId, groups, - publicGroups, students, supervisors = List(), allAssignments = List(), @@ -215,7 +212,7 @@ class Group extends Component {

{(isAdmin || isSuperAdmin) && @@ -245,7 +242,6 @@ class Group extends Component { supervisors={supervisors} isAdmin={isAdmin || isSuperAdmin} groups={groups} - publicGroups={publicGroups} /> {!isAdmin && @@ -262,7 +258,7 @@ class Group extends Component { } {(isAdmin || isSuperAdmin || isSupervisor) && @@ -293,7 +289,6 @@ Group.propTypes = { groupExercises: ImmutablePropTypes.map, publicAssignments: ImmutablePropTypes.list, groups: ImmutablePropTypes.map, - publicGroups: ImmutablePropTypes.map, isStudent: PropTypes.bool, isAdmin: PropTypes.bool, isSupervisor: PropTypes.bool, @@ -322,7 +317,6 @@ const mapStateToProps = (state, { params: { groupId } }) => { group: groupSelector(groupId)(state), userId, groups: groupsSelector(state), - publicGroups: publicGroupsSelectors(state), publicAssignments: groupsPublicAssignmentsSelector(state, groupId), allAssignments: groupsAllAssignmentsSelector(state, groupId), groupExercises: getExercisesForGroup(state, groupId), diff --git a/src/pages/Instance/Instance.js b/src/pages/Instance/Instance.js index f38a7e492..43e4bcc57 100644 --- a/src/pages/Instance/Instance.js +++ b/src/pages/Instance/Instance.js @@ -20,9 +20,8 @@ import { instanceSelector, isAdminOfInstance } from '../../redux/selectors/instances'; -import { createGroup } from '../../redux/modules/groups'; -import { fetchInstancePublicGroups } from '../../redux/modules/publicGroups'; -import { publicGroupsSelectors } from '../../redux/selectors/publicGroups'; +import { createGroup, fetchInstanceGroups } from '../../redux/modules/groups'; +import { groupsSelector } from '../../redux/selectors/groups'; import { loggedInUserIdSelector } from '../../redux/selectors/auth'; import { isLoggedAsSuperAdmin } from '../../redux/selectors/users'; @@ -32,7 +31,7 @@ class Instance extends Component { static loadAsync = ({ instanceId }, dispatch) => Promise.all([ dispatch(fetchInstanceIfNeeded(instanceId)), - dispatch(fetchInstancePublicGroups(instanceId)) + dispatch(fetchInstanceGroups(instanceId)) ]); componentWillMount() { @@ -150,7 +149,7 @@ export default withLinks( const userId = loggedInUserIdSelector(state); return { instance: instanceSelector(state, instanceId), - groups: publicGroupsSelectors(state), + groups: groupsSelector(state), isAdmin: isAdminOfInstance(userId, instanceId)(state), isSuperAdmin: isLoggedAsSuperAdmin(state), formValues: getFormValues('editGroup')(state) diff --git a/src/pages/Login/Login.js b/src/pages/Login/Login.js index 03b43f964..c7a6c8832 100644 --- a/src/pages/Login/Login.js +++ b/src/pages/Login/Login.js @@ -80,8 +80,7 @@ class Login extends Component { - {' '} + />{' '} dispatch((dispatch, getState) => - dispatch(fetchProfileIfNeeded(userId)) + dispatch(fetchUserIfNeeded(userId)) .then(() => dispatch(fetchUserIfNeeded(loggedInUserId))) .then(() => { const state = getState(); const instanceId = getJsData(getUser(loggedInUserId)(state)) - .instanceId; + .privateData.instanceId; - return dispatch( - fetchInstanceGroupsIfNeeded(instanceId) - ).then(groups => + return dispatch(fetchInstanceGroups(instanceId)).then(groups => Promise.all( groups.value.map(group => { if ( - group.students.indexOf(userId) >= 0 && + group.privateData && + group.privateData.students.indexOf(userId) >= 0 && (isAdmin || userId === loggedInUserId || - group.supervisors.indexOf(loggedInUserId) >= 0 || - group.admins.indexOf(loggedInUserId) >= 0) + group.privateData.supervisors.indexOf(loggedInUserId) >= + 0 || + group.privateData.admins.indexOf(loggedInUserId) >= 0) ) { return Promise.all([ dispatch(fetchAssignmentsForGroup(group.id)), @@ -314,7 +312,7 @@ export default withLinks( return { loggedInUserId, student: isStudent(userId)(state), - user: getProfile(userId)(state), + user: getUser(userId)(state), isAdmin: isSuperadmin, studentOfGroupsIds: studentOfGroupsIdsSelector(userId)(state).toArray(), groupAssignments: groupId => diff --git a/src/pages/Users/Users.js b/src/pages/Users/Users.js index 17eb8fe49..dd5d7689c 100644 --- a/src/pages/Users/Users.js +++ b/src/pages/Users/Users.js @@ -117,7 +117,7 @@ class Users extends Component { next => action => { push( changeLanguage( window.location.pathname, - action.payload.user.settings.defaultLanguage + action.payload.user.privateData.settings.defaultLanguage ) ) ); diff --git a/src/redux/modules/groups.js b/src/redux/modules/groups.js index 74172ac54..6492b1fe7 100644 --- a/src/redux/modules/groups.js +++ b/src/redux/modules/groups.js @@ -79,7 +79,7 @@ export const fetchUsersGroups = userId => meta: { userId } }); -export const fetchInstanceGroupsIfNeeded = instanceId => +export const fetchInstanceGroups = instanceId => actions.fetchMany({ endpoint: `/instances/${instanceId}/groups`, meta: { instanceId } @@ -186,14 +186,21 @@ const reducer = handleActions( // update the new hierarchy inside the local state const { payload: group } = action; if ( - group.parentGroupId === null || - !state.getIn(['resources', group.parentGroupId]) + !group.privateData || + group.privateData.parentGroupId === null || + !state.getIn(['resources', group.privateData.parentGroupId]) ) { return state; } return state.updateIn( - ['resources', group.parentGroupId, 'data', 'childGroups', 'all'], + [ + 'resources', + group.privateData.parentGroupId, + 'data', + 'childGroups', + 'all' + ], children => children.push(group.id) ); }, @@ -231,8 +238,9 @@ const reducer = handleActions( { payload, meta: { groupId, userId } } ) => state.hasIn(['resources', groupId, 'data']) - ? state.updateIn(['resources', groupId, 'data', 'students'], students => - students.push(userId) + ? state.updateIn( + ['resources', groupId, 'data', 'privateData', 'students'], + students => students.push(userId) ) : state, @@ -241,8 +249,9 @@ const reducer = handleActions( { payload, meta: { groupId, userId } } ) => state.hasIn(['resources', groupId, 'data']) - ? state.updateIn(['resources', groupId, 'data', 'students'], students => - students.filter(id => id !== userId) + ? state.updateIn( + ['resources', groupId, 'data', 'privateData', 'students'], + students => students.filter(id => id !== userId) ) : state, @@ -250,8 +259,9 @@ const reducer = handleActions( state, { payload, meta: { groupId, userId } } ) => - state.updateIn(['resources', groupId, 'data', 'students'], students => - students.filter(id => id !== userId) + state.updateIn( + ['resources', groupId, 'data', 'privateData', 'students'], + students => students.filter(id => id !== userId) ), [additionalActionTypes.MAKE_SUPERVISOR_FULFILLED]: ( @@ -259,7 +269,7 @@ const reducer = handleActions( { payload, meta: { groupId, userId } } ) => state.updateIn( - ['resources', groupId, 'data', 'supervisors'], + ['resources', groupId, 'data', 'privateData', 'supervisors'], supervisors => supervisors.push( fromJS(payload.supervisors.find(id => id === userId)) @@ -271,7 +281,7 @@ const reducer = handleActions( { payload, meta: { groupId, userId } } ) => state.updateIn( - ['resources', groupId, 'data', 'supervisors'], + ['resources', groupId, 'data', 'privateData', 'supervisors'], supervisors => supervisors.filter(id => id !== userId) ), @@ -299,7 +309,7 @@ const reducer = handleActions( ) => state.updateIn(['resources', groupId, 'data'], group => group - .set('admins', List(admins)) + .setIn(['privateData', 'admins'], List(admins)) .set('primaryAdminsIds', primaryAdminsIds) ), @@ -327,7 +337,7 @@ const reducer = handleActions( ) => state.updateIn(['resources', groupId, 'data'], group => group - .set('admins', List(admins)) + .setIn(['privateData', 'admins'], List(admins)) .set('primaryAdminsIds', primaryAdminsIds) ), @@ -347,7 +357,7 @@ const reducer = handleActions( { payload: { id: assignmentId, isPublic, groupId } } ) => state.updateIn( - ['resources', groupId, 'data', 'assignments', 'public'], + ['resources', groupId, 'data', 'privateData', 'assignments', 'public'], assignments => { if (isPublic) { return assignments.push(assignmentId).toSet().toList(); @@ -365,7 +375,7 @@ const reducer = handleActions( { payload: { id: assignmentId }, meta: { body: { groupId } } } ) => state.updateIn( - ['resources', groupId, 'data', 'assignments', 'all'], + ['resources', groupId, 'data', 'privateData', 'assignments', 'all'], assignments => { if (!assignments) { assignments = List(); @@ -380,7 +390,7 @@ const reducer = handleActions( ) => state.update('resources', groups => groups.map(group => - group.updateIn(['data', 'assignments'], assignments => + group.updateIn(['data', 'privateData', 'assignments'], assignments => assignments .update('all', ids => ids.filter(id => id !== assignmentId)) .update('public', ids => ids.filter(id => id !== assignmentId)) diff --git a/src/redux/modules/publicGroups.js b/src/redux/modules/publicGroups.js deleted file mode 100644 index 843ebee92..000000000 --- a/src/redux/modules/publicGroups.js +++ /dev/null @@ -1,32 +0,0 @@ -import { handleActions } from 'redux-actions'; -import factory, { initialState } from '../helpers/resourceManager'; - -const resourceName = 'publicGroups'; -const { actions, reduceActions } = factory({ - resourceName, - apiEndpointFactory: groupId => `/groups/${groupId}/public` -}); - -/** - * Actions - */ - -export const fetchPublicGroupsIfNeeded = actions.fetchIfNeeded; -export const fetchPublicGroupIfNeeded = actions.fetchOneIfNeeded; - -export const fetchInstancePublicGroups = instanceId => - actions.fetchMany({ - endpoint: `/instances/${instanceId}/groups/public`, - meta: { instanceId } - }); - -/** - * Reducer - */ - -const reducer = handleActions( - Object.assign({}, reduceActions, {}), - initialState -); - -export default reducer; diff --git a/src/redux/modules/publicProfiles.js b/src/redux/modules/publicProfiles.js deleted file mode 100644 index eb2911470..000000000 --- a/src/redux/modules/publicProfiles.js +++ /dev/null @@ -1,57 +0,0 @@ -import { handleActions } from 'redux-actions'; -import factory, { - initialState, - createRecord, - resourceStatus -} from '../helpers/resourceManager'; - -import { actionTypes as userActionTypes } from './users'; - -const resourceName = 'publicProfiles'; -const { actions, reduceActions } = factory({ - resourceName, - apiEndpointFactory: userId => `/users/${userId}/public` -}); - -/** - * Actions - */ - -export const fetchProfilesIfNeeded = actions.fetchIfNeeded; -export const fetchProfileIfNeeded = actions.fetchOneIfNeeded; - -/** - * Reducer - */ - -const reducer = handleActions( - Object.assign({}, reduceActions, { - [userActionTypes.FETCH_FULFILLED]: ( - state, - { payload: { id, fullName, avatarUrl, isVerified = false } } - ) => - state.setIn( - ['resources', id], - createRecord({ - data: { id, fullName, avatarUrl, isVerified }, - state: resourceStatus.FULFILLED - }) - ), - - [userActionTypes.FETCH_MANY_FULFILLED]: (state, { payload: users }) => - users.reduce( - (state, { id, fullName, avatarUrl, isVerified = false }) => - state.setIn( - ['resources', id], - createRecord({ - data: { id, fullName, avatarUrl, isVerified }, - state: resourceStatus.FULFILLED - }) - ), - state - ) - }), - initialState -); - -export default reducer; diff --git a/src/redux/modules/users.js b/src/redux/modules/users.js index c629a2cc5..926b079df 100644 --- a/src/redux/modules/users.js +++ b/src/redux/modules/users.js @@ -91,7 +91,7 @@ const reducer = handleActions( } return state.updateIn( - ['resources', userId, 'data', 'groups', 'studentOf'], + ['resources', userId, 'data', 'privateData', 'groups', 'studentOf'], list => list.push(groupId) ); }, @@ -105,7 +105,7 @@ const reducer = handleActions( } return state.updateIn( - ['resources', userId, 'data', 'groups', 'studentOf'], + ['resources', userId, 'data', 'privateData', 'groups', 'studentOf'], list => list.filter(id => id !== groupId) ); }, @@ -119,7 +119,7 @@ const reducer = handleActions( } return state.updateIn( - ['resources', userId, 'data', 'groups', 'studentOf'], + ['resources', userId, 'data', 'privateData', 'groups', 'studentOf'], list => list.filter(id => id !== groupId) ); }, @@ -133,7 +133,7 @@ const reducer = handleActions( } return state.updateIn( - ['resources', userId, 'data', 'groups', 'studentOf'], + ['resources', userId, 'data', 'privateData', 'groups', 'studentOf'], list => list.push(groupId) ); }, @@ -147,7 +147,7 @@ const reducer = handleActions( } return state.updateIn( - ['resources', userId, 'data', 'groups', 'supervisorOf'], + ['resources', userId, 'data', 'privateData', 'groups', 'supervisorOf'], list => list.push(groupId) ); }, @@ -161,7 +161,7 @@ const reducer = handleActions( } return state.updateIn( - ['resources', userId, 'data', 'groups', 'supervisorOf'], + ['resources', userId, 'data', 'privateData', 'groups', 'supervisorOf'], list => list.push(payload.id) ); }, @@ -175,7 +175,7 @@ const reducer = handleActions( } return state.updateIn( - ['resources', userId, 'data', 'groups', 'supervisorOf'], + ['resources', userId, 'data', 'privateData', 'groups', 'supervisorOf'], list => list.filter(id => id !== groupId) ); }, @@ -189,7 +189,7 @@ const reducer = handleActions( } return state.updateIn( - ['resources', userId, 'data', 'groups', 'supervisorOf'], + ['resources', userId, 'data', 'privateData', 'groups', 'supervisorOf'], list => list.filter(id => id !== groupId) ); }, @@ -203,7 +203,7 @@ const reducer = handleActions( } return state.updateIn( - ['resources', userId, 'data', 'groups', 'supervisorOf'], + ['resources', userId, 'data', 'privateData', 'groups', 'supervisorOf'], list => list.push(groupId) ); } diff --git a/src/redux/reducer.js b/src/redux/reducer.js index e33a10f3f..18cda1b42 100644 --- a/src/redux/reducer.js +++ b/src/redux/reducer.js @@ -18,7 +18,6 @@ import pipelines from './modules/pipelines'; import files from './modules/files'; import filesContent from './modules/filesContent'; import groups from './modules/groups'; -import publicGroups from './modules/publicGroups'; import groupResults from './modules/groupResults'; import groupExercises from './modules/groupExercises'; import instances from './modules/instances'; @@ -26,7 +25,6 @@ import licences from './modules/licences'; import limits from './modules/limits'; import simpleLimits from './modules/simpleLimits'; import notifications from './modules/notifications'; -import publicProfiles from './modules/publicProfiles'; import { default as search } from './modules/search'; // because of a named export 'search' import sidebar from './modules/sidebar'; import stats from './modules/stats'; @@ -66,7 +64,6 @@ const createRecodexReducers = token => ({ files, filesContent, groups, - publicGroups, groupResults, groupExercises, instances, @@ -82,7 +79,6 @@ const createRecodexReducers = token => ({ registration, users, userSwitching, - publicProfiles, upload, runtimeEnvironments, supplementaryFiles, diff --git a/src/redux/selectors/groups.js b/src/redux/selectors/groups.js index 650f23418..8ddd85615 100644 --- a/src/redux/selectors/groups.js +++ b/src/redux/selectors/groups.js @@ -46,7 +46,10 @@ export const studentOfSelector2 = userId => groups .filter(isReady) .map(getJsData) - .filter(group => group.students.indexOf(userId) >= 0) + .filter( + group => + group.privateData && group.privateData.students.indexOf(userId) >= 0 + ) ); export const supervisorOfSelector2 = userId => @@ -54,7 +57,11 @@ export const supervisorOfSelector2 = userId => groups .filter(isReady) .map(getJsData) - .filter(group => group.supervisors.indexOf(userId) >= 0) + .filter( + group => + group.privateData && + group.privateData.supervisors.indexOf(userId) >= 0 + ) ); export const adminOfSelector = userId => @@ -62,13 +69,19 @@ export const adminOfSelector = userId => groups .filter(isReady) .map(getJsData) - .filter(group => group.admins.indexOf(userId) >= 0) + .filter( + group => + group.privateData && group.privateData.admins.indexOf(userId) >= 0 + ) ); const usersOfGroup = (type, groupId) => createSelector( groupSelector(groupId), - group => (group && isReady(group) ? group.getIn(['data', type]) : List()) + group => + group && isReady(group) + ? group.getIn(['data', 'privateData', type]) + : List() ); export const studentsOfGroup = groupId => usersOfGroup('students', groupId); @@ -89,7 +102,10 @@ export const groupsPublicAssignmentsSelector = createSelector( [groupsSelector, getAssignments, getParam], (groups, assignments, groupId) => groups - .getIn([groupId, 'data', 'assignments', 'public'], EMPTY_LIST) + .getIn( + [groupId, 'data', 'privateData', 'assignments', 'public'], + EMPTY_LIST + ) .map(id => assignments.getIn(['resources', id])) ); @@ -97,7 +113,7 @@ export const groupsAllAssignmentsSelector = createSelector( [groupsSelector, getAssignments, getParam], (groups, assignments, groupId) => groups - .getIn([groupId, 'data', 'assignments', 'all'], EMPTY_LIST) + .getIn([groupId, 'data', 'privateData', 'assignments', 'all'], EMPTY_LIST) .map(id => assignments.getIn(['resources', id])) ); diff --git a/src/redux/selectors/publicGroups.js b/src/redux/selectors/publicGroups.js deleted file mode 100644 index e83094ebd..000000000 --- a/src/redux/selectors/publicGroups.js +++ /dev/null @@ -1,18 +0,0 @@ -import { createSelector } from 'reselect'; -import { Map } from 'immutable'; - -/** - * Select groups part of the state - */ -const EMPTY_MAP = Map(); - -export const publicGroupsSelectors = state => - state.publicGroups.get('resources'); - -export const publicGroupSelector = id => - createSelector(publicGroupsSelectors, groups => groups.get(id)); - -export const publicGroupDataAccessorSelector = createSelector( - publicGroupsSelectors, - groups => groupId => groups.getIn([groupId, 'data'], EMPTY_MAP) -); diff --git a/src/redux/selectors/publicProfiles.js b/src/redux/selectors/publicProfiles.js deleted file mode 100644 index b2bb9c9ac..000000000 --- a/src/redux/selectors/publicProfiles.js +++ /dev/null @@ -1,11 +0,0 @@ -import { createSelector } from 'reselect'; - -const getProfiles = state => state.publicProfiles.get('resources'); - -/** - * Select users part of the state - */ -export const profilesSelector = getProfiles; - -export const getProfile = userId => - createSelector(profilesSelector, users => users.get(userId)); diff --git a/src/redux/selectors/users.js b/src/redux/selectors/users.js index 11eb18ba4..3dc8aafab 100644 --- a/src/redux/selectors/users.js +++ b/src/redux/selectors/users.js @@ -54,7 +54,7 @@ export const supervisorsOfGroupSelector = createSelector( (users, groups, groupId) => users.filter(user => groups - .getIn([groupId, 'data', 'supervisors'], EMPTY_LIST) + .getIn([groupId, 'data', 'privateData', 'supervisors'], EMPTY_LIST) .toJS() .includes(user.id) ) @@ -65,7 +65,7 @@ export const studentsOfGroupSelector = createSelector( (users, groups, groupId) => users.filter(user => groups - .getIn([groupId, 'data', 'students'], EMPTY_LIST) + .getIn([groupId, 'data', 'privateData', 'students'], EMPTY_LIST) .toJS() .includes(user.id) ) @@ -80,7 +80,7 @@ export const isVerified = userId => export const getRole = userId => createSelector( getUser(userId), - user => (user ? user.getIn(['data', 'role']) : null) + user => (user ? user.getIn(['data', 'privateData', 'role']) : null) ); export const isStudent = userId => @@ -92,7 +92,10 @@ export const isSupervisor = userId => export const getUserSettings = userId => createSelector( getUser(userId), - user => (isReady(user) ? user.getIn(['data', 'settings']).toJS() : {}) + user => + isReady(user) + ? user.getIn(['data', 'privateData', 'settings']).toJS() + : {} ); export const loggedInUserSelector = createSelector( @@ -104,7 +107,7 @@ export const isLoggedAsSuperAdmin = createSelector( [loggedInUserSelector], loggedInUser => loggedInUser && isReady(loggedInUser) - ? loggedInUser.getIn(['data', 'role']) === 'superadmin' + ? loggedInUser.getIn(['data', 'privateData', 'role']) === 'superadmin' : false ); @@ -113,7 +116,7 @@ export const memberOfInstancesIdsSelector = userId => getUser(userId), user => user && isReady(user) - ? List([user.getIn(['data', 'instanceId'])]) + ? List([user.getIn(['data', 'privateData', 'instanceId'])]) : List() // @todo: Change when the user can be member of multiple instances ); @@ -122,7 +125,7 @@ export const studentOfGroupsIdsSelector = userId => getUser(userId), user => user && isReady(user) - ? user.getIn(['data', 'groups', 'studentOf']) + ? user.getIn(['data', 'privateData', 'groups', 'studentOf'], List()) : List() ); @@ -131,7 +134,7 @@ export const supervisorOfGroupsIdsSelector = userId => getUser(userId), user => user && isReady(user) - ? user.getIn(['data', 'groups', 'supervisorOf']) + ? user.getIn(['data', 'privateData', 'groups', 'supervisorOf'], List()) : List() ); @@ -156,7 +159,7 @@ export const isAdminOf = (userId, groupId) => isSuperAdmin === true || (group && isReady(group) && - group.getIn(['data', 'admins']).indexOf(userId) >= 0) + group.getIn(['data', 'privateData', 'admins']).indexOf(userId) >= 0) ); export const isMemberOf = (userId, groupId) => diff --git a/src/redux/selectors/usersGroups.js b/src/redux/selectors/usersGroups.js index 47cbc747e..26750ef27 100644 --- a/src/redux/selectors/usersGroups.js +++ b/src/redux/selectors/usersGroups.js @@ -25,14 +25,16 @@ export default createSelector( export const loggedInStudentOfGroupsIdsSelector = createSelector( loggedInUserSelector, user => - user && isReady(user) ? user.getIn(['data', 'groups', 'studentOf']) : List() + user && isReady(user) + ? user.getIn(['data', 'privateData', 'groups', 'studentOf']) + : List() ); export const loggedInSupervisorOfGroupsIdsSelector = createSelector( loggedInUserSelector, user => user && isReady(user) - ? user.getIn(['data', 'groups', 'supervisorOf']) + ? user.getIn(['data', 'privateData', 'groups', 'supervisorOf']) : List() ); diff --git a/test/redux/middleware/authMiddleware-test.js b/test/redux/middleware/authMiddleware-test.js index f727fe3b5..90b715315 100644 --- a/test/redux/middleware/authMiddleware-test.js +++ b/test/redux/middleware/authMiddleware-test.js @@ -57,8 +57,10 @@ describe('Middleware for access token storage and injecting to HTTP requests', ( payload: { accessToken: 'abcdefgh', user: { - settings: { - defaultLanguage: 'xy' + privateData: { + settings: { + defaultLanguage: 'xy' + } } } }