Skip to content

Commit 193a054

Browse files
authored
Merge pull request #4186 from appirio-tech/dev
Connect 2.14.0
2 parents 5e55b9f + ca74afe commit 193a054

File tree

92 files changed

+2062
-1589
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

92 files changed

+2062
-1589
lines changed

.circleci/config.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ workflows:
138138
- build-dev
139139
filters:
140140
branches:
141-
only: ['feature/faqs', 'feature/project_september']
141+
only: ['feature/unified-permissions']
142142

143143
- deployProd:
144144
context : org-global

docs/permissions-guide/permissions-guide.md

+8-5
Original file line numberDiff line numberDiff line change
@@ -18,20 +18,23 @@ Let's say you would like to add a new place in code where you want to check user
1818

1919
2. After you add a new permission, regenerate [permissions list](https://htmlpreview.github.io/?https://github.com/appirio-tech/connect-app/blob/dev/docs/permissions.html) by running `npm run generate:doc:permissions`.
2020

21-
3. To check if user has permission in code use method `hasPermission(permission)`.
21+
3. To check if logged-in user has permission in code use method `hasPermission(permission)`.
2222

2323
Example:<br>
24-
24+
2525
```js
26-
import PERMISSIONS from 'config/permissions'
26+
import { PERMISSIONS } from 'config/permissions'
2727
import { hasPermission } from 'helpers/permissions'
2828

2929
if (hasPermission(PERMISSIONS.MANAGE_PROJECT_PLAN)) {
3030
...
3131
}
3232
```
3333

34-
- Note, optionally, you may pass the `project` object like this `hasPermission(permission, project)`. But you don't have to as `hasPermission` gets `project` object from the Redux Store (`projectState.project`) automatically. Only in case if you want to check user permission to another project which is not loaded into the Redux Store then you may pass `project` explicitly.
34+
4. If you would like to check permissions for other user (not the current user) or for other project (not the current project) you may pass the second argument `entities: { project?: object, user?: object }`:
35+
- `hasPermission(permission, { project })` - check permissions for another project
36+
- `hasPermission(permission, { user })` - check permissions for another user
37+
- `hasPermission(permission, { project, user })` - check permissions for another project and user
3538

3639
## Roles
3740

@@ -49,4 +52,4 @@ By default every user has one role `Topcoder User`, generally this means that su
4952

5053
When user joins some project and become a member of the project, such a user has one **Project Role** inside that project. One user may have different **Project Role** in different projects. See [the list of all Project Roles](https://github.com/appirio-tech/connect-app/blob/dev/src/config/constants.js#L638-L647) which we use in Connect App.
5154

52-
<img src="./images/project-roles.png" width="720">
55+
<img src="./images/project-roles.png" width="720">

docs/permissions.html

+983-226
Large diffs are not rendered by default.

scripts/permissions-doc/index.js

+13-7
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import _ from 'lodash'
1111
import fs from 'fs'
1212
import path from 'path'
1313
import handlebars from 'handlebars'
14-
import PERMISSIONS from '../../src/config/permissions'
14+
import { PERMISSIONS } from '../../src/config/permissions'
1515
import {
1616
PROJECT_ROLE_CUSTOMER,
1717
PROJECT_ROLE_COPILOT,
@@ -42,6 +42,8 @@ import {
4242
const docTemplatePath = path.resolve(__dirname, './template.hbs')
4343
const outputDocPath = path.resolve(__dirname, '../../docs/permissions.html')
4444

45+
handlebars.registerHelper('istrue', value => value === true)
46+
4547
/**
4648
* All Project Roles
4749
*/
@@ -121,8 +123,8 @@ function normalizePermission(permission) {
121123

122124
if (!normalizedPermission.allowRule) {
123125
normalizedPermission = {
124-
_meta: permission._meta,
125-
allowRule: _.omit(permission, '_meta')
126+
meta: permission.meta,
127+
allowRule: _.omit(permission, 'meta')
126128
}
127129
}
128130

@@ -143,13 +145,17 @@ const renderDocument = handlebars.compile(templateStr)
143145
const permissionKeys = _.keys(PERMISSIONS)
144146
const permissions = permissionKeys.map((key) => ({
145147
...PERMISSIONS[key],
146-
_meta: {
147-
...PERMISSIONS[key]._meta,
148+
meta: {
149+
...PERMISSIONS[key].meta,
148150
key,
149151
}
150152
}))
151-
const groupsObj = _.groupBy(permissions, '_meta.group')
152-
const groups = _.toPairs(groupsObj).map(([title, permissions]) => ({ title, permissions }))
153+
const groupsObj = _.groupBy(permissions, 'meta.group')
154+
const groups = _.toPairs(groupsObj).map(([title, permissions]) => ({
155+
title,
156+
permissions,
157+
anchor: `section-${title.toLowerCase().replace(/\s+/g, '-')}`,
158+
}))
153159

154160
groups.forEach((group) => {
155161
group.permissions = group.permissions.map(normalizePermission)

scripts/permissions-doc/template.hbs

+79-19
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,53 @@
66
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous">
77
<title>Permissions</title>
88
<style>
9+
body {
10+
padding-bottom: 100px;
11+
}
12+
913
.small-text {
1014
font-size: 62.5%;
1115
line-height: 120%;
1216
}
17+
18+
.permission-variable {
19+
line-height: 80%;
20+
margin: 5px 0;
21+
word-break: break-word;
22+
}
23+
24+
.permission-title {
25+
line-height: 120%;
26+
margin: 5px 0;
27+
}
28+
29+
.anchor-container .anchor {
30+
float: left;
31+
opacity: 1;
32+
line-height: 1;
33+
margin-left: -20px;
34+
}
35+
36+
.anchor-container .anchor::before {
37+
visibility: hidden;
38+
font-size: 16px;
39+
content: '🔗';
40+
display: inline-block;
41+
width: 20px;
42+
}
43+
44+
.anchor-container:hover .anchor {
45+
text-decoration: none;
46+
}
47+
48+
.anchor-container:hover .anchor::before {
49+
visibility: visible;
50+
}
51+
52+
.badge-crossed {
53+
opacity: 0.4;
54+
text-decoration: line-through;
55+
}
1356
</style>
1457
</head>
1558
<body>
@@ -21,42 +64,59 @@
2164
<p>Legend:</p>
2265
<ul>
2366
<li><span class="badge badge-primary">allowed Project Role</span> - users with such a <strong>Project Role</strong> are allowed to perform the action</li>
24-
<li><span class="badge badge-warning">denied Project Role</span> - users with such a <strong>Project Role</strong> are denied to perform the action even they have some other allow roles</li>
67+
<li><span class="badge badge-primary badge-crossed">denied Project Role</span> - users with such a <strong>Project Role</strong> are denied to perform the action even they have some other allow roles</li>
2568
<li><span class="badge badge-success">allowed Topcoder Role</span> - users with such a <strong>Topcoder Role</strong> are allowed to perform the action</li>
26-
<li><span class="badge badge-danger">denied Topcoder Role</span> - users with such a <strong>Topcoder Role</strong> are denied to perform the action even they have some other allow roles</li>
69+
<li><span class="badge badge-success badge-crossed">denied Topcoder Role</span> - users with such a <strong>Topcoder Role</strong> are denied to perform the action even they have some other allow roles</li>
2770
</ul>
2871
</div>
2972

3073
{{#each groups}}
3174
<div class="row">
3275
<div class="col pt-5 pb-2">
33-
<h2>{{title}}</h2>
76+
<h2 class="anchor-container">
77+
<a href="#{{anchor}}" name="{{anchor}}" class="anchor"></a>{{title}}
78+
</h2>
3479
</div>
3580
</div>
3681
{{#each permissions}}
3782
<div class="row border-top">
3883
<div class="col py-2">
39-
{{_meta.title}}
40-
<div><small><code>{{_meta.key}}</code></small></div>
41-
<div class="text-black-50 small-text">{{_meta.description}}</div>
84+
<div class="permission-title anchor-container">
85+
<a href="#{{meta.key}}" name="{{meta.key}}" class="anchor"></a>{{meta.title}}
86+
</div>
87+
<div class="permission-variable"><small><code>{{meta.key}}</code></small></div>
88+
<div class="text-black-50 small-text">{{meta.description}}</div>
4289
</div>
4390
<div class="col-9 py-2">
44-
{{#each allowRule.projectRoles}}
45-
<span class="badge badge-primary" title="Allowed Project Role">{{this}}</span>
46-
{{/each}}
47-
{{#each denyRule.projectRoles}}
48-
<span class="badge badge-warning" title="Denied Project Role">{{this}}</span>
49-
{{/each}}
50-
{{#each allowRule.topcoderRoles}}
51-
<span class="badge badge-success" title="Allowed Topcoder Role">{{this}}</span>
52-
{{/each}}
53-
{{#each denyRule.topcoderRoles}}
54-
<span class="badge badge-danger" title="Denied Topcoder Role">{{this}}</span>
55-
{{/each}}
91+
<div>
92+
{{#if (istrue allowRule.projectRoles)}}
93+
<span class="badge badge-primary" title="Allowed">Any Project Member</span>
94+
{{else}}
95+
{{#each allowRule.projectRoles}}
96+
<span class="badge badge-primary" title="Allowed Project Role">{{this}}</span>
97+
{{/each}}
98+
{{/if}}
99+
{{#each denyRule.projectRoles}}
100+
<span class="badge badge-primary badge-crossed" title="Denied Project Role">{{this}}</span>
101+
{{/each}}
102+
</div>
103+
104+
<div>
105+
{{#if (istrue allowRule.topcoderRoles)}}
106+
<span class="badge badge-success" title="Allowed">Any Logged-in User</span>
107+
{{else}}
108+
{{#each allowRule.topcoderRoles}}
109+
<span class="badge badge-success" title="Allowed Topcoder Role">{{this}}</span>
110+
{{/each}}
111+
{{/if}}
112+
{{#each denyRule.topcoderRoles}}
113+
<span class="badge badge-success badge-crossed" title="Denied Topcoder Role">{{this}}</span>
114+
{{/each}}
115+
</div>
56116
</div>
57117
</div>
58118
{{/each}}
59119
{{/each}}
60120
</div>
61121
</body>
62-
</html>
122+
</html>

src/components/AssetsLibrary/FilesGridView.jsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ import {
2525
PROJECT_FEED_TYPE_MESSAGES
2626
} from '../../config/constants'
2727
import { hasPermission } from '../../helpers/permissions'
28-
import PERMISSIONS from '../../config/permissions'
28+
import { PERMISSIONS } from '../../config/permissions'
2929

3030
let selectedLink
3131
let clearing = false

src/components/AssetsLibrary/LinksGridView.jsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ import {
2222
} from '../../config/constants'
2323
import FilterColHeader from './FilterColHeader'
2424
import { hasPermission } from '../../helpers/permissions'
25-
import PERMISSIONS from '../../config/permissions'
25+
import { PERMISSIONS } from '../../config/permissions'
2626

2727
let selectedLink
2828
let clearing = false

src/components/Feed/Feed.jsx

+4-2
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import RichTextArea from '../RichTextArea/RichTextArea'
1010
import NotificationsReader from '../../components/NotificationsReader'
1111
import IconButton from '../IconButton/IconButton'
1212

13-
import { EVENT_TYPE, PROJECT_FEED_TYPE_MESSAGES, PROJECT_ROLE_CUSTOMER } from '../../config/constants'
13+
import { EVENT_TYPE, PROJECT_FEED_TYPE_MESSAGES } from '../../config/constants'
1414

1515
import XMarkIcon from '../../assets/icons/x-mark.svg'
1616
import FullscreenIcon from '../../assets/icons/ui-fullscreen.svg'
@@ -20,6 +20,8 @@ import CloseIcon from 'appirio-tech-react-components/components/Icons/CloseIcon'
2020
import MoreIcon from '../../assets/icons/more.svg'
2121

2222
import './Feed.scss'
23+
import { hasPermission } from '../../helpers/permissions'
24+
import { PERMISSIONS } from '../../config/permissions'
2325

2426
class Feed extends React.Component {
2527
constructor(props) {
@@ -95,7 +97,7 @@ class Feed extends React.Component {
9597
}
9698

9799
filterProjectMembers(projectMembers, isPrivate) {
98-
return isPrivate ? _.pickBy(projectMembers, pm => pm.role !== PROJECT_ROLE_CUSTOMER) : projectMembers
100+
return isPrivate ? _.filter(projectMembers, member => hasPermission(PERMISSIONS.ACCESS_PRIVATE_POST, { user: member })) : projectMembers
99101
}
100102

101103
render() {

src/components/IncomepleteUserProfileDialog/IncompleteUserProfileDialog.jsx

-1
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,6 @@ const IncompleteUserProfileDialog = ({
4545
IncompleteUserProfileDialog.propTypes = {
4646
profileSettings: PT.object.isRequired,
4747
saveProfileSettings: PT.func.isRequired,
48-
isTopcoderUser: PT.bool.isRequired,
4948
user: PT.object.isRequired,
5049
onCloseDialog: PT.func.isRequired,
5150
title: PT.string.isRequired,

src/components/IncompleteUserProfile/IncompleteUserProfile.jsx

+5-6
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,19 @@
33
*/
44
import React from 'react'
55
import PT from 'prop-types'
6-
import { PROFILE_FIELDS_CONFIG } from '../../config/constants'
76
import ProfileSettingsForm from '../../routes/settings/routes/profile/components/ProfileSettingsForm'
8-
import { getDefaultTopcoderRole } from '../../helpers/permissions'
7+
import { getDefaultTopcoderRole, hasPermission } from '../../helpers/permissions'
98
import { timezones } from 'appirio-tech-react-components/constants/timezones'
9+
import { getUserProfileFieldsConfig } from '../../helpers/tcHelpers'
10+
import { PERMISSIONS } from '../../config/permissions'
1011

1112
const IncompleteUserProfile = ({
1213
profileSettings,
1314
saveProfileSettings,
14-
isTopcoderUser,
1515
user,
1616
...restProps
1717
}) => {
18-
const fieldsConfig = isTopcoderUser ? PROFILE_FIELDS_CONFIG.TOPCODER : PROFILE_FIELDS_CONFIG.CUSTOMER
18+
const fieldsConfig = getUserProfileFieldsConfig()
1919
// never show avatar
2020
delete fieldsConfig.avatar
2121
// config the form to only show required fields which doesn't have the value yet
@@ -40,7 +40,7 @@ const IncompleteUserProfile = ({
4040
console.log('Auto-detected timezone', prefilledProfileSettings.settings.timeZone)
4141
}
4242

43-
if (isTopcoderUser) {
43+
if (!hasPermission(PERMISSIONS.VIEW_USER_PROFILE_AS_CUSTOMER)) {
4444
// We don't ask Topcoder User for "Company Name" and "Title"
4545
// but server requires them, so if they are not yet defined, we set them automatically
4646
if (!profileSettings.settings.companyName) {
@@ -71,7 +71,6 @@ const IncompleteUserProfile = ({
7171
IncompleteUserProfile.propTypes = {
7272
profileSettings: PT.object.isRequired,
7373
saveProfileSettings: PT.func.isRequired,
74-
isTopcoderUser: PT.bool.isRequired,
7574
user: PT.object.isRequired,
7675
}
7776

src/components/Posts/PostsContainer.jsx

+3-3
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ class PostsContainer extends React.Component {
6060
* which is accepted by Feed component
6161
*/
6262
prepareFeed() {
63-
const { topic, error, allMembers, currentMemberRole, tag } = this.props
63+
const { topic, error, allMembers, tag } = this.props
6464
const { showAll } = this.state
6565

6666
if (!topic || !tag) {
@@ -73,7 +73,7 @@ class PostsContainer extends React.Component {
7373
// Github issue##623, allow comments on all posts (including system posts)
7474
allowComments: true,
7575
user: isSystemUser(topic.userId) ? SYSTEM_USER : allMembers[topic.userId],
76-
unread: !topic.read && !!currentMemberRole,
76+
unread: !topic.read,
7777
totalComments: topic.totalPosts,
7878
comments: [],
7979
}
@@ -88,7 +88,7 @@ class PostsContainer extends React.Component {
8888
isSavingComment: p.isSavingComment,
8989
isDeletingComment: p.isDeletingComment,
9090
error: p.error,
91-
unread: !p.read && !!currentMemberRole,
91+
unread: !p.read,
9292
date,
9393
createdAt: p.date,
9494
edited,

0 commit comments

Comments
 (0)