浏览代码

improve a bit user/workspace admin screen

Damien ACCORSI 9 年前
父节点
当前提交
dd84222efd

+ 56 - 3
tracim/tracim/controllers/admin/user.py 查看文件

18
 from formencode.validators import FieldsMatch
18
 from formencode.validators import FieldsMatch
19
 
19
 
20
 from tracim.controllers import TIMRestController
20
 from tracim.controllers import TIMRestController
21
+from tracim.controllers.user import UserWorkspaceRestController
22
+
21
 from tracim.lib import CST
23
 from tracim.lib import CST
22
 from tracim.lib import helpers as h
24
 from tracim.lib import helpers as h
23
 from tracim.lib.base import logger
25
 from tracim.lib.base import logger
212
         tg.redirect(next_url)
214
         tg.redirect(next_url)
213
 
215
 
214
 
216
 
217
+class UserWorkspaceRestController(TIMRestController):
218
+
219
+    def _before(self, *args, **kw):
220
+        """
221
+        Instantiate the current workspace in tg.tmpl_context
222
+        :param args:
223
+        :param kw:
224
+        :return:
225
+        """
226
+        super(self.__class__, self)._before(args, kw)
227
+
228
+        api = UserApi(tg.tmpl_context.current_user)
229
+        user_id = tg.request.controller_state.routing_args.get('user_id')
230
+        user = api.get_one(user_id)
231
+        tg.tmpl_context.user_id = user_id
232
+        tg.tmpl_context.user = user
233
+
234
+    @tg.expose()
235
+    def enable_notifications(self, workspace_id, next_url=None):
236
+        workspace_id = int(workspace_id)
237
+        api = WorkspaceApi(tg.tmpl_context.current_user)
238
+
239
+        workspace = api.get_one(workspace_id)
240
+        api.enable_notifications(tg.tmpl_context.user, workspace)
241
+        tg.flash(_('User {}: notification enabled for workspace {}').format(
242
+            tg.tmpl_context.user.get_display_name(), workspace.label))
243
+
244
+        if next_url:
245
+            tg.redirect(tg.url(next_url))
246
+        tg.redirect(self.parent_controller.url(None, 'me'))
247
+
248
+    @tg.expose()
249
+    def disable_notifications(self, workspace_id, next_url=None):
250
+        workspace_id = int(workspace_id)
251
+        api = WorkspaceApi(tg.tmpl_context.current_user)
252
+
253
+        workspace = api.get_one(workspace_id)
254
+        api.disable_notifications(tg.tmpl_context.user, workspace)
255
+        tg.flash(_('User {}: notification disabled for workspace {}').format(
256
+            tg.tmpl_context.user.get_display_name(), workspace.label))
257
+
258
+        if next_url:
259
+            tg.redirect(tg.url(next_url))
260
+        tg.redirect(self.parent_controller.url(None, 'me'))
261
+
262
+
215
 class UserRestController(TIMRestController):
263
 class UserRestController(TIMRestController):
216
     """
264
     """
217
      CRUD Controller allowing to manage Users
265
      CRUD Controller allowing to manage Users
220
 
268
 
221
     password = UserPasswordAdminRestController()
269
     password = UserPasswordAdminRestController()
222
     profile = UserProfileAdminRestController()
270
     profile = UserProfileAdminRestController()
271
+    workspaces = UserWorkspaceRestController()
223
 
272
 
224
     @classmethod
273
     @classmethod
225
     def current_item_id_key_in_context(cls):
274
     def current_item_id_key_in_context(cls):
289
 
338
 
290
         user = api.get_one(user_id) # FIXME
339
         user = api.get_one(user_id) # FIXME
291
 
340
 
292
-        dictified_user = Context(CTX.USER).toDict(user, 'user')
341
+        role_api = RoleApi(tg.tmpl_context.current_user)
342
+        role_list = role_api.get_roles_for_select_field()
343
+
344
+        dictified_user = Context(CTX.ADMIN_USER).toDict(user, 'user')
293
         current_user_content = Context(CTX.CURRENT_USER).toDict(tmpl_context.current_user)
