Browse Source

[https://github.com/tracim/tracim/issues/813] added handling of roles in frontend and apps

Skylsmoi 6 years ago
parent
commit
98c0f4d95c

+ 4 - 2
frontend/src/appFactory.js View File

4
 
4
 
5
 export function appFactory (WrappedComponent) {
5
 export function appFactory (WrappedComponent) {
6
   return class AppFactory extends React.Component {
6
   return class AppFactory extends React.Component {
7
-    renderAppFeature = (appConfig, user, content) => GLOBAL_renderAppFeature({
8
-      loggedUser: user.logged ? user : {},
7
+    renderAppFeature = (appConfig, user, idRoleUserWorkspace, content) => GLOBAL_renderAppFeature({
8
+      loggedUser: user.logged
9
+        ? {...user, idRoleUserWorkspace}
10
+        : {},
9
       config: {
11
       config: {
10
         ...appConfig,
12
         ...appConfig,
11
         domContainer: 'appFeatureContainer',
13
         domContainer: 'appFeatureContainer',

+ 40 - 28
frontend/src/component/Workspace/BtnExtandedAction.jsx View File

18
       </button>
18
       </button>
19
 
19
 
20
       <div className='extandedaction__subdropdown dropdown-menu' aria-labelledby='dropdownMenuButton'>
20
       <div className='extandedaction__subdropdown dropdown-menu' aria-labelledby='dropdownMenuButton'>
21
-        <div className='subdropdown__item dropdown-item d-flex align-items-center' onClick={props.onClickExtendedAction.edit}>
22
-          <div className='subdropdown__item__icon mr-3'>
23
-            <i className='fa fa-fw fa-pencil' />
24
-          </div>
25
-          <div className='subdropdown__item__text'>
26
-            {props.t('Edit')}
27
-          </div>
28
-        </div>
21
+        {props.idRoleUserWorkspace >= 2 &&
22
+          <div className='subdropdown__item dropdown-item d-flex align-items-center' onClick={props.onClickExtendedAction.edit}>
23
+            <div className='subdropdown__item__icon mr-3'>
24
+              <i className='fa fa-fw fa-pencil' />
25
+            </div>
29
 
26
 
30
-        <div className='subdropdown__item dropdown-item d-flex align-items-center' onClick={props.onClickExtendedAction.move}>
31
-          <div className='subdropdown__item__icon mr-3'>
32
-            <i className='fa fa-fw fa-arrows-alt' />
27
+            <div className='subdropdown__item__text'>
28
+              {props.t('Edit')}
29
+            </div>
33
           </div>
30
           </div>
34
-          <div className='subdropdown__item__text'>
35
-            {props.t('Move')}
31
+        }
32
+
33
+        {props.idRoleUserWorkspace >= 4 &&
34
+          <div className='subdropdown__item dropdown-item d-flex align-items-center' onClick={props.onClickExtendedAction.move}>
35
+            <div className='subdropdown__item__icon mr-3'>
36
+              <i className='fa fa-fw fa-arrows-alt' />
37
+            </div>
38
+
39
+            <div className='subdropdown__item__text'>
40
+              {props.t('Move')}
41
+            </div>
36
           </div>
42
           </div>
37
-        </div>
43
+        }
38
 
44
 
39
         {/* <div className='subdropdown__item dropdown-item d-flex align-items-center' onClick={props.onClickExtendedAction.download}>
45
         {/* <div className='subdropdown__item dropdown-item d-flex align-items-center' onClick={props.onClickExtendedAction.download}>
40
           <div className='subdropdown__item__icon mr-3'>
46
           <div className='subdropdown__item__icon mr-3'>
45
           </div>
51
           </div>
46
         </div> */ }
52
         </div> */ }
47
 
53
 
48
-        <div className='subdropdown__item dropdown-item d-flex align-items-center' onClick={props.onClickExtendedAction.archive}>
49
-          <div className='subdropdown__item__icon mr-3'>
50
-            <i className='fa fa-fw fa-archive' />
51
-          </div>
52
-          <div className='subdropdown__item__text'>
53
-            {props.t('Archive')}
54
-          </div>
55
-        </div>
54
+        {props.idRoleUserWorkspace >= 4 &&
55
+          <div className='subdropdown__item dropdown-item d-flex align-items-center' onClick={props.onClickExtendedAction.archive}>
56
+            <div className='subdropdown__item__icon mr-3'>
57
+              <i className='fa fa-fw fa-archive' />
58
+            </div>
56
 
59
 
57
-        <div className='subdropdown__item dropdown-item d-flex align-items-center' onClick={props.onClickExtendedAction.delete}>
58
-          <div className='subdropdown__item__icon mr-3'>
59
-            <i className='fa fa-fw fa-trash-o' />
60
+            <div className='subdropdown__item__text'>
61
+              {props.t('Archive')}
62
+            </div>
60
           </div>
63
           </div>
61
-          <div className='subdropdown__item__text'>
62
-            {props.t('Delete')}
64
+        }
65
+
66
+        {props.idRoleUserWorkspace >= 4 &&
67
+          <div className='subdropdown__item dropdown-item d-flex align-items-center' onClick={props.onClickExtendedAction.delete}>
68
+            <div className='subdropdown__item__icon mr-3'>
69
+              <i className='fa fa-fw fa-trash-o' />
70
+            </div>
71
+
72
+            <div className='subdropdown__item__text'>
73
+              {props.t('Delete')}
74
+            </div>
63
           </div>
75
           </div>
64
-        </div>
76
+        }
65
 
77
 
66
       </div>
78
       </div>
67
     </div>
79
     </div>

+ 8 - 3
frontend/src/component/Workspace/ContentItem.jsx View File

19
         </div>
19
         </div>
20
       </div>
20
       </div>
21
 
21
 
22
-      <div className='d-none d-md-flex'>
23
-        <BtnExtandedAction onClickExtendedAction={props.onClickExtendedAction} />
24
-      </div>
22
+      {props.idRoleUserWorkspace >= 2 &&
23
+        <div className='d-none d-md-flex'>
24
+          <BtnExtandedAction
25
+            idRoleUserWorkspace={props.idRoleUserWorkspace}
26
+            onClickExtendedAction={props.onClickExtendedAction}
27
+          />
28
+        </div>
29
+      }
25
 
30
 
26
       <div className={classnames('content__status')} style={{color: status.hexcolor}}>
31
       <div className={classnames('content__status')} style={{color: status.hexcolor}}>
27
         <div className='content__status__text d-none d-xl-flex align-items-center justify-content-between'>
32
         <div className='content__status__text d-none d-xl-flex align-items-center justify-content-between'>

+ 36 - 33
frontend/src/component/Workspace/Folder.jsx View File

30
       availableApp,
30
       availableApp,
31
       folderData,
31
       folderData,
32
       // onClickItem,
32
       // onClickItem,
33
+      idRoleUserWorkspace,
33
       onClickExtendedAction,
34
       onClickExtendedAction,
34
       onClickCreateContent,
35
       onClickCreateContent,
35
       // onClickFolder,
36
       // onClickFolder,
54
           </div>
55
           </div>
55
 
56
 
56
           <div className='folder__header__button'>
57
           <div className='folder__header__button'>
57
-
58
-            <div className='folder__header__button__addbtn'>
59
-              <button
60
-                className='addbtn__text btn btn-outline-primary dropdown-toggle'
61
-                type='button'
62
-                id='dropdownMenuButton'
63
-                data-toggle='dropdown'
64
-                aria-haspopup='true'
65
-                aria-expanded='false'
66
-                onClick={e => e.stopPropagation()}
67
-              >
68
-                {t('Create in folder...')}
69
-              </button>
70
-
71
-              <div className='addbtn__subdropdown dropdown-menu' aria-labelledby='dropdownMenuButton'>
72
-                <SubDropdownCreateButton
73
-                  idFolder={null}
74
-                  availableApp={availableApp}
75
-                  onClickCreateContent={onClickCreateContent}
76
-                />
77
-              </div>
78
-
79
-              <div className='d-none d-md-flex'>
80
-                <BtnExtandedAction onClickExtendedAction={{
81
-                  edit: e => onClickExtendedAction.edit(e, folderData),
82
-                  move: e => onClickExtendedAction.move(e, folderData),
83
-                  download: e => onClickExtendedAction.download(e, folderData),
84
-                  archive: e => onClickExtendedAction.archive(e, folderData),
85
-                  delete: e => onClickExtendedAction.delete(e, folderData)
86
-                }} />
58
+            {idRoleUserWorkspace >= 2 &&
59
+              <div className='folder__header__button__addbtn'>
60
+                <button
61
+                  className='addbtn__text btn btn-outline-primary dropdown-toggle'
62
+                  type='button'
63
+                  id='dropdownMenuButton'
64
+                  data-toggle='dropdown'
65
+                  aria-haspopup='true'
66
+                  aria-expanded='false'
67
+                  onClick={e => e.stopPropagation()}
68
+                >
69
+                  {t('Create in folder...')}
70
+                </button>
71
+
72
+                <div className='addbtn__subdropdown dropdown-menu' aria-labelledby='dropdownMenuButton'>
73
+                  <SubDropdownCreateButton
74
+                    idFolder={null}
75
+                    availableApp={availableApp}
76
+                    onClickCreateContent={onClickCreateContent}
77
+                  />
78
+                </div>
79
+
80
+                <div className='d-none d-md-flex'>
81
+                  <BtnExtandedAction
82
+                    idRoleUserWorkspace={idRoleUserWorkspace}
83
+                    onClickExtendedAction={{
84
+                      edit: e => onClickExtendedAction.edit(e, folderData),
85
+                      move: e => onClickExtendedAction.move(e, folderData),
86
+                      download: e => onClickExtendedAction.download(e, folderData),
87
+                      archive: e => onClickExtendedAction.archive(e, folderData),
88
+                      delete: e => onClickExtendedAction.delete(e, folderData)
89
+                    }}
90
+                  />
91
+                </div>
87
               </div>
92
               </div>
88
-
89
-            </div>
93
+            }
90
           </div>
94
           </div>
91
 
95
 
92
           <div className='folder__header__status' />
96
           <div className='folder__header__status' />
93
-
94
         </div>
97
         </div>
95
 
98
 
96
         <div className='folder__content'>
99
         <div className='folder__content'>

+ 16 - 2
frontend/src/component/Workspace/OpenContentApp.jsx View File

2
 import { connect } from 'react-redux'
2
 import { connect } from 'react-redux'
3
 import { withRouter } from 'react-router'
3
 import { withRouter } from 'react-router'
4
 import appFactory from '../../appFactory.js'
4
 import appFactory from '../../appFactory.js'
5
+import { ROLE, findIdRoleUserWorkspace } from '../../helper.js'
5
 
6
 
6
 // @FIXME Côme - 2018/07/31 - should this be in a component like AppFeatureManager ?
7
 // @FIXME Côme - 2018/07/31 - should this be in a component like AppFeatureManager ?
7
 export class OpenContentApp extends React.Component {
8
 export class OpenContentApp extends React.Component {
8
   openContentApp = () => {
9
   openContentApp = () => {
9
-    const { idWorkspace, appOpenedType, user, workspaceContentList, contentType, renderAppFeature, dispatchCustomEvent, match } = this.props
10
+    const {
11
+      idWorkspace,
12
+      appOpenedType,
13
+      user,
14
+      currentWorkspace,
15
+      workspaceContentList,
16
+      contentType,
17
+      renderAppFeature,
18
+      dispatchCustomEvent,
19
+      match
20
+    } = this.props
10
 
21
 
11
     if (isNaN(idWorkspace) || idWorkspace === -1) return
22
     if (isNaN(idWorkspace) || idWorkspace === -1) return
12
 
23
 
30
         renderAppFeature(
41
         renderAppFeature(
31
           contentType.find(ct => ct.slug === contentToOpen.type),
42
           contentType.find(ct => ct.slug === contentToOpen.type),
32
           user,
43
           user,
44
+          findIdRoleUserWorkspace(user.user_id, currentWorkspace.memberList, ROLE),
33
           contentToOpen
45
           contentToOpen
34
         )
46
         )
35
         this.props.updateAppOpenedType(contentToOpen.type)
47
         this.props.updateAppOpenedType(contentToOpen.type)
54
   }
66
   }
55
 }
67
 }
56
 
68
 
57
-const mapStateToProps = ({ user, workspaceContentList, contentType }) => ({ user, workspaceContentList, contentType })
69
+const mapStateToProps = ({ user, currentWorkspace, workspaceContentList, contentType }) => ({
70
+  user, currentWorkspace, workspaceContentList, contentType
71
+})
58
 export default withRouter(connect(mapStateToProps)(appFactory(OpenContentApp)))
72
 export default withRouter(connect(mapStateToProps)(appFactory(OpenContentApp)))

+ 34 - 18
frontend/src/container/WorkspaceContent.jsx View File

2
 import { connect } from 'react-redux'
2
 import { connect } from 'react-redux'
3
 import { withRouter, Route } from 'react-router-dom'
3
 import { withRouter, Route } from 'react-router-dom'
4
 import appFactory from '../appFactory.js'
4
 import appFactory from '../appFactory.js'
5
-import { PAGE } from '../helper.js'
5
+import { PAGE, ROLE, findIdRoleUserWorkspace } from '../helper.js'
6
 import Folder from '../component/Workspace/Folder.jsx'
6
 import Folder from '../component/Workspace/Folder.jsx'
7
 import ContentItem from '../component/Workspace/ContentItem.jsx'
7
 import ContentItem from '../component/Workspace/ContentItem.jsx'
8
 import ContentItemHeader from '../component/Workspace/ContentItemHeader.jsx'
8
 import ContentItemHeader from '../component/Workspace/ContentItemHeader.jsx'
16
 } from 'tracim_frontend_lib'
16
 } from 'tracim_frontend_lib'
17
 import {
17
 import {
18
   getWorkspaceContentList,
18
   getWorkspaceContentList,
19
+  getWorkspaceMemberList,
19
   getFolderContent,
20
   getFolderContent,
20
   putWorkspaceContentArchived,
21
   putWorkspaceContentArchived,
21
   putWorkspaceContentDeleted
22
   putWorkspaceContentDeleted
24
   newFlashMessage,
25
   newFlashMessage,
25
   setWorkspaceContentList,
26
   setWorkspaceContentList,
26
   setWorkspaceContentArchived,
27
   setWorkspaceContentArchived,
27
-  setWorkspaceContentDeleted
28
+  setWorkspaceContentDeleted,
29
+  setWorkspaceMemberList
28
 } from '../action-creator.sync.js'
30
 } from '../action-creator.sync.js'
29
 
31
 
30
 const qs = require('query-string')
32
 const qs = require('query-string')
101
   }
103
   }
102
 
104
 
103
   loadContentList = async idWorkspace => {
105
   loadContentList = async idWorkspace => {
104
-    const { user, location, dispatch } = this.props
106
+    const { user, dispatch } = this.props
105
 
107
 
106
     const wsContent = await dispatch(getWorkspaceContentList(user, idWorkspace, 0))
108
     const wsContent = await dispatch(getWorkspaceContentList(user, idWorkspace, 0))
109
+    const wsMember = await dispatch(getWorkspaceMemberList(user, idWorkspace))
107
 
110
 
108
-    if (wsContent.status === 200) dispatch(setWorkspaceContentList(wsContent.json, qs.parse(location.search).type))
111
+    if (await wsContent.status === 200) dispatch(setWorkspaceContentList(wsContent.json))
109
     else dispatch(newFlashMessage('Error while loading workspace', 'danger'))
112
     else dispatch(newFlashMessage('Error while loading workspace', 'danger'))
113
+
114
+    if (await wsMember.status === 200) dispatch(setWorkspaceMemberList(wsMember.json))
115
+    else dispatch(newFlashMessage('Error while loading members list', 'warning'))
110
   }
116
   }
111
 
117
 
112
   handleClickContentItem = content => {
118
   handleClickContentItem = content => {
171
   handleUpdateAppOpenedType = openedAppType => this.setState({appOpenedType: openedAppType})
177
   handleUpdateAppOpenedType = openedAppType => this.setState({appOpenedType: openedAppType})
172
 
178
 
173
   render () {
179
   render () {
174
-    const { workspaceContentList, contentType } = this.props
180
+    const { user, currentWorkspace, workspaceContentList, contentType } = this.props
175
 
181
 
176
     const filterWorkspaceContent = (contentList, filter) => {
182
     const filterWorkspaceContent = (contentList, filter) => {
177
       return filter.length === 0
183
       return filter.length === 0
188
       ? filterWorkspaceContent(workspaceContentList, urlFilter ? [urlFilter] : [])
194
       ? filterWorkspaceContent(workspaceContentList, urlFilter ? [urlFilter] : [])
189
       : []
195
       : []
190
 
196
 
197
+    const idRoleUserWorkspace = findIdRoleUserWorkspace(user.user_id, currentWorkspace.memberList, ROLE)
198
+
191
     return (
199
     return (
192
       <div className='WorkspaceContent' style={{width: '100%'}}>
200
       <div className='WorkspaceContent' style={{width: '100%'}}>
193
         <OpenContentApp
201
         <OpenContentApp
212
             title='Liste des Contenus'
220
             title='Liste des Contenus'
213
             subtitle={workspaceContentList.label ? workspaceContentList.label : ''}
221
             subtitle={workspaceContentList.label ? workspaceContentList.label : ''}
214
           >
222
           >
215
-            <DropdownCreateButton
216
-              parentClass='workspace__header__btnaddcontent'
217
-              idFolder={null} // null because it is workspace root content
218
-              onClickCreateContent={this.handleClickCreateContent}
219
-              availableApp={contentType.filter(ct => ct.slug !== 'comment')} // @FIXME: Côme - 2018/08/21 - should use props.appList
220
-            />
223
+            {idRoleUserWorkspace >= 2 &&
224
+              <DropdownCreateButton
225
+                parentClass='workspace__header__btnaddcontent'
226
+                idFolder={null} // null because it is workspace root content
227
+                onClickCreateContent={this.handleClickCreateContent}
228
+                availableApp={contentType.filter(ct => ct.slug !== 'comment')} // @FIXME: Côme - 2018/08/21 - should use props.appList
229
+              />
230
+            }
221
           </PageTitle>
231
           </PageTitle>
222
 
232
 
223
           <PageContent parentClass='workspace__content'>
233
           <PageContent parentClass='workspace__content'>
230
                     availableApp={contentType.filter(ct => ct.slug !== 'comment')} // @FIXME: Côme - 2018/08/21 - should use props.appList
240
                     availableApp={contentType.filter(ct => ct.slug !== 'comment')} // @FIXME: Côme - 2018/08/21 - should use props.appList
231
                     folderData={c}
241
                     folderData={c}
232
                     onClickItem={this.handleClickContentItem}
242
                     onClickItem={this.handleClickContentItem}
243
+                    idRoleUserWorkspace={idRoleUserWorkspace}
233
                     onClickExtendedAction={{
244
                     onClickExtendedAction={{
234
                       edit: this.handleClickEditContentItem,
245
                       edit: this.handleClickEditContentItem,
235
                       move: this.handleClickMoveContentItem,
246
                       move: this.handleClickMoveContentItem,
251
                     statusSlug={c.statusSlug}
262
                     statusSlug={c.statusSlug}
252
                     contentType={contentType.length ? contentType.find(ct => ct.slug === c.type) : null}
263
                     contentType={contentType.length ? contentType.find(ct => ct.slug === c.type) : null}
253
                     onClickItem={() => this.handleClickContentItem(c)}
264
                     onClickItem={() => this.handleClickContentItem(c)}
265
+                    idRoleUserWorkspace={idRoleUserWorkspace}
254
                     onClickExtendedAction={{
266
                     onClickExtendedAction={{
255
                       edit: e => this.handleClickEditContentItem(e, c),
267
                       edit: e => this.handleClickEditContentItem(e, c),
256
                       move: e => this.handleClickMoveContentItem(e, c),
268
                       move: e => this.handleClickMoveContentItem(e, c),
266
               )}
278
               )}
267
             </div>
279
             </div>
268
 
280
 
269
-            <DropdownCreateButton
270
-              customClass='workspace__content__button'
271
-              idFolder={null}
272
-              onClickCreateContent={this.handleClickCreateContent}
273
-              availableApp={contentType.filter(ct => ct.slug !== 'comment')} // @FIXME: Côme - 2018/08/21 - should use props.appList
274
-            />
281
+            {idRoleUserWorkspace >= 2 &&
282
+              <DropdownCreateButton
283
+                customClass='workspace__content__button'
284
+                idFolder={null}
285
+                onClickCreateContent={this.handleClickCreateContent}
286
+                availableApp={contentType.filter(ct => ct.slug !== 'comment')} // @FIXME: Côme - 2018/08/21 - should use props.appList
287
+              />
288
+            }
275
           </PageContent>
289
           </PageContent>
276
 
290
 
277
         </PageWrapper>
291
         </PageWrapper>
280
   }
294
   }
281
 }
295
 }
282
 
296
 
283
-const mapStateToProps = ({ user, workspaceContentList, workspaceList, contentType }) => ({ user, workspaceContentList, workspaceList, contentType })
297
+const mapStateToProps = ({ user, currentWorkspace, workspaceContentList, workspaceList, contentType }) => ({
298
+  user, currentWorkspace, workspaceContentList, workspaceList, contentType
299
+})
284
 export default withRouter(connect(mapStateToProps)(appFactory(WorkspaceContent)))
300
 export default withRouter(connect(mapStateToProps)(appFactory(WorkspaceContent)))

+ 44 - 6
frontend/src/helper.js View File

44
 }
44
 }
45
 
45
 
46
 export const ROLE = [{
46
 export const ROLE = [{
47
-  id: 0,
47
+  id: 1,
48
   slug: 'reader',
48
   slug: 'reader',
49
   faIcon: 'eye',
49
   faIcon: 'eye',
50
-  hexcolor: '#15D948',
50
+  hexcolor: '#15d948',
51
   label: 'Reader'
51
   label: 'Reader'
52
 }, {
52
 }, {
53
-  id: 1,
53
+  id: 2,
54
   slug: 'contributor',
54
   slug: 'contributor',
55
   faIcon: 'pencil',
55
   faIcon: 'pencil',
56
-  hexcolor: '#3145F7',
56
+  hexcolor: '#3145f7',
57
   label: 'Contributor'
57
   label: 'Contributor'
58
 }, {
58
 }, {
59
-  id: 2,
59
+  id: 4,
60
   slug: 'content-manager',
60
   slug: 'content-manager',
61
   faIcon: 'graduation-cap',
61
   faIcon: 'graduation-cap',
62
   hexcolor: '#f2af2d',
62
   hexcolor: '#f2af2d',
63
   label: 'Content manager'
63
   label: 'Content manager'
64
 }, {
64
 }, {
65
-  id: 3,
65
+  id: 8,
66
   slug: 'workspace-manager',
66
   slug: 'workspace-manager',
67
   faIcon: 'gavel',
67
   faIcon: 'gavel',
68
   hexcolor: '#ed0007',
68
   hexcolor: '#ed0007',
69
   label: 'Workspace manager'
69
   label: 'Workspace manager'
70
 }]
70
 }]
71
 
71
 
72
+export const findIdRoleUserWorkspace = (idUser, memberList, roleList) => {
73
+  const myself = memberList.find(u => u.id === idUser) || {role: 'reader'}
74
+  return (roleList.find(r => myself.role === r.slug) || {id: 1}).id
75
+}
76
+
77
+// Côme - 2018/08/21 - useful ?
78
+export const ROLE2 = {
79
+  reader: {
80
+    id: 1,
81
+    sluf: 'reader',
82
+    faIcon: 'eye',
83
+    hexcolor: '#15D948',
84
+    label: 'Reader'
85
+  },
86
+  contributor: {
87
+    id: 2,
88
+    slug: 'contributor',
89
+    faIcon: 'pencil',
90
+    hexcolor: '#3145f7',
91
+    label: 'Contributor'
92
+  },
93
+  contentManager: {
94
+    id: 4,
95
+    slug: 'content-manager',
96
+    faIcon: 'graduation-cap',
97
+    hexcolor: '#f2af2d',
98
+    label: 'Content manager'
99
+  },
100
+  workspaceManager: {
101
+    id: 8,
102
+    slug: 'workspace-manager',
103
+    faIcon: 'gavel',
104
+    hexcolor: '#ed0007',
105
+    label: 'Workspace manager'
106
+  }
107
+}
108
+
72
 export const PROFILE = {
109
 export const PROFILE = {
73
   ADMINISTRATOR: 'administrators',
110
   ADMINISTRATOR: 'administrators',
111
+  MANAGER: 'managers',
74
   USER: 'users'
112
   USER: 'users'
75
 }
113
 }
76
 
114
 

+ 25 - 18
frontend_app_html-document/src/container/HtmlDocument.jsx View File

384
           customColor={config.hexcolor}
384
           customColor={config.hexcolor}
385
           faIcon={config.faIcon}
385
           faIcon={config.faIcon}
386
           title={content.label}
386
           title={content.label}
387
+          idRoleUserWorkspace={loggedUser.idRoleUserWorkspace}
387
           onClickCloseBtn={this.handleClickBtnCloseApp}
388
           onClickCloseBtn={this.handleClickBtnCloseApp}
388
           onValidateChangeTitle={this.handleSaveEditTitle}
389
           onValidateChangeTitle={this.handleSaveEditTitle}
389
         />
390
         />
395
         >
396
         >
396
           <div /* this div in display flex, justify-content space-between */>
397
           <div /* this div in display flex, justify-content space-between */>
397
             <div className='d-flex'>
398
             <div className='d-flex'>
398
-              <NewVersionBtn
399
-                customColor={config.hexcolor}
400
-                onClickNewVersionBtn={this.handleClickNewVersion}
401
-                disabled={mode !== MODE.VIEW}
402
-              />
399
+              {loggedUser.idRoleUserWorkspace >= 2 &&
400
+                <NewVersionBtn
401
+                  customColor={config.hexcolor}
402
+                  onClickNewVersionBtn={this.handleClickNewVersion}
403
+                  disabled={mode !== MODE.VIEW}
404
+                />
405
+              }
403
 
406
 
404
               {mode === MODE.REVISION &&
407
               {mode === MODE.REVISION &&
405
                 <button
408
                 <button
414
             </div>
417
             </div>
415
 
418
 
416
             <div className='d-flex'>
419
             <div className='d-flex'>
417
-              <SelectStatus
418
-                selectedStatus={config.availableStatuses.find(s => s.slug === content.status)}
419
-                availableStatus={config.availableStatuses}
420
-                onChangeStatus={this.handleChangeStatus}
421
-                disabled={mode === MODE.REVISION}
422
-              />
423
-
424
-              <ArchiveDeleteContent
425
-                customColor={config.hexcolor}
426
-                onClickArchiveBtn={this.handleClickArchive}
427
-                onClickDeleteBtn={this.handleClickDelete}
428
-                disabled={mode === MODE.REVISION}
429
-              />
420
+              {loggedUser.idRoleUserWorkspace >= 2 &&
421
+                <SelectStatus
422
+                  selectedStatus={config.availableStatuses.find(s => s.slug === content.status)}
423
+                  availableStatus={config.availableStatuses}
424
+                  onChangeStatus={this.handleChangeStatus}
425
+                  disabled={mode === MODE.REVISION}
426
+                />
427
+              }
428
+
429
+              {loggedUser.idRoleUserWorkspace >= 4 &&
430
+                <ArchiveDeleteContent
431
+                  customColor={config.hexcolor}
432
+                  onClickArchiveBtn={this.handleClickArchive}
433
+                  onClickDeleteBtn={this.handleClickDelete}
434
+                  disabled={mode === MODE.REVISION}
435
+                />
436
+              }
430
             </div>
437
             </div>
431
           </div>
438
           </div>
432
         </PopinFixedOption>
439
         </PopinFixedOption>

+ 2 - 1
frontend_app_html-document/src/helper.js View File

73
     email: 'osef@algoo.fr',
73
     email: 'osef@algoo.fr',
74
     lang: 'en',
74
     lang: 'en',
75
     avatar_url: 'https://avatars3.githubusercontent.com/u/11177014?s=460&v=4',
75
     avatar_url: 'https://avatars3.githubusercontent.com/u/11177014?s=460&v=4',
76
-    auth: btoa(`${'admin@admin.admin'}:${'admin@admin.admin'}`)
76
+    auth: btoa(`${'admin@admin.admin'}:${'admin@admin.admin'}`),
77
+    idRoleUserWorkspace: 1
77
   },
78
   },
78
   content: {
79
   content: {
79
     author: {
80
     author: {

+ 18 - 13
frontend_app_thread/src/container/Thread.jsx View File

270
           customColor={config.hexcolor}
270
           customColor={config.hexcolor}
271
           faIcon={config.faIcon}
271
           faIcon={config.faIcon}
272
           title={content.label}
272
           title={content.label}
273
+          idRoleUserWorkspace={loggedUser.idRoleUserWorkspace}
273
           onClickCloseBtn={this.handleClickBtnCloseApp}
274
           onClickCloseBtn={this.handleClickBtnCloseApp}
274
           onValidateChangeTitle={this.handleSaveEditTitle}
275
           onValidateChangeTitle={this.handleSaveEditTitle}
275
         />
276
         />
280
           i18n={i18n}
281
           i18n={i18n}
281
         >
282
         >
282
           <div className='justify-content-end'>
283
           <div className='justify-content-end'>
283
-            <SelectStatus
284
-              selectedStatus={config.availableStatuses.find(s => s.slug === content.status)}
285
-              availableStatus={config.availableStatuses}
286
-              onChangeStatus={this.handleChangeStatus}
287
-              disabled={false}
288
-            />
289
-
290
-            <ArchiveDeleteContent
291
-              customColor={config.hexcolor}
292
-              onClickArchiveBtn={this.handleClickArchive}
293
-              onClickDeleteBtn={this.handleClickDelete}
294
-              disabled={false}
295
-            />
284
+            {loggedUser.idRoleUserWorkspace >= 2 &&
285
+              <SelectStatus
286
+                selectedStatus={config.availableStatuses.find(s => s.slug === content.status)}
287
+                availableStatus={config.availableStatuses}
288
+                onChangeStatus={this.handleChangeStatus}
289
+                disabled={false}
290
+              />
291
+            }
292
+
293
+            {loggedUser.idRoleUserWorkspace >= 4 &&
294
+              <ArchiveDeleteContent
295
+                customColor={config.hexcolor}
296
+                onClickArchiveBtn={this.handleClickArchive}
297
+                onClickDeleteBtn={this.handleClickDelete}
298
+                disabled={false}
299
+              />
300
+            }
296
           </div>
301
           </div>
297
         </PopinFixedOption>
302
         </PopinFixedOption>
298
 
303
 

+ 3 - 1
frontend_app_thread/src/helper.js View File

54
     lastname: 'Stoilenom',
54
     lastname: 'Stoilenom',
55
     email: 'osef@algoo.fr',
55
     email: 'osef@algoo.fr',
56
     avatar_url: 'https://avatars3.githubusercontent.com/u/11177014?s=460&v=4',
56
     avatar_url: 'https://avatars3.githubusercontent.com/u/11177014?s=460&v=4',
57
-    lang: 'en'
57
+    auth: btoa(`${'admin@admin.admin'}:${'admin@admin.admin'}`),
58
+    lang: 'en',
59
+    idRoleUserWorkspace: 1
58
   },
60
   },
59
   content: {
61
   content: {
60
     author: {
62
     author: {

+ 11 - 7
frontend_lib/src/component/PopinFixed/PopinFixedHeader.jsx View File

27
   }
27
   }
28
 
28
 
29
   render () {
29
   render () {
30
-    const { customClass, customColor, faIcon, title, onClickCloseBtn } = this.props
30
+    const { customClass, customColor, faIcon, title, idRoleUserWorkspace, onClickCloseBtn } = this.props
31
 
31
 
32
     return (
32
     return (
33
       <div className={classnames('wsContentGeneric__header', `${customClass}__header`)} style={{backgroundColor: customColor}}>
33
       <div className={classnames('wsContentGeneric__header', `${customClass}__header`)} style={{backgroundColor: customColor}}>
42
           }
42
           }
43
         </div>
43
         </div>
44
 
44
 
45
-        <div
46
-          className={classnames('wsContentGeneric__header__edittitle', `${customClass}__header__changetitle`)}
47
-          onClick={this.handleClickChangeTitleBtn}
48
-        >
49
-          {this.state.editTitle ? <i className='fa fa-check' title='Valider le Titre' /> : <i className='fa fa-pencil' title='Modifier le Titre' />}
50
-        </div>
45
+        {idRoleUserWorkspace >= 2 &&
46
+          <div
47
+            className={classnames('wsContentGeneric__header__edittitle', `${customClass}__header__changetitle`)}
48
+            onClick={this.handleClickChangeTitleBtn}
49
+          >
50
+            {this.state.editTitle ? <i className='fa fa-check' title='Valider le Titre' /> : <i className='fa fa-pencil' title='Modifier le Titre' />}
51
+          </div>
52
+        }
51
 
53
 
52
         <div
54
         <div
53
           className={classnames('wsContentGeneric__header__close', `${customClass}__header__close`)}
55
           className={classnames('wsContentGeneric__header__close', `${customClass}__header__close`)}
68
   customClass: PropTypes.string,
70
   customClass: PropTypes.string,
69
   customColor: PropTypes.string,
71
   customColor: PropTypes.string,
70
   title: PropTypes.string,
72
   title: PropTypes.string,
73
+  idRoleUserWorkspace: PropTypes.number,
71
   onValidateChangeTitle: PropTypes.func
74
   onValidateChangeTitle: PropTypes.func
72
 }
75
 }
73
 
76
 
75
   customClass: '',
78
   customClass: '',
76
   customColor: '',
79
   customColor: '',
77
   title: '',
80
   title: '',
81
+  idRoleUserWorkspace: 1,
78
   onChangeTitle: () => {}
82
   onChangeTitle: () => {}
79
 }
83
 }

+ 59 - 56
frontend_lib/src/component/Timeline/Timeline.jsx View File

105
             <li style={{visibility: 'hidden'}} ref={el => { this.timelineBottom = el }} />
105
             <li style={{visibility: 'hidden'}} ref={el => { this.timelineBottom = el }} />
106
           </ul>
106
           </ul>
107
 
107
 
108
-          <form className={classnames(`${props.customClass}__texteditor`, 'timeline__body__texteditor')}>
109
-            <div className={classnames(`${props.customClass}__texteditor__textinput`, 'timeline__body__texteditor__textinput')}>
110
-              <textarea
111
-                id='wysiwygTimelineComment'
112
-                placeholder='Votre message ...'
113
-                value={props.newComment}
114
-                onChange={props.onChangeNewComment}
115
-                disabled={props.disableComment}
116
-              />
117
-            </div>
118
-
119
-            <div className={classnames(`${props.customClass}__texteditor__wrapper`, 'timeline__body__texteditor__wrapper')}>
120
-              <div className={classnames(`${props.customClass}__texteditor__advancedtext`, 'timeline__body__texteditor__advancedtext')}>
121
-                <button
122
-                  type='button'
123
-                  className={classnames(
124
-                    `${props.customClass}__texteditor__advancedtext__btn timeline__body__texteditor__advancedtext__btn btn`
125
-                  )}
126
-                  onClick={props.onClickWysiwygBtn}
108
+          {props.loggedUser.idRoleUserWorkspace >= 2 &&
109
+            <form className={classnames(`${props.customClass}__texteditor`, 'timeline__body__texteditor')}>
110
+              <div className={classnames(`${props.customClass}__texteditor__textinput`, 'timeline__body__texteditor__textinput')}>
111
+                <textarea
112
+                  id='wysiwygTimelineComment'
113
+                  placeholder='Votre message ...'
114
+                  value={props.newComment}
115
+                  onChange={props.onChangeNewComment}
127
                   disabled={props.disableComment}
116
                   disabled={props.disableComment}
128
-                  style={{
129
-                    backgroundColor: 'transparent',
130
-                    color: '#333',
131
-                    borderColor: props.customColor,
132
-                    ':hover': {
133
-                      backgroundColor: props.customColor,
134
-                      color: '#fdfdfd'
135
-                    }
136
-                  }}
137
-                  key={'timeline__comment__advancedtext'}
138
-                >
139
-                  {props.wysiwyg ? 'Texte simple' : 'Texte riche'}
140
-                </button>
117
+                />
141
               </div>
118
               </div>
142
 
119
 
143
-              <div className={classnames(`${props.customClass}__texteditor__submit`, 'timeline__body__texteditor__submit')}>
144
-                <button
145
-                  type='button'
146
-                  className={classnames(`${props.customClass}__texteditor__submit__btn`, 'timeline__body__texteditor__submit__btn btn')}
147
-                  onClick={props.onClickValidateNewCommentBtn}
148
-                  disabled={props.disableComment}
149
-                  style={{
150
-                    backgroundColor: props.customColor,
151
-                    color: '#fdfdfd',
152
-                    ':hover': {
153
-                      backgroundColor: color(props.customColor).darken(0.15).hexString()
154
-                    }
155
-                  }}
156
-                  key={'timeline__comment__send'}
157
-                >
158
-                  Envoyer
159
-                  <div
160
-                    className={classnames(`${props.customClass}__texteditor__submit__btn__icon`, 'timeline__body__texteditor__submit__btn__icon')}>
161
-                    <i className='fa fa-paper-plane-o' />
162
-                  </div>
163
-                </button>
120
+              <div className={classnames(`${props.customClass}__texteditor__wrapper`, 'timeline__body__texteditor__wrapper')}>
121
+                <div className={classnames(`${props.customClass}__texteditor__advancedtext`, 'timeline__body__texteditor__advancedtext')}>
122
+                  <button
123
+                    type='button'
124
+                    className={classnames(
125
+                      `${props.customClass}__texteditor__advancedtext__btn timeline__body__texteditor__advancedtext__btn btn`
126
+                    )}
127
+                    onClick={props.onClickWysiwygBtn}
128
+                    disabled={props.disableComment}
129
+                    style={{
130
+                      backgroundColor: 'transparent',
131
+                      color: '#333',
132
+                      borderColor: props.customColor,
133
+                      ':hover': {
134
+                        backgroundColor: props.customColor,
135
+                        color: '#fdfdfd'
136
+                      }
137
+                    }}
138
+                    key={'timeline__comment__advancedtext'}
139
+                  >
140
+                    {props.wysiwyg ? 'Texte simple' : 'Texte riche'}
141
+                  </button>
142
+                </div>
143
+
144
+                <div className={classnames(`${props.customClass}__texteditor__submit`, 'timeline__body__texteditor__submit')}>
145
+                  <button
146
+                    type='button'
147
+                    className={classnames(`${props.customClass}__texteditor__submit__btn`, 'timeline__body__texteditor__submit__btn btn')}
148
+                    onClick={props.onClickValidateNewCommentBtn}
149
+                    disabled={props.disableComment}
150
+                    style={{
151
+                      backgroundColor: props.customColor,
152
+                      color: '#fdfdfd',
153
+                      ':hover': {
154
+                        backgroundColor: color(props.customColor).darken(0.15).hexString()
155
+                      }
156
+                    }}
157
+                    key={'timeline__comment__send'}
158
+                  >
159
+                    Envoyer
160
+                    <div
161
+                      className={classnames(`${props.customClass}__texteditor__submit__btn__icon`, 'timeline__body__texteditor__submit__btn__icon')}>
162
+                      <i className='fa fa-paper-plane-o' />
163
+                    </div>
164
+                  </button>
165
+                </div>
164
               </div>
166
               </div>
165
-            </div>
166
-          </form>
167
+            </form>
168
+          }
167
         </div>
169
         </div>
168
       </div>
170
       </div>
169
     )
171
     )
200
   loggedUser: {
202
   loggedUser: {
201
     id: '',
203
     id: '',
202
     name: '',
204
     name: '',
203
-    avatar: ''
205
+    avatar: '',
206
+    idRoleUserWorkspace: 1
204
   },
207
   },
205
   timelineData: [],
208
   timelineData: [],
206
   wysiwyg: false,
209
   wysiwyg: false,