Skip to content

Commit 75c4a63

Browse files
committed
OBSERVER SHOULD BE ABLE TO VIEW CHALLENGES IN A PROJECT
1 parent 4921b33 commit 75c4a63

File tree

7 files changed

+65
-27
lines changed

7 files changed

+65
-27
lines changed

src/components/ChallengeEditor/ChallengeViewTabs/index.js

+4-3
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import LegacyLinks from '../../LegacyLinks'
1313
import ForumLink from '../../ForumLink'
1414
import Registrants from '../Registrants'
1515
import Submissions from '../Submissions'
16-
import { checkAdmin, getResourceRoleByName } from '../../../util/tc'
16+
import { checkAdmin, checkReadOnlyRoles, getResourceRoleByName } from '../../../util/tc'
1717
import { CHALLENGE_STATUS, MESSAGE } from '../../../config/constants'
1818
import Tooltip from '../../Tooltip'
1919
import CancelDropDown from '../Cancel-Dropdown'
@@ -88,14 +88,15 @@ const ChallengeViewTabs = ({
8888
const isDraft = challenge.status.toUpperCase() === CHALLENGE_STATUS.DRAFT
8989
const isSelfServiceCopilot = challenge.legacy.selfServiceCopilot === loggedInUser.handle
9090
const isAdmin = checkAdmin(token)
91+
const isReadOnly = checkReadOnlyRoles(token)
9192
const canApprove = (isSelfServiceCopilot || enableEdit) && isDraft && isSelfService
9293
const hasBillingAccount = _.get(projectDetail, 'billingAccountId') !== null
9394
// only challenges that have a billing account can be launched AND
9495
// if this isn't self-service, permit launching if the challenge is draft
9596
// OR if this isn't a non-self-service draft, permit launching if:
9697
// a) the current user is either the self-service copilot or is an admin AND
9798
// b) the challenge is approved
98-
const canLaunch = hasBillingAccount &&
99+
const canLaunch = hasBillingAccount && !isReadOnly &&
99100
((!isSelfService && isDraft) ||
100101
((isSelfServiceCopilot || enableEdit || isAdmin) &&
101102
challenge.status.toUpperCase() === CHALLENGE_STATUS.APPROVED))
@@ -127,7 +128,7 @@ const ChallengeViewTabs = ({
127128
styles.actionButtonsRight
128129
)}
129130
>
130-
{(isDraft || challenge.status === 'New') && !isSelfService &&
131+
{(isDraft || challenge.status === 'New') && !isReadOnly && !isSelfService &&
131132
(<div className={styles['cancel-button']}><CancelDropDown challenge={challenge} onSelectMenu={cancelChallenge} /></div>)}
132133
{canLaunch && (
133134
<div className={styles.button}>

src/components/ChallengesComponent/ChallengeCard/index.js

+11-5
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import styles from './ChallengeCard.module.scss'
1414
import { formatDate } from '../../../util/date'
1515
import { CHALLENGE_STATUS, COMMUNITY_APP_URL, DIRECT_PROJECT_URL, MESSAGE, ONLINE_REVIEW_URL } from '../../../config/constants'
1616
import ConfirmationModal from '../../Modal/ConfirmationModal'
17-
import { checkChallengeEditPermission } from '../../../util/tc'
17+
import { checkChallengeEditPermission, checkReadOnlyRoles } from '../../../util/tc'
1818
import AlertModal from '../../Modal/AlertModal'
1919
import Tooltip from '../../Tooltip'
2020

@@ -208,6 +208,7 @@ class ChallengeCard extends React.Component {
208208
: `Do you want to delete "${challenge.name}"?`
209209
const orUrl = `${ONLINE_REVIEW_URL}/review/actions/ViewProjectDetails?pid=${challenge.legacyId}`
210210
const communityAppUrl = `${COMMUNITY_APP_URL}/challenges/${challenge.id}`
211+
const isReadOnly = checkReadOnlyRoles(this.props.auth.token)
211212

212213
return (
213214
<div className={styles.item}>
@@ -277,9 +278,13 @@ class ChallengeCard extends React.Component {
277278
<div className={styles.col3}>
278279
{renderStatus(challenge.status.toUpperCase(), getStatusText)}
279280
</div>
280-
<div className={styles.col6}>
281-
{(disableHover ? <Link className={styles.link} to={`/projects/${challenge.projectId}/challenges/${challenge.id}/edit`}>Edit</Link> : hoverComponents(challenge, this.onUpdateLaunch, this.deleteModalLaunch))}
282-
</div>
281+
{
282+
!isReadOnly && (
283+
<div className={styles.col6}>
284+
{(disableHover ? <Link className={styles.link} to={`/projects/${challenge.projectId}/challenges/${challenge.id}/edit`}>Edit</Link> : hoverComponents(challenge, this.onUpdateLaunch, this.deleteModalLaunch))}
285+
</div>
286+
)
287+
}
283288
<div className={styles.col6}>
284289
<a className={styles.link} href={orUrl} target='_blank'>OR</a>
285290
</div>
@@ -306,7 +311,8 @@ ChallengeCard.propTypes = {
306311
isBillingAccountExpired: PropTypes.bool,
307312
disableHover: PropTypes.bool,
308313
getStatusText: PropTypes.func,
309-
challengeTypes: PropTypes.arrayOf(PropTypes.shape())
314+
challengeTypes: PropTypes.arrayOf(PropTypes.shape()),
315+
auth: PropTypes.object.isRequired
310316
}
311317

312318
export default withRouter(ChallengeCard)

src/components/ChallengesComponent/ChallengeList/index.js

+1
Original file line numberDiff line numberDiff line change
@@ -771,6 +771,7 @@ class ChallengeList extends Component {
771771
disableHover
772772
getStatusText={this.getStatusTextFunc(selfService)}
773773
challengeTypes={challengeTypes}
774+
auth={this.props.auth}
774775
/>
775776
</li>
776777
)

src/components/ChallengesComponent/index.js

+4-1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { PrimaryButton } from '../Buttons'
1010
import ChallengeList from './ChallengeList'
1111
import styles from './ChallengesComponent.module.scss'
1212
import xss from 'xss'
13+
import { checkReadOnlyRoles } from '../../util/tc'
1314

1415
const ChallengesComponent = ({
1516
challenges,
@@ -42,6 +43,8 @@ const ChallengesComponent = ({
4243
auth,
4344
challengeTypes
4445
}) => {
46+
const isReadOnly = checkReadOnlyRoles(auth.token)
47+
4548
return (
4649
<div>
4750
<Helmet title={activeProject ? activeProject.name : ''} />
@@ -67,7 +70,7 @@ const ChallengesComponent = ({
6770
</span>
6871
)}
6972
</div>
70-
{activeProject && activeProject.id ? (
73+
{activeProject && activeProject.id && !isReadOnly ? (
7174
<Link
7275
className={styles.buttonLaunchNew}
7376
to={`/projects/${activeProject.id}/challenges/new`}

src/config/constants.js

+6-1
Original file line numberDiff line numberDiff line change
@@ -221,7 +221,12 @@ export const ALLOWED_USER_ROLES = [
221221
'administrator',
222222
'connect admin',
223223
'connect manager',
224-
'connect copilot'
224+
'connect copilot',
225+
'topcoder user'
226+
]
227+
228+
export const READ_ONLY_ROLES = [
229+
'topcoder user'
225230
]
226231

227232
export const ADMIN_ROLES = [

src/routes.js

+28-16
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import { getFreshToken, decodeToken } from 'tc-auth-lib'
1515
import { saveToken } from './actions/auth'
1616
import { loadChallengeDetails } from './actions/challenges'
1717
import { connect } from 'react-redux'
18-
import { checkAllowedRoles } from './util/tc'
18+
import { checkAllowedRoles, checkReadOnlyRoles } from './util/tc'
1919
import { setCookie, removeCookie, isBetaMode } from './util/cookie'
2020
import IdleTimer from 'react-idle-timer'
2121
import modalStyles from './styles/modal.module.scss'
@@ -36,9 +36,11 @@ class RedirectToChallenge extends React.Component {
3636
}
3737

3838
componentWillReceiveProps (nextProps) {
39+
const { token } = nextProps
40+
const isReadOnly = checkReadOnlyRoles(token)
3941
const projectId = _.get(nextProps.challengeDetails, 'projectId')
4042
const challengeId = _.get(nextProps.challengeDetails, 'id')
41-
if (projectId && challengeId) {
43+
if (projectId && challengeId && isReadOnly) {
4244
console.log('Redircting to full URL')
4345
this.props.history.replace(`/projects/${projectId}/challenges/${challengeId}/view`)
4446
}
@@ -61,7 +63,8 @@ RedirectToChallenge.propTypes = {
6163
loadChallengeDetails: PropTypes.func,
6264
challengeDetails: PropTypes.object,
6365
match: PropTypes.object,
64-
history: PropTypes.object
66+
history: PropTypes.object,
67+
token: PropTypes.string
6568
}
6669

6770
const ConnectRedirectToChallenge = connect(mapStateToProps, mapDispatchToProps)(RedirectToChallenge)
@@ -128,6 +131,7 @@ class Routes extends React.Component {
128131
}
129132

130133
const isAllowed = checkAllowedRoles(_.get(decodeToken(this.props.token), 'roles'))
134+
const isReadOnly = checkReadOnlyRoles(this.props.token)
131135
const modal = (<ConfirmationModal
132136
theme={theme}
133137
title='Session Timeout'
@@ -176,26 +180,34 @@ class Routes extends React.Component {
176180
<Tab />
177181
)()}
178182
/>
179-
<Route exact path='/users'
180-
render={() => renderApp(
181-
<Users />,
182-
<TopBarContainer />,
183-
<Tab />
184-
)()}
185-
/>
183+
{
184+
!isReadOnly && (
185+
<Route exact path='/users'
186+
render={() => renderApp(
187+
<Users />,
188+
<TopBarContainer />,
189+
<Tab />
190+
)()}
191+
/>
192+
)
193+
}
186194
<Route exact path='/self-service'
187195
render={() => renderApp(
188196
<Challenges selfService />,
189197
<TopBarContainer />,
190198
<Tab selfService />
191199
)()}
192200
/>
193-
<Route exact path='/projects/:projectId/challenges/new'
194-
render={({ match }) => renderApp(
195-
<ChallengeEditor />,
196-
<TopBarContainer />,
197-
<Tab projectId={match.params.projectId} menu={'New Challenge'} />
198-
)()} />
201+
{
202+
!isReadOnly && (
203+
<Route exact path='/projects/:projectId/challenges/new'
204+
render={({ match }) => renderApp(
205+
<ChallengeEditor />,
206+
<TopBarContainer />,
207+
<Tab projectId={match.params.projectId} menu={'New Challenge'} />
208+
)()} />
209+
)
210+
}
199211
<Route exact path='/challenges/:challengeId' component={ConnectRedirectToChallenge} />
200212
<Route
201213
path='/projects/:projectId/challenges/:challengeId'

src/util/tc.js

+11-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ import {
66
CHALLENGE_TRACKS,
77
ALLOWED_USER_ROLES,
88
ADMIN_ROLES,
9-
SUBMITTER_ROLE_UUID
9+
SUBMITTER_ROLE_UUID,
10+
READ_ONLY_ROLES
1011
} from '../config/constants'
1112
import _ from 'lodash'
1213
import { decodeToken } from 'tc-auth-lib'
@@ -149,6 +150,15 @@ export const getDomainTypes = (trackId) => {
149150
export const checkAllowedRoles = roles =>
150151
roles.some(val => ALLOWED_USER_ROLES.indexOf(val.toLowerCase()) > -1)
151152

153+
/**
154+
* Checks if read only role is present in allowed roles
155+
* @param token
156+
*/
157+
export const checkReadOnlyRoles = token => {
158+
const roles = _.get(decodeToken(token), 'roles')
159+
return roles.some(val => READ_ONLY_ROLES.indexOf(val.toLowerCase()) > -1)
160+
}
161+
152162
/**
153163
* Checks if token has any of the admin roles
154164
* @param token

0 commit comments

Comments
 (0)