345
         current_user_content = Context(CTX.CURRENT_USER).toDict(tmpl_context.current_user)
294
-        fake_api_content = DictLikeClass(current_user=current_user_content)
295
-        fake_api = Context(CTX.WORKSPACE).toDict(fake_api_content)
346
+        fake_api_content = DictLikeClass(current_user=current_user_content,
347
+                                         role_types=role_list)
348
+        fake_api = Context(CTX.ADMIN_USER).toDict(fake_api_content)
296
 
349
 
297
         return DictLikeClass(result = dictified_user, fake_api=fake_api)
350
         return DictLikeClass(result = dictified_user, fake_api=fake_api)
298
 
351
 

+ 5 - 6
tracim/tracim/controllers/admin/workspace.py 查看文件

54
     def get_one(self, user_id):
54
     def get_one(self, user_id):
55
         pass
55
         pass
56
 
56
 
57
-
58
     def put(self, *args, **kw):
57
     def put(self, *args, **kw):
59
         pass
58
         pass
60
 
59
 
90
     def undelete(self, user_id, old_role):
89
     def undelete(self, user_id, old_role):
91
         user_id = int(user_id)
90
         user_id = int(user_id)
92
         role_id = int(old_role)
91
         role_id = int(old_role)
93
-        self._add_user_with_role(user_id, role_id, _('User {} restored in workspace {} as {}'))
92
+        self._add_user_with_role(user_id, role_id, None, _('User {} restored in workspace {} as {}'))
94
         tg.redirect(self.parent_controller.url(tg.tmpl_context.workspace_id))
93
         tg.redirect(self.parent_controller.url(tg.tmpl_context.workspace_id))
95
 
94
 
96
     @tg.expose()
95
     @tg.expose()
97
-    def post(self, user_id, role_id):
96
+    def post(self, user_id, role_id, with_notif=False):
98
         user_id = int(user_id)
97
         user_id = int(user_id)
99
         role_id = int(role_id)
98
         role_id = int(role_id)
100
-        self._add_user_with_role(user_id, role_id, _('User {} added to workspace {} as {}'))
99
+        self._add_user_with_role(user_id, role_id, with_notif, _('User {} added to workspace {} as {}'))
101
         tg.redirect(self.parent_controller.url(tg.tmpl_context.workspace_id))
100
         tg.redirect(self.parent_controller.url(tg.tmpl_context.workspace_id))
102
 
101
 
103
-    def _add_user_with_role(self, user_id: int, role_id: int, flash_msg_template)-> UserRoleInWorkspace:
102
+    def _add_user_with_role(self, user_id: int, role_id: int, with_notif: bool, flash_msg_template)-> UserRoleInWorkspace:
104
         user_api = UserApi(tg.tmpl_context.current_user)
103
         user_api = UserApi(tg.tmpl_context.current_user)
105
         user = user_api.get_one(user_id)
104
         user = user_api.get_one(user_id)
106
 
105
 
107
         role_api = RoleApi(tg.tmpl_context.current_user)
106
         role_api = RoleApi(tg.tmpl_context.current_user)
108
-        role = role_api.create_one(user, tg.tmpl_context.workspace, role_id)
107
+        role = role_api.create_one(user, tg.tmpl_context.workspace, role_id, with_notif)
109
 
108
 
110
         tg.flash(flash_msg_template.format(
109
         tg.flash(flash_msg_template.format(
111
             role.user.get_display_name(),
110
             role.user.get_display_name(),

+ 3 - 1
tracim/tracim/lib/userworkspace.py 查看文件

50
     def get_one(self, user_id, workspace_id):
50
     def get_one(self, user_id, workspace_id):
51
         return self._get_one_rsc(user_id, workspace_id).one()
51
         return self._get_one_rsc(user_id, workspace_id).one()
52
 
52
 
53
-    def create_one(self, user: User, workspace: Workspace, role_level: int, flush: bool=True) -> UserRoleInWorkspace:
53
+    def create_one(self, user: User, workspace: Workspace, role_level: int, with_notif: bool, flush: bool=True) -> UserRoleInWorkspace:
54
         role = self.create_role()
54
         role = self.create_role()
55
         role.user_id = user.user_id
55
         role.user_id = user.user_id
56
         role.workspace = workspace
56
         role.workspace = workspace
57
         role.role = role_level
57
         role.role = role_level
58
+        if with_notif is not None:
59
+            role.do_notify = with_notif
58
         if flush:
60
         if flush:
59
             DBSession.flush()
61
             DBSession.flush()
60
         return role
62
         return role

+ 8 - 3
tracim/tracim/model/serializers.py 查看文件

62
 
62
 
63
 class CTX(object):
63
 class CTX(object):
64
     """ constants that are used for serialization / dictification of models"""
64
     """ constants that are used for serialization / dictification of models"""
65
+    ADMIN_USER = 'ADMIN_USER'
65
     ADMIN_WORKSPACE = 'ADMIN_WORKSPACE'
66
     ADMIN_WORKSPACE = 'ADMIN_WORKSPACE'
66
     ADMIN_WORKSPACES = 'ADMIN_WORKSPACES'
67
     ADMIN_WORKSPACES = 'ADMIN_WORKSPACES'
67
     CONTENT_LIST = 'CONTENT_LIST'
68
     CONTENT_LIST = 'CONTENT_LIST'
785
 
786
 
786
 
787
 
787
 @pod_serializer(RoleType, CTX.ADMIN_WORKSPACE)
788
 @pod_serializer(RoleType, CTX.ADMIN_WORKSPACE)
789
+@pod_serializer(RoleType, CTX.ADMIN_USER)
788
 def serialize_role_list_for_select_field_in_workspace(role_type: RoleType, context: Context):
790
 def serialize_role_list_for_select_field_in_workspace(role_type: RoleType, context: Context):
789
     """
791
     """
790
     Actually, roles are serialized as users (with minimal information)
792
     Actually, roles are serialized as users (with minimal information)
838
 
840
 
839
 
841
 
840
 @pod_serializer(User, CTX.USER)
842
 @pod_serializer(User, CTX.USER)
843
+@pod_serializer(User, CTX.ADMIN_USER)
841
 @pod_serializer(User, CTX.CURRENT_USER)
844
 @pod_serializer(User, CTX.CURRENT_USER)
842
 def serialize_user_for_user(user: User, context: Context):
845
 def serialize_user_for_user(user: User, context: Context):
843
     """
846
     """
876
     result['style'] = role.style
879
     result['style'] = role.style
877
     result['role_description'] = role.role_as_label()
880
     result['role_description'] = role.role_as_label()
878
     result['email'] = role.user.email
881
     result['email'] = role.user.email
879
-    result['user'] = role.user
882
+    result['user'] = context.toDict(role.user)
880
     result['notifications_subscribed'] = role.do_notify
883
     result['notifications_subscribed'] = role.do_notify
881
     return result
884
     return result
882
 
885
 
883
 
886
 
884
 @pod_serializer(UserRoleInWorkspace, CTX.USER)
887
 @pod_serializer(UserRoleInWorkspace, CTX.USER)
885
 @pod_serializer(UserRoleInWorkspace, CTX.CURRENT_USER)
888
 @pod_serializer(UserRoleInWorkspace, CTX.CURRENT_USER)
889
+@pod_serializer(UserRoleInWorkspace, CTX.ADMIN_USER)
886
 def serialize_role_in_list_for_user(role: UserRoleInWorkspace, context: Context):
890
 def serialize_role_in_list_for_user(role: UserRoleInWorkspace, context: Context):
887
     """
891
     """
888
     Actually, roles are serialized as users (with minimal information)
892
     Actually, roles are serialized as users (with minimal information)
896
     result['label'] = role.role_as_label()
900
     result['label'] = role.role_as_label()
897
     result['style'] = RoleType(role.role).css_style
901
     result['style'] = RoleType(role.role).css_style
898
     result['workspace'] =  context.toDict(role.workspace)
902
     result['workspace'] =  context.toDict(role.workspace)
899
-    result['user'] = role.user
903
+    result['user'] = Context(CTX.DEFAULT).toDict(role.user)
900
     result['notifications_subscribed'] = role.do_notify
904
     result['notifications_subscribed'] = role.do_notify
901
 
905
 
902
     # result['workspace_name'] = role.workspace.label
906
     # result['workspace_name'] = role.workspace.label
910
 def serialize_workspace_default(workspace: Workspace, context: Context):
914
 def serialize_workspace_default(workspace: Workspace, context: Context):
911
     result = DictLikeClass(
915
     result = DictLikeClass(
912
         id = workspace.workspace_id,
916
         id = workspace.workspace_id,
913
-        label = workspace.label,
917
+        label = workspace.label,  # FIXME - 2015-08-20 - remove this property
918
+        name = workspace.label,  # use name instead of label
914
         url = context.url('/workspaces/{}'.format(workspace.workspace_id))
919
         url = context.url('/workspaces/{}'.format(workspace.workspace_id))
915
     )
920
     )
916
     return result
921
     return result

+ 6 - 2
tracim/tracim/templates/admin/user_getone.mak 查看文件

61
                             ${ICON.FA('fa-bar-chart t-less-visible')}
61
                             ${ICON.FA('fa-bar-chart t-less-visible')}
62
                             ${_('Global profile')}
62
                             ${_('Global profile')}
63
                         </h3>
63
                         </h3>
64
-                        ${P.USER_PROFILE(result.user)}
64
+                        ${P.USER_PROFILE(fake_api.current_user, result.user)}
65
                     </div>
65
                     </div>
66
                     <div style="margin-top: 4em;">
66
                     <div style="margin-top: 4em;">
67
                         <h3>
67
                         <h3>
81
                                     </tr>
81
                                     </tr>
82
                                 </thead>
82
                                 </thead>
83
                                 % for role in result.user.roles:
83
                                 % for role in result.user.roles:
84
-                                    ${TABLE_ROW.USER_ROLE_IN_WORKSPACE(role)}
84
+<%
85
+    enable_link = '/admin/users/{user}/workspaces/{workspace}/enable_notifications?next_url=/admin/users/{user}'
86
+    disable_link = '/admin/users/{user}/workspaces/{workspace}/disable_notifications?next_url=/admin/users/{user}'
87
+%>
88
+                                    ${TABLE_ROW.USER_ROLE_IN_WORKSPACE(fake_api.current_user, role, show_id=True, enable_link=enable_link, disable_link=disable_link)}
85
                                 % endfor
89
                                 % endfor
86
                             </table>
90
                             </table>
87
                         % endif
91
                         % endif

+ 6 - 0
tracim/tracim/templates/admin/workspace_getone.mak 查看文件

83
                                             % endfor
83
                                             % endfor
84
                                         </div>
84
                                         </div>
85
 
85
 
86
+                                        <div class="checkbox">
87
+                                            <label>
88
+                                                <input type="checkbox" id="with_notif" name="with_notif" checked="checked"/> ${_('Subscribe to mail notifications')}
89
+                                            </label>
90
+                                        </div>
91
+
86
                                         <span class="pull-right" style="margin-top: 0.5em;">
92
                                         <span class="pull-right" style="margin-top: 0.5em;">
87
                                             <button id="current-document-add-comment-save-button" type="submit" class="btn btn-small btn-success" title="Add first comment"><i class=" fa fa-check"></i> ${_('Validate')}</button>
93
                                             <button id="current-document-add-comment-save-button" type="submit" class="btn btn-small btn-success" title="Add first comment"><i class=" fa fa-check"></i> ${_('Validate')}</button>
88
                                         </span>
94
                                         </span>

+ 1 - 1
tracim/tracim/templates/home.mak 查看文件

157
                                                 </tr>
157
                                                 </tr>
158
                                             </thead>
158
                                             </thead>
159
                                             % for role in fake_api.current_user.roles:
159
                                             % for role in fake_api.current_user.roles:
160
-                                                ${TABLE_ROW.USER_ROLE_IN_WORKSPACE(role, show_id=False, enable_link='/user/me/workspaces/{workspace}/enable_notifications?next_url=/home', disable_link='/user/me/workspaces/{workspace}/disable_notifications?next_url=/home')}
160
+                                                ${TABLE_ROW.USER_ROLE_IN_WORKSPACE(fake_api.current_user, role, show_id=False, enable_link='/user/me/workspaces/{workspace}/enable_notifications?next_url=/home', disable_link='/user/me/workspaces/{workspace}/disable_notifications?next_url=/home')}
161
                                             % endfor
161
                                             % endfor
162
                                         </table>
162
                                         </table>
163
                                     % endif
163
                                     % endif

+ 1 - 1
tracim/tracim/templates/user_get_me.mak 查看文件

62
                             </table>
62
                             </table>
63
                         % endif
63
                         % endif
64
                     </div>
64
                     </div>
65
-                    % if len(result.user.roles)>0:
65
+                    % if len(result.user.roles) > 0:
66
                         <p class="alert alert-info">${_('You can configure your email notifications by clicking on the email icons above')}</p>
66
                         <p class="alert alert-info">${_('You can configure your email notifications by clicking on the email icons above')}</p>
67
                     % endif
67
                     % endif
68
                 </div>
68
                 </div>

+ 1 - 1
tracim/tracim/templates/widgets/paragraph.mak 查看文件

1
 <%namespace name="ICON" file="tracim.templates.widgets.icon"/>
1
 <%namespace name="ICON" file="tracim.templates.widgets.icon"/>
2
 <%namespace name="BUTTON" file="tracim.templates.widgets.button"/>
2
 <%namespace name="BUTTON" file="tracim.templates.widgets.button"/>
3
 
3
 
4
-<%def name="USER_PROFILE(user)">
4
+<%def name="USER_PROFILE(current_user, user)">
5
     % if user.profile.id >= 1:
5
     % if user.profile.id >= 1:
6
         <p>${ICON.FA('fa-male t-green fa-lg fa-fw')}<span> ${_('This user a standard user.')}</span></p>
6
         <p>${ICON.FA('fa-male t-green fa-lg fa-fw')}<span> ${_('This user a standard user.')}</span></p>
7
     %else:
7
     %else:

+ 30 - 4
tracim/tracim/templates/widgets/table_row.mak 查看文件

2
 <%namespace name="ICON" file="tracim.templates.widgets.icon"/>
2
 <%namespace name="ICON" file="tracim.templates.widgets.icon"/>
3
 <%namespace name="SPAN" file="tracim.templates.widgets.span"/>
3
 <%namespace name="SPAN" file="tracim.templates.widgets.span"/>
4
 
4
 
5
-<%def name="USER_ROLE_IN_WORKSPACE(role, show_id=True, enable_link=None, disable_link=None)">
5
+<%def name="USER_ROLE_IN_WORKSPACE(current_user, role, show_id=True, enable_link=None, disable_link=None, role_types=None)">
6
     <tr>
6
     <tr>
7
         % if show_id:
7
         % if show_id:
8
             <td class="text-right">${role.workspace.id}</td>
8
             <td class="text-right">${role.workspace.id}</td>
9
         % endif
9
         % endif
10
         <td><a href="${tg.url('/admin/workspaces/{}').format(role.workspace.id)}">${role.workspace.name}</a></td>
10
         <td><a href="${tg.url('/admin/workspaces/{}').format(role.workspace.id)}">${role.workspace.name}</a></td>
11
-        <td><span style="${role.style}"><i class="fa ${role.icon}"></i> ${role.label}</span></td>
12
-        % if enable_link or disable_link:
11
+
12
+        % if role_types:
13
+            ## <td>${BUTTON.SECURED_ROLE_SELECTOR(fake_api.current_user, result.workspace, member, fake_api.role_types)}</td>
14
+            <td><span style="${role.style}"><i class="fa ${role.icon}"></i> ${role.label}</span></td>
15
+        % else:
16
+            <td><span style="${role.style}"><i class="fa ${role.icon}"></i> ${role.label}</span></td>
17
+        % endif
18
+
19
+        <%
20
+            user_is_himself = current_user.id == role.user.id
21
+            user_is_manager = h.user_role(current_user, role.workspace) >= 8
22
+            ## allow user to change notification status only if current user is manager on the given workspace
23
+        %>
24
+
25
+        % if (enable_link or disable_link) and (user_is_himself or user_is_manager) :
13
             <td>${SPAN.NOTIFICATION_SUBSCRIBED(role.user, role.workspace, role.notifications_subscribed, enable_link, disable_link)}</td>
26
             <td>${SPAN.NOTIFICATION_SUBSCRIBED(role.user, role.workspace, role.notifications_subscribed, enable_link, disable_link)}</td>
14
         % else:
27
         % else:
15
             <td>${SPAN.NOTIFICATION_SUBSCRIBED(role.user, role.workspace, role.notifications_subscribed)}</td>
28
             <td>${SPAN.NOTIFICATION_SUBSCRIBED(role.user, role.workspace, role.notifications_subscribed)}</td>
23
         <td class="text-right">${member.id}</td>
36
         <td class="text-right">${member.id}</td>
24
         <td ><a href="${tg.url('/admin/users/{}'.format(member.id))}">${member.name}</a></td>
37
         <td ><a href="${tg.url('/admin/users/{}'.format(member.id))}">${member.name}</a></td>
25
         <td>${BUTTON.SECURED_ROLE_SELECTOR(fake_api.current_user, result.workspace, member, fake_api.role_types)}</td>
38
         <td>${BUTTON.SECURED_ROLE_SELECTOR(fake_api.current_user, result.workspace, member, fake_api.role_types)}</td>
26
-        <td>${SPAN.NOTIFICATION_SUBSCRIBED(member, workspace, member.notifications_subscribed)}</td>
39
+        <%
40
+            user_is_himself = current_user.id == member.id
41
+            user_is_manager = h.user_role(current_user, workspace) >= 8
42
+            ## allow user to change notification status only if current user is manager on the given workspace
43
+
44
+            enable_link = '/admin/users/{user}/workspaces/{workspace}/enable_notifications?next_url=/admin/workspaces/{workspace}'
45
+            disable_link = '/admin/users/{user}/workspaces/{workspace}/disable_notifications?next_url=/admin/workspaces/{workspace}'
46
+        %>
47
+        % if (enable_link or disable_link) and (user_is_himself or user_is_manager) :
48
+            <td>${SPAN.NOTIFICATION_SUBSCRIBED(member, workspace, member.notifications_subscribed, enable_link, disable_link)}</td>
49
+        % else:
50
+            <td>${SPAN.NOTIFICATION_SUBSCRIBED(member, workspace, member.notifications_subscribed)}</td>
51
+        % endif
52
+
27
         <td><a title="${_('Remove this user from the current workspace')}" class="t-less-visible t-red-on-hover t-red btn btn-default btn-xs" href="${tg.url('/admin/workspaces/{}/roles/{}/delete'.format(result.workspace.id, member.id))}">${ICON.FA('fa-remove fa-fw')}</a></td>
53
         <td><a title="${_('Remove this user from the current workspace')}" class="t-less-visible t-red-on-hover t-red btn btn-default btn-xs" href="${tg.url('/admin/workspaces/{}/roles/{}/delete'.format(result.workspace.id, member.id))}">${ICON.FA('fa-remove fa-fw')}</a></td>
28
     </tr>
54
     </tr>
29
 </%def>
55
 </%def>