##// END OF EJS Templates
user-account: expose membership of user groups to users....
marcink -
r2496:3250285e default
parent child Browse files
Show More
@@ -0,0 +1,75 b''
1 ## -*- coding: utf-8 -*-
2
3 <div class="panel panel-default">
4 <div class="panel-heading">
5 <h3 class="panel-title">${_('User Group Membership')}</h3>
6 </div>
7
8 <div class="panel-body">
9 <div class="groups_management">
10 <div id="repos_list_wrap">
11 <table id="user_group_list_table" class="display"></table>
12 </div>
13 </div>
14 </div>
15 </div>
16
17
18 <script>
19 var api;
20 $(document).ready(function() {
21
22 var get_datatable_count = function(){
23 $('#user_group_count').text(api.page.info().recordsDisplay);
24 };
25
26 $('#user_group_list_table').on('click', 'a.editor_remove', function (e) {
27 e.preventDefault();
28 var row = api.row($(this).closest('tr'));
29 row.remove().draw();
30 } );
31
32 $('#user_group_list_table').DataTable({
33 data: ${c.user_groups|n},
34 dom: 'rtp',
35 pageLength: ${c.visual.admin_grid_items},
36 order: [[ 0, "asc" ]],
37 columns: [
38 { data: {"_": "group_name",
39 "sort": "group_name"}, title: "${_('Name')}", className: "td-componentname," ,
40 render: function (data,type,full,meta)
41 {return '<div><i class="icon-group" title="User group">'+data+'</i></div>'}},
42
43 { data: {"_": "group_description",
44 "sort": "group_description"}, title: "${_('Description')}", className: "td-description" },
45 { data: {"_": "users_group_id"}, className: "td-user",
46 render: function (data,type,full,meta)
47 {return '<input type="hidden" name="users_group_id" value="'+data+'">'}},
48 { data: {"_": "active",
49 "sort": "active"}, title: "${_('Active')}", className: "td-active"},
50 { data: {"_": "owner_data"}, title: "${_('Owner')}", className: "td-user",
51 render: function (data,type,full,meta)
52 {return '<div class="rc-user tooltip">'+
53 '<img class="gravatar" src="'+ data.owner_icon +'" height="16" width="16">'+
54 data.owner +'</div>'
55 }
56 }
57 ],
58 language: {
59 paginate: DEFAULT_GRID_PAGINATION,
60 emptyTable: _gettext("No user groups available yet.")
61 },
62 "initComplete": function( settings, json ) {
63 var data_grid = $('#user_group_list_table').dataTable();
64 api = data_grid.api();
65 get_datatable_count();
66 }
67 });
68
69 // update the counter when doing search
70 $('#user_group_list_table').on( 'search.dt', function (e,settings) {
71 get_datatable_count();
72 });
73
74 });
75 </script> No newline at end of file
@@ -1,140 +1,145 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2018 RhodeCode GmbH
3 # Copyright (C) 2016-2018 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21
21
22 from rhodecode.apps._base import ADMIN_PREFIX
22 from rhodecode.apps._base import ADMIN_PREFIX
23
23
24
24
25 def includeme(config):
25 def includeme(config):
26
26
27 config.add_route(
27 config.add_route(
28 name='my_account_profile',
28 name='my_account_profile',
29 pattern=ADMIN_PREFIX + '/my_account/profile')
29 pattern=ADMIN_PREFIX + '/my_account/profile')
30
30
31 # my account edit details
31 # my account edit details
32 config.add_route(
32 config.add_route(
33 name='my_account_edit',
33 name='my_account_edit',
34 pattern=ADMIN_PREFIX + '/my_account/edit')
34 pattern=ADMIN_PREFIX + '/my_account/edit')
35 config.add_route(
35 config.add_route(
36 name='my_account_update',
36 name='my_account_update',
37 pattern=ADMIN_PREFIX + '/my_account/update')
37 pattern=ADMIN_PREFIX + '/my_account/update')
38
38
39 # my account password
39 # my account password
40 config.add_route(
40 config.add_route(
41 name='my_account_password',
41 name='my_account_password',
42 pattern=ADMIN_PREFIX + '/my_account/password')
42 pattern=ADMIN_PREFIX + '/my_account/password')
43
43
44 config.add_route(
44 config.add_route(
45 name='my_account_password_update',
45 name='my_account_password_update',
46 pattern=ADMIN_PREFIX + '/my_account/password/update')
46 pattern=ADMIN_PREFIX + '/my_account/password/update')
47
47
48 # my account tokens
48 # my account tokens
49 config.add_route(
49 config.add_route(
50 name='my_account_auth_tokens',
50 name='my_account_auth_tokens',
51 pattern=ADMIN_PREFIX + '/my_account/auth_tokens')
51 pattern=ADMIN_PREFIX + '/my_account/auth_tokens')
52 config.add_route(
52 config.add_route(
53 name='my_account_auth_tokens_add',
53 name='my_account_auth_tokens_add',
54 pattern=ADMIN_PREFIX + '/my_account/auth_tokens/new')
54 pattern=ADMIN_PREFIX + '/my_account/auth_tokens/new')
55 config.add_route(
55 config.add_route(
56 name='my_account_auth_tokens_delete',
56 name='my_account_auth_tokens_delete',
57 pattern=ADMIN_PREFIX + '/my_account/auth_tokens/delete')
57 pattern=ADMIN_PREFIX + '/my_account/auth_tokens/delete')
58
58
59 # my account ssh keys
59 # my account ssh keys
60 config.add_route(
60 config.add_route(
61 name='my_account_ssh_keys',
61 name='my_account_ssh_keys',
62 pattern=ADMIN_PREFIX + '/my_account/ssh_keys')
62 pattern=ADMIN_PREFIX + '/my_account/ssh_keys')
63 config.add_route(
63 config.add_route(
64 name='my_account_ssh_keys_generate',
64 name='my_account_ssh_keys_generate',
65 pattern=ADMIN_PREFIX + '/my_account/ssh_keys/generate')
65 pattern=ADMIN_PREFIX + '/my_account/ssh_keys/generate')
66 config.add_route(
66 config.add_route(
67 name='my_account_ssh_keys_add',
67 name='my_account_ssh_keys_add',
68 pattern=ADMIN_PREFIX + '/my_account/ssh_keys/new')
68 pattern=ADMIN_PREFIX + '/my_account/ssh_keys/new')
69 config.add_route(
69 config.add_route(
70 name='my_account_ssh_keys_delete',
70 name='my_account_ssh_keys_delete',
71 pattern=ADMIN_PREFIX + '/my_account/ssh_keys/delete')
71 pattern=ADMIN_PREFIX + '/my_account/ssh_keys/delete')
72
72
73 # my account user group membership
74 config.add_route(
75 name='my_account_user_group_membership',
76 pattern=ADMIN_PREFIX + '/my_account/user_group_membership')
77
73 # my account emails
78 # my account emails
74 config.add_route(
79 config.add_route(
75 name='my_account_emails',
80 name='my_account_emails',
76 pattern=ADMIN_PREFIX + '/my_account/emails')
81 pattern=ADMIN_PREFIX + '/my_account/emails')
77 config.add_route(
82 config.add_route(
78 name='my_account_emails_add',
83 name='my_account_emails_add',
79 pattern=ADMIN_PREFIX + '/my_account/emails/new')
84 pattern=ADMIN_PREFIX + '/my_account/emails/new')
80 config.add_route(
85 config.add_route(
81 name='my_account_emails_delete',
86 name='my_account_emails_delete',
82 pattern=ADMIN_PREFIX + '/my_account/emails/delete')
87 pattern=ADMIN_PREFIX + '/my_account/emails/delete')
83
88
84 config.add_route(
89 config.add_route(
85 name='my_account_repos',
90 name='my_account_repos',
86 pattern=ADMIN_PREFIX + '/my_account/repos')
91 pattern=ADMIN_PREFIX + '/my_account/repos')
87
92
88 config.add_route(
93 config.add_route(
89 name='my_account_watched',
94 name='my_account_watched',
90 pattern=ADMIN_PREFIX + '/my_account/watched')
95 pattern=ADMIN_PREFIX + '/my_account/watched')
91
96
92 config.add_route(
97 config.add_route(
93 name='my_account_perms',
98 name='my_account_perms',
94 pattern=ADMIN_PREFIX + '/my_account/perms')
99 pattern=ADMIN_PREFIX + '/my_account/perms')
95
100
96 config.add_route(
101 config.add_route(
97 name='my_account_notifications',
102 name='my_account_notifications',
98 pattern=ADMIN_PREFIX + '/my_account/notifications')
103 pattern=ADMIN_PREFIX + '/my_account/notifications')
99
104
100 config.add_route(
105 config.add_route(
101 name='my_account_notifications_toggle_visibility',
106 name='my_account_notifications_toggle_visibility',
102 pattern=ADMIN_PREFIX + '/my_account/toggle_visibility')
107 pattern=ADMIN_PREFIX + '/my_account/toggle_visibility')
103
108
104 # my account pull requests
109 # my account pull requests
105 config.add_route(
110 config.add_route(
106 name='my_account_pullrequests',
111 name='my_account_pullrequests',
107 pattern=ADMIN_PREFIX + '/my_account/pull_requests')
112 pattern=ADMIN_PREFIX + '/my_account/pull_requests')
108 config.add_route(
113 config.add_route(
109 name='my_account_pullrequests_data',
114 name='my_account_pullrequests_data',
110 pattern=ADMIN_PREFIX + '/my_account/pull_requests/data')
115 pattern=ADMIN_PREFIX + '/my_account/pull_requests/data')
111
116
112 # notifications
117 # notifications
113 config.add_route(
118 config.add_route(
114 name='notifications_show_all',
119 name='notifications_show_all',
115 pattern=ADMIN_PREFIX + '/notifications')
120 pattern=ADMIN_PREFIX + '/notifications')
116
121
117 # notifications
122 # notifications
118 config.add_route(
123 config.add_route(
119 name='notifications_mark_all_read',
124 name='notifications_mark_all_read',
120 pattern=ADMIN_PREFIX + '/notifications/mark_all_read')
125 pattern=ADMIN_PREFIX + '/notifications/mark_all_read')
121
126
122 config.add_route(
127 config.add_route(
123 name='notifications_show',
128 name='notifications_show',
124 pattern=ADMIN_PREFIX + '/notifications/{notification_id}')
129 pattern=ADMIN_PREFIX + '/notifications/{notification_id}')
125
130
126 config.add_route(
131 config.add_route(
127 name='notifications_update',
132 name='notifications_update',
128 pattern=ADMIN_PREFIX + '/notifications/{notification_id}/update')
133 pattern=ADMIN_PREFIX + '/notifications/{notification_id}/update')
129
134
130 config.add_route(
135 config.add_route(
131 name='notifications_delete',
136 name='notifications_delete',
132 pattern=ADMIN_PREFIX + '/notifications/{notification_id}/delete')
137 pattern=ADMIN_PREFIX + '/notifications/{notification_id}/delete')
133
138
134 # channelstream test
139 # channelstream test
135 config.add_route(
140 config.add_route(
136 name='my_account_notifications_test_channelstream',
141 name='my_account_notifications_test_channelstream',
137 pattern=ADMIN_PREFIX + '/my_account/test_channelstream')
142 pattern=ADMIN_PREFIX + '/my_account/test_channelstream')
138
143
139 # Scan module for configuration decorators.
144 # Scan module for configuration decorators.
140 config.scan('.views', ignore='.tests')
145 config.scan('.views', ignore='.tests')
@@ -1,585 +1,599 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2018 RhodeCode GmbH
3 # Copyright (C) 2016-2018 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import logging
21 import logging
22 import datetime
22 import datetime
23
23
24 import formencode
24 import formencode
25 import formencode.htmlfill
25 import formencode.htmlfill
26 from pyramid.httpexceptions import HTTPFound
26 from pyramid.httpexceptions import HTTPFound
27 from pyramid.view import view_config
27 from pyramid.view import view_config
28 from pyramid.renderers import render
28 from pyramid.renderers import render
29 from pyramid.response import Response
29 from pyramid.response import Response
30
30
31 from rhodecode.apps._base import BaseAppView, DataGridAppView
31 from rhodecode.apps._base import BaseAppView, DataGridAppView
32 from rhodecode import forms
32 from rhodecode import forms
33 from rhodecode.lib import helpers as h
33 from rhodecode.lib import helpers as h
34 from rhodecode.lib import audit_logger
34 from rhodecode.lib import audit_logger
35 from rhodecode.lib.ext_json import json
35 from rhodecode.lib.ext_json import json
36 from rhodecode.lib.auth import LoginRequired, NotAnonymous, CSRFRequired
36 from rhodecode.lib.auth import LoginRequired, NotAnonymous, CSRFRequired
37 from rhodecode.lib.channelstream import (
37 from rhodecode.lib.channelstream import (
38 channelstream_request, ChannelstreamException)
38 channelstream_request, ChannelstreamException)
39 from rhodecode.lib.utils2 import safe_int, md5, str2bool
39 from rhodecode.lib.utils2 import safe_int, md5, str2bool
40 from rhodecode.model.auth_token import AuthTokenModel
40 from rhodecode.model.auth_token import AuthTokenModel
41 from rhodecode.model.comment import CommentsModel
41 from rhodecode.model.comment import CommentsModel
42 from rhodecode.model.db import (
42 from rhodecode.model.db import (
43 Repository, UserEmailMap, UserApiKeys, UserFollowing, joinedload,
43 Repository, UserEmailMap, UserApiKeys, UserFollowing, joinedload,
44 PullRequest)
44 PullRequest)
45 from rhodecode.model.forms import UserForm, UserExtraEmailForm
45 from rhodecode.model.forms import UserForm, UserExtraEmailForm
46 from rhodecode.model.meta import Session
46 from rhodecode.model.meta import Session
47 from rhodecode.model.pull_request import PullRequestModel
47 from rhodecode.model.pull_request import PullRequestModel
48 from rhodecode.model.scm import RepoList
48 from rhodecode.model.scm import RepoList
49 from rhodecode.model.user import UserModel
49 from rhodecode.model.user import UserModel
50 from rhodecode.model.repo import RepoModel
50 from rhodecode.model.repo import RepoModel
51 from rhodecode.model.user_group import UserGroupModel
51 from rhodecode.model.validation_schema.schemas import user_schema
52 from rhodecode.model.validation_schema.schemas import user_schema
52
53
53 log = logging.getLogger(__name__)
54 log = logging.getLogger(__name__)
54
55
55
56
56 class MyAccountView(BaseAppView, DataGridAppView):
57 class MyAccountView(BaseAppView, DataGridAppView):
57 ALLOW_SCOPED_TOKENS = False
58 ALLOW_SCOPED_TOKENS = False
58 """
59 """
59 This view has alternative version inside EE, if modified please take a look
60 This view has alternative version inside EE, if modified please take a look
60 in there as well.
61 in there as well.
61 """
62 """
62
63
63 def load_default_context(self):
64 def load_default_context(self):
64 c = self._get_local_tmpl_context()
65 c = self._get_local_tmpl_context()
65 c.user = c.auth_user.get_instance()
66 c.user = c.auth_user.get_instance()
66 c.allow_scoped_tokens = self.ALLOW_SCOPED_TOKENS
67 c.allow_scoped_tokens = self.ALLOW_SCOPED_TOKENS
67
68
68 return c
69 return c
69
70
70 @LoginRequired()
71 @LoginRequired()
71 @NotAnonymous()
72 @NotAnonymous()
72 @view_config(
73 @view_config(
73 route_name='my_account_profile', request_method='GET',
74 route_name='my_account_profile', request_method='GET',
74 renderer='rhodecode:templates/admin/my_account/my_account.mako')
75 renderer='rhodecode:templates/admin/my_account/my_account.mako')
75 def my_account_profile(self):
76 def my_account_profile(self):
76 c = self.load_default_context()
77 c = self.load_default_context()
77 c.active = 'profile'
78 c.active = 'profile'
78 return self._get_template_context(c)
79 return self._get_template_context(c)
79
80
80 @LoginRequired()
81 @LoginRequired()
81 @NotAnonymous()
82 @NotAnonymous()
82 @view_config(
83 @view_config(
83 route_name='my_account_password', request_method='GET',
84 route_name='my_account_password', request_method='GET',
84 renderer='rhodecode:templates/admin/my_account/my_account.mako')
85 renderer='rhodecode:templates/admin/my_account/my_account.mako')
85 def my_account_password(self):
86 def my_account_password(self):
86 c = self.load_default_context()
87 c = self.load_default_context()
87 c.active = 'password'
88 c.active = 'password'
88 c.extern_type = c.user.extern_type
89 c.extern_type = c.user.extern_type
89
90
90 schema = user_schema.ChangePasswordSchema().bind(
91 schema = user_schema.ChangePasswordSchema().bind(
91 username=c.user.username)
92 username=c.user.username)
92
93
93 form = forms.Form(
94 form = forms.Form(
94 schema,
95 schema,
95 action=h.route_path('my_account_password_update'),
96 action=h.route_path('my_account_password_update'),
96 buttons=(forms.buttons.save, forms.buttons.reset))
97 buttons=(forms.buttons.save, forms.buttons.reset))
97
98
98 c.form = form
99 c.form = form
99 return self._get_template_context(c)
100 return self._get_template_context(c)
100
101
101 @LoginRequired()
102 @LoginRequired()
102 @NotAnonymous()
103 @NotAnonymous()
103 @CSRFRequired()
104 @CSRFRequired()
104 @view_config(
105 @view_config(
105 route_name='my_account_password_update', request_method='POST',
106 route_name='my_account_password_update', request_method='POST',
106 renderer='rhodecode:templates/admin/my_account/my_account.mako')
107 renderer='rhodecode:templates/admin/my_account/my_account.mako')
107 def my_account_password_update(self):
108 def my_account_password_update(self):
108 _ = self.request.translate
109 _ = self.request.translate
109 c = self.load_default_context()
110 c = self.load_default_context()
110 c.active = 'password'
111 c.active = 'password'
111 c.extern_type = c.user.extern_type
112 c.extern_type = c.user.extern_type
112
113
113 schema = user_schema.ChangePasswordSchema().bind(
114 schema = user_schema.ChangePasswordSchema().bind(
114 username=c.user.username)
115 username=c.user.username)
115
116
116 form = forms.Form(
117 form = forms.Form(
117 schema, buttons=(forms.buttons.save, forms.buttons.reset))
118 schema, buttons=(forms.buttons.save, forms.buttons.reset))
118
119
119 if c.extern_type != 'rhodecode':
120 if c.extern_type != 'rhodecode':
120 raise HTTPFound(self.request.route_path('my_account_password'))
121 raise HTTPFound(self.request.route_path('my_account_password'))
121
122
122 controls = self.request.POST.items()
123 controls = self.request.POST.items()
123 try:
124 try:
124 valid_data = form.validate(controls)
125 valid_data = form.validate(controls)
125 UserModel().update_user(c.user.user_id, **valid_data)
126 UserModel().update_user(c.user.user_id, **valid_data)
126 c.user.update_userdata(force_password_change=False)
127 c.user.update_userdata(force_password_change=False)
127 Session().commit()
128 Session().commit()
128 except forms.ValidationFailure as e:
129 except forms.ValidationFailure as e:
129 c.form = e
130 c.form = e
130 return self._get_template_context(c)
131 return self._get_template_context(c)
131
132
132 except Exception:
133 except Exception:
133 log.exception("Exception updating password")
134 log.exception("Exception updating password")
134 h.flash(_('Error occurred during update of user password'),
135 h.flash(_('Error occurred during update of user password'),
135 category='error')
136 category='error')
136 else:
137 else:
137 instance = c.auth_user.get_instance()
138 instance = c.auth_user.get_instance()
138 self.session.setdefault('rhodecode_user', {}).update(
139 self.session.setdefault('rhodecode_user', {}).update(
139 {'password': md5(instance.password)})
140 {'password': md5(instance.password)})
140 self.session.save()
141 self.session.save()
141 h.flash(_("Successfully updated password"), category='success')
142 h.flash(_("Successfully updated password"), category='success')
142
143
143 raise HTTPFound(self.request.route_path('my_account_password'))
144 raise HTTPFound(self.request.route_path('my_account_password'))
144
145
145 @LoginRequired()
146 @LoginRequired()
146 @NotAnonymous()
147 @NotAnonymous()
147 @view_config(
148 @view_config(
148 route_name='my_account_auth_tokens', request_method='GET',
149 route_name='my_account_auth_tokens', request_method='GET',
149 renderer='rhodecode:templates/admin/my_account/my_account.mako')
150 renderer='rhodecode:templates/admin/my_account/my_account.mako')
150 def my_account_auth_tokens(self):
151 def my_account_auth_tokens(self):
151 _ = self.request.translate
152 _ = self.request.translate
152
153
153 c = self.load_default_context()
154 c = self.load_default_context()
154 c.active = 'auth_tokens'
155 c.active = 'auth_tokens'
155 c.lifetime_values = AuthTokenModel.get_lifetime_values(translator=_)
156 c.lifetime_values = AuthTokenModel.get_lifetime_values(translator=_)
156 c.role_values = [
157 c.role_values = [
157 (x, AuthTokenModel.cls._get_role_name(x))
158 (x, AuthTokenModel.cls._get_role_name(x))
158 for x in AuthTokenModel.cls.ROLES]
159 for x in AuthTokenModel.cls.ROLES]
159 c.role_options = [(c.role_values, _("Role"))]
160 c.role_options = [(c.role_values, _("Role"))]
160 c.user_auth_tokens = AuthTokenModel().get_auth_tokens(
161 c.user_auth_tokens = AuthTokenModel().get_auth_tokens(
161 c.user.user_id, show_expired=True)
162 c.user.user_id, show_expired=True)
162 c.role_vcs = AuthTokenModel.cls.ROLE_VCS
163 c.role_vcs = AuthTokenModel.cls.ROLE_VCS
163 return self._get_template_context(c)
164 return self._get_template_context(c)
164
165
165 def maybe_attach_token_scope(self, token):
166 def maybe_attach_token_scope(self, token):
166 # implemented in EE edition
167 # implemented in EE edition
167 pass
168 pass
168
169
169 @LoginRequired()
170 @LoginRequired()
170 @NotAnonymous()
171 @NotAnonymous()
171 @CSRFRequired()
172 @CSRFRequired()
172 @view_config(
173 @view_config(
173 route_name='my_account_auth_tokens_add', request_method='POST',)
174 route_name='my_account_auth_tokens_add', request_method='POST',)
174 def my_account_auth_tokens_add(self):
175 def my_account_auth_tokens_add(self):
175 _ = self.request.translate
176 _ = self.request.translate
176 c = self.load_default_context()
177 c = self.load_default_context()
177
178
178 lifetime = safe_int(self.request.POST.get('lifetime'), -1)
179 lifetime = safe_int(self.request.POST.get('lifetime'), -1)
179 description = self.request.POST.get('description')
180 description = self.request.POST.get('description')
180 role = self.request.POST.get('role')
181 role = self.request.POST.get('role')
181
182
182 token = AuthTokenModel().create(
183 token = AuthTokenModel().create(
183 c.user.user_id, description, lifetime, role)
184 c.user.user_id, description, lifetime, role)
184 token_data = token.get_api_data()
185 token_data = token.get_api_data()
185
186
186 self.maybe_attach_token_scope(token)
187 self.maybe_attach_token_scope(token)
187 audit_logger.store_web(
188 audit_logger.store_web(
188 'user.edit.token.add', action_data={
189 'user.edit.token.add', action_data={
189 'data': {'token': token_data, 'user': 'self'}},
190 'data': {'token': token_data, 'user': 'self'}},
190 user=self._rhodecode_user, )
191 user=self._rhodecode_user, )
191 Session().commit()
192 Session().commit()
192
193
193 h.flash(_("Auth token successfully created"), category='success')
194 h.flash(_("Auth token successfully created"), category='success')
194 return HTTPFound(h.route_path('my_account_auth_tokens'))
195 return HTTPFound(h.route_path('my_account_auth_tokens'))
195
196
196 @LoginRequired()
197 @LoginRequired()
197 @NotAnonymous()
198 @NotAnonymous()
198 @CSRFRequired()
199 @CSRFRequired()
199 @view_config(
200 @view_config(
200 route_name='my_account_auth_tokens_delete', request_method='POST')
201 route_name='my_account_auth_tokens_delete', request_method='POST')
201 def my_account_auth_tokens_delete(self):
202 def my_account_auth_tokens_delete(self):
202 _ = self.request.translate
203 _ = self.request.translate
203 c = self.load_default_context()
204 c = self.load_default_context()
204
205
205 del_auth_token = self.request.POST.get('del_auth_token')
206 del_auth_token = self.request.POST.get('del_auth_token')
206
207
207 if del_auth_token:
208 if del_auth_token:
208 token = UserApiKeys.get_or_404(del_auth_token)
209 token = UserApiKeys.get_or_404(del_auth_token)
209 token_data = token.get_api_data()
210 token_data = token.get_api_data()
210
211
211 AuthTokenModel().delete(del_auth_token, c.user.user_id)
212 AuthTokenModel().delete(del_auth_token, c.user.user_id)
212 audit_logger.store_web(
213 audit_logger.store_web(
213 'user.edit.token.delete', action_data={
214 'user.edit.token.delete', action_data={
214 'data': {'token': token_data, 'user': 'self'}},
215 'data': {'token': token_data, 'user': 'self'}},
215 user=self._rhodecode_user,)
216 user=self._rhodecode_user,)
216 Session().commit()
217 Session().commit()
217 h.flash(_("Auth token successfully deleted"), category='success')
218 h.flash(_("Auth token successfully deleted"), category='success')
218
219
219 return HTTPFound(h.route_path('my_account_auth_tokens'))
220 return HTTPFound(h.route_path('my_account_auth_tokens'))
220
221
221 @LoginRequired()
222 @LoginRequired()
222 @NotAnonymous()
223 @NotAnonymous()
223 @view_config(
224 @view_config(
224 route_name='my_account_emails', request_method='GET',
225 route_name='my_account_emails', request_method='GET',
225 renderer='rhodecode:templates/admin/my_account/my_account.mako')
226 renderer='rhodecode:templates/admin/my_account/my_account.mako')
226 def my_account_emails(self):
227 def my_account_emails(self):
227 _ = self.request.translate
228 _ = self.request.translate
228
229
229 c = self.load_default_context()
230 c = self.load_default_context()
230 c.active = 'emails'
231 c.active = 'emails'
231
232
232 c.user_email_map = UserEmailMap.query()\
233 c.user_email_map = UserEmailMap.query()\
233 .filter(UserEmailMap.user == c.user).all()
234 .filter(UserEmailMap.user == c.user).all()
234 return self._get_template_context(c)
235 return self._get_template_context(c)
235
236
236 @LoginRequired()
237 @LoginRequired()
237 @NotAnonymous()
238 @NotAnonymous()
238 @CSRFRequired()
239 @CSRFRequired()
239 @view_config(
240 @view_config(
240 route_name='my_account_emails_add', request_method='POST')
241 route_name='my_account_emails_add', request_method='POST')
241 def my_account_emails_add(self):
242 def my_account_emails_add(self):
242 _ = self.request.translate
243 _ = self.request.translate
243 c = self.load_default_context()
244 c = self.load_default_context()
244
245
245 email = self.request.POST.get('new_email')
246 email = self.request.POST.get('new_email')
246
247
247 try:
248 try:
248 form = UserExtraEmailForm(self.request.translate)()
249 form = UserExtraEmailForm(self.request.translate)()
249 data = form.to_python({'email': email})
250 data = form.to_python({'email': email})
250 email = data['email']
251 email = data['email']
251
252
252 UserModel().add_extra_email(c.user.user_id, email)
253 UserModel().add_extra_email(c.user.user_id, email)
253 audit_logger.store_web(
254 audit_logger.store_web(
254 'user.edit.email.add', action_data={
255 'user.edit.email.add', action_data={
255 'data': {'email': email, 'user': 'self'}},
256 'data': {'email': email, 'user': 'self'}},
256 user=self._rhodecode_user,)
257 user=self._rhodecode_user,)
257
258
258 Session().commit()
259 Session().commit()
259 h.flash(_("Added new email address `%s` for user account") % email,
260 h.flash(_("Added new email address `%s` for user account") % email,
260 category='success')
261 category='success')
261 except formencode.Invalid as error:
262 except formencode.Invalid as error:
262 h.flash(h.escape(error.error_dict['email']), category='error')
263 h.flash(h.escape(error.error_dict['email']), category='error')
263 except Exception:
264 except Exception:
264 log.exception("Exception in my_account_emails")
265 log.exception("Exception in my_account_emails")
265 h.flash(_('An error occurred during email saving'),
266 h.flash(_('An error occurred during email saving'),
266 category='error')
267 category='error')
267 return HTTPFound(h.route_path('my_account_emails'))
268 return HTTPFound(h.route_path('my_account_emails'))
268
269
269 @LoginRequired()
270 @LoginRequired()
270 @NotAnonymous()
271 @NotAnonymous()
271 @CSRFRequired()
272 @CSRFRequired()
272 @view_config(
273 @view_config(
273 route_name='my_account_emails_delete', request_method='POST')
274 route_name='my_account_emails_delete', request_method='POST')
274 def my_account_emails_delete(self):
275 def my_account_emails_delete(self):
275 _ = self.request.translate
276 _ = self.request.translate
276 c = self.load_default_context()
277 c = self.load_default_context()
277
278
278 del_email_id = self.request.POST.get('del_email_id')
279 del_email_id = self.request.POST.get('del_email_id')
279 if del_email_id:
280 if del_email_id:
280 email = UserEmailMap.get_or_404(del_email_id).email
281 email = UserEmailMap.get_or_404(del_email_id).email
281 UserModel().delete_extra_email(c.user.user_id, del_email_id)
282 UserModel().delete_extra_email(c.user.user_id, del_email_id)
282 audit_logger.store_web(
283 audit_logger.store_web(
283 'user.edit.email.delete', action_data={
284 'user.edit.email.delete', action_data={
284 'data': {'email': email, 'user': 'self'}},
285 'data': {'email': email, 'user': 'self'}},
285 user=self._rhodecode_user,)
286 user=self._rhodecode_user,)
286 Session().commit()
287 Session().commit()
287 h.flash(_("Email successfully deleted"),
288 h.flash(_("Email successfully deleted"),
288 category='success')
289 category='success')
289 return HTTPFound(h.route_path('my_account_emails'))
290 return HTTPFound(h.route_path('my_account_emails'))
290
291
291 @LoginRequired()
292 @LoginRequired()
292 @NotAnonymous()
293 @NotAnonymous()
293 @CSRFRequired()
294 @CSRFRequired()
294 @view_config(
295 @view_config(
295 route_name='my_account_notifications_test_channelstream',
296 route_name='my_account_notifications_test_channelstream',
296 request_method='POST', renderer='json_ext')
297 request_method='POST', renderer='json_ext')
297 def my_account_notifications_test_channelstream(self):
298 def my_account_notifications_test_channelstream(self):
298 message = 'Test message sent via Channelstream by user: {}, on {}'.format(
299 message = 'Test message sent via Channelstream by user: {}, on {}'.format(
299 self._rhodecode_user.username, datetime.datetime.now())
300 self._rhodecode_user.username, datetime.datetime.now())
300 payload = {
301 payload = {
301 # 'channel': 'broadcast',
302 # 'channel': 'broadcast',
302 'type': 'message',
303 'type': 'message',
303 'timestamp': datetime.datetime.utcnow(),
304 'timestamp': datetime.datetime.utcnow(),
304 'user': 'system',
305 'user': 'system',
305 'pm_users': [self._rhodecode_user.username],
306 'pm_users': [self._rhodecode_user.username],
306 'message': {
307 'message': {
307 'message': message,
308 'message': message,
308 'level': 'info',
309 'level': 'info',
309 'topic': '/notifications'
310 'topic': '/notifications'
310 }
311 }
311 }
312 }
312
313
313 registry = self.request.registry
314 registry = self.request.registry
314 rhodecode_plugins = getattr(registry, 'rhodecode_plugins', {})
315 rhodecode_plugins = getattr(registry, 'rhodecode_plugins', {})
315 channelstream_config = rhodecode_plugins.get('channelstream', {})
316 channelstream_config = rhodecode_plugins.get('channelstream', {})
316
317
317 try:
318 try:
318 channelstream_request(channelstream_config, [payload], '/message')
319 channelstream_request(channelstream_config, [payload], '/message')
319 except ChannelstreamException as e:
320 except ChannelstreamException as e:
320 log.exception('Failed to send channelstream data')
321 log.exception('Failed to send channelstream data')
321 return {"response": 'ERROR: {}'.format(e.__class__.__name__)}
322 return {"response": 'ERROR: {}'.format(e.__class__.__name__)}
322 return {"response": 'Channelstream data sent. '
323 return {"response": 'Channelstream data sent. '
323 'You should see a new live message now.'}
324 'You should see a new live message now.'}
324
325
325 def _load_my_repos_data(self, watched=False):
326 def _load_my_repos_data(self, watched=False):
326 if watched:
327 if watched:
327 admin = False
328 admin = False
328 follows_repos = Session().query(UserFollowing)\
329 follows_repos = Session().query(UserFollowing)\
329 .filter(UserFollowing.user_id == self._rhodecode_user.user_id)\
330 .filter(UserFollowing.user_id == self._rhodecode_user.user_id)\
330 .options(joinedload(UserFollowing.follows_repository))\
331 .options(joinedload(UserFollowing.follows_repository))\
331 .all()
332 .all()
332 repo_list = [x.follows_repository for x in follows_repos]
333 repo_list = [x.follows_repository for x in follows_repos]
333 else:
334 else:
334 admin = True
335 admin = True
335 repo_list = Repository.get_all_repos(
336 repo_list = Repository.get_all_repos(
336 user_id=self._rhodecode_user.user_id)
337 user_id=self._rhodecode_user.user_id)
337 repo_list = RepoList(repo_list, perm_set=[
338 repo_list = RepoList(repo_list, perm_set=[
338 'repository.read', 'repository.write', 'repository.admin'])
339 'repository.read', 'repository.write', 'repository.admin'])
339
340
340 repos_data = RepoModel().get_repos_as_dict(
341 repos_data = RepoModel().get_repos_as_dict(
341 repo_list=repo_list, admin=admin)
342 repo_list=repo_list, admin=admin)
342 # json used to render the grid
343 # json used to render the grid
343 return json.dumps(repos_data)
344 return json.dumps(repos_data)
344
345
345 @LoginRequired()
346 @LoginRequired()
346 @NotAnonymous()
347 @NotAnonymous()
347 @view_config(
348 @view_config(
348 route_name='my_account_repos', request_method='GET',
349 route_name='my_account_repos', request_method='GET',
349 renderer='rhodecode:templates/admin/my_account/my_account.mako')
350 renderer='rhodecode:templates/admin/my_account/my_account.mako')
350 def my_account_repos(self):
351 def my_account_repos(self):
351 c = self.load_default_context()
352 c = self.load_default_context()
352 c.active = 'repos'
353 c.active = 'repos'
353
354
354 # json used to render the grid
355 # json used to render the grid
355 c.data = self._load_my_repos_data()
356 c.data = self._load_my_repos_data()
356 return self._get_template_context(c)
357 return self._get_template_context(c)
357
358
358 @LoginRequired()
359 @LoginRequired()
359 @NotAnonymous()
360 @NotAnonymous()
360 @view_config(
361 @view_config(
361 route_name='my_account_watched', request_method='GET',
362 route_name='my_account_watched', request_method='GET',
362 renderer='rhodecode:templates/admin/my_account/my_account.mako')
363 renderer='rhodecode:templates/admin/my_account/my_account.mako')
363 def my_account_watched(self):
364 def my_account_watched(self):
364 c = self.load_default_context()
365 c = self.load_default_context()
365 c.active = 'watched'
366 c.active = 'watched'
366
367
367 # json used to render the grid
368 # json used to render the grid
368 c.data = self._load_my_repos_data(watched=True)
369 c.data = self._load_my_repos_data(watched=True)
369 return self._get_template_context(c)
370 return self._get_template_context(c)
370
371
371 @LoginRequired()
372 @LoginRequired()
372 @NotAnonymous()
373 @NotAnonymous()
373 @view_config(
374 @view_config(
374 route_name='my_account_perms', request_method='GET',
375 route_name='my_account_perms', request_method='GET',
375 renderer='rhodecode:templates/admin/my_account/my_account.mako')
376 renderer='rhodecode:templates/admin/my_account/my_account.mako')
376 def my_account_perms(self):
377 def my_account_perms(self):
377 c = self.load_default_context()
378 c = self.load_default_context()
378 c.active = 'perms'
379 c.active = 'perms'
379
380
380 c.perm_user = c.auth_user
381 c.perm_user = c.auth_user
381 return self._get_template_context(c)
382 return self._get_template_context(c)
382
383
383 @LoginRequired()
384 @LoginRequired()
384 @NotAnonymous()
385 @NotAnonymous()
385 @view_config(
386 @view_config(
386 route_name='my_account_notifications', request_method='GET',
387 route_name='my_account_notifications', request_method='GET',
387 renderer='rhodecode:templates/admin/my_account/my_account.mako')
388 renderer='rhodecode:templates/admin/my_account/my_account.mako')
388 def my_notifications(self):
389 def my_notifications(self):
389 c = self.load_default_context()
390 c = self.load_default_context()
390 c.active = 'notifications'
391 c.active = 'notifications'
391
392
392 return self._get_template_context(c)
393 return self._get_template_context(c)
393
394
394 @LoginRequired()
395 @LoginRequired()
395 @NotAnonymous()
396 @NotAnonymous()
396 @CSRFRequired()
397 @CSRFRequired()
397 @view_config(
398 @view_config(
398 route_name='my_account_notifications_toggle_visibility',
399 route_name='my_account_notifications_toggle_visibility',
399 request_method='POST', renderer='json_ext')
400 request_method='POST', renderer='json_ext')
400 def my_notifications_toggle_visibility(self):
401 def my_notifications_toggle_visibility(self):
401 user = self._rhodecode_db_user
402 user = self._rhodecode_db_user
402 new_status = not user.user_data.get('notification_status', True)
403 new_status = not user.user_data.get('notification_status', True)
403 user.update_userdata(notification_status=new_status)
404 user.update_userdata(notification_status=new_status)
404 Session().commit()
405 Session().commit()
405 return user.user_data['notification_status']
406 return user.user_data['notification_status']
406
407
407 @LoginRequired()
408 @LoginRequired()
408 @NotAnonymous()
409 @NotAnonymous()
409 @view_config(
410 @view_config(
410 route_name='my_account_edit',
411 route_name='my_account_edit',
411 request_method='GET',
412 request_method='GET',
412 renderer='rhodecode:templates/admin/my_account/my_account.mako')
413 renderer='rhodecode:templates/admin/my_account/my_account.mako')
413 def my_account_edit(self):
414 def my_account_edit(self):
414 c = self.load_default_context()
415 c = self.load_default_context()
415 c.active = 'profile_edit'
416 c.active = 'profile_edit'
416
417
417 c.perm_user = c.auth_user
418 c.perm_user = c.auth_user
418 c.extern_type = c.user.extern_type
419 c.extern_type = c.user.extern_type
419 c.extern_name = c.user.extern_name
420 c.extern_name = c.user.extern_name
420
421
421 defaults = c.user.get_dict()
422 defaults = c.user.get_dict()
422
423
423 data = render('rhodecode:templates/admin/my_account/my_account.mako',
424 data = render('rhodecode:templates/admin/my_account/my_account.mako',
424 self._get_template_context(c), self.request)
425 self._get_template_context(c), self.request)
425 html = formencode.htmlfill.render(
426 html = formencode.htmlfill.render(
426 data,
427 data,
427 defaults=defaults,
428 defaults=defaults,
428 encoding="UTF-8",
429 encoding="UTF-8",
429 force_defaults=False
430 force_defaults=False
430 )
431 )
431 return Response(html)
432 return Response(html)
432
433
433 @LoginRequired()
434 @LoginRequired()
434 @NotAnonymous()
435 @NotAnonymous()
435 @CSRFRequired()
436 @CSRFRequired()
436 @view_config(
437 @view_config(
437 route_name='my_account_update',
438 route_name='my_account_update',
438 request_method='POST',
439 request_method='POST',
439 renderer='rhodecode:templates/admin/my_account/my_account.mako')
440 renderer='rhodecode:templates/admin/my_account/my_account.mako')
440 def my_account_update(self):
441 def my_account_update(self):
441 _ = self.request.translate
442 _ = self.request.translate
442 c = self.load_default_context()
443 c = self.load_default_context()
443 c.active = 'profile_edit'
444 c.active = 'profile_edit'
444
445
445 c.perm_user = c.auth_user
446 c.perm_user = c.auth_user
446 c.extern_type = c.user.extern_type
447 c.extern_type = c.user.extern_type
447 c.extern_name = c.user.extern_name
448 c.extern_name = c.user.extern_name
448
449
449 _form = UserForm(self.request.translate, edit=True,
450 _form = UserForm(self.request.translate, edit=True,
450 old_data={'user_id': self._rhodecode_user.user_id,
451 old_data={'user_id': self._rhodecode_user.user_id,
451 'email': self._rhodecode_user.email})()
452 'email': self._rhodecode_user.email})()
452 form_result = {}
453 form_result = {}
453 try:
454 try:
454 post_data = dict(self.request.POST)
455 post_data = dict(self.request.POST)
455 post_data['new_password'] = ''
456 post_data['new_password'] = ''
456 post_data['password_confirmation'] = ''
457 post_data['password_confirmation'] = ''
457 form_result = _form.to_python(post_data)
458 form_result = _form.to_python(post_data)
458 # skip updating those attrs for my account
459 # skip updating those attrs for my account
459 skip_attrs = ['admin', 'active', 'extern_type', 'extern_name',
460 skip_attrs = ['admin', 'active', 'extern_type', 'extern_name',
460 'new_password', 'password_confirmation']
461 'new_password', 'password_confirmation']
461 # TODO: plugin should define if username can be updated
462 # TODO: plugin should define if username can be updated
462 if c.extern_type != "rhodecode":
463 if c.extern_type != "rhodecode":
463 # forbid updating username for external accounts
464 # forbid updating username for external accounts
464 skip_attrs.append('username')
465 skip_attrs.append('username')
465
466
466 UserModel().update_user(
467 UserModel().update_user(
467 self._rhodecode_user.user_id, skip_attrs=skip_attrs,
468 self._rhodecode_user.user_id, skip_attrs=skip_attrs,
468 **form_result)
469 **form_result)
469 h.flash(_('Your account was updated successfully'),
470 h.flash(_('Your account was updated successfully'),
470 category='success')
471 category='success')
471 Session().commit()
472 Session().commit()
472
473
473 except formencode.Invalid as errors:
474 except formencode.Invalid as errors:
474 data = render(
475 data = render(
475 'rhodecode:templates/admin/my_account/my_account.mako',
476 'rhodecode:templates/admin/my_account/my_account.mako',
476 self._get_template_context(c), self.request)
477 self._get_template_context(c), self.request)
477
478
478 html = formencode.htmlfill.render(
479 html = formencode.htmlfill.render(
479 data,
480 data,
480 defaults=errors.value,
481 defaults=errors.value,
481 errors=errors.error_dict or {},
482 errors=errors.error_dict or {},
482 prefix_error=False,
483 prefix_error=False,
483 encoding="UTF-8",
484 encoding="UTF-8",
484 force_defaults=False)
485 force_defaults=False)
485 return Response(html)
486 return Response(html)
486
487
487 except Exception:
488 except Exception:
488 log.exception("Exception updating user")
489 log.exception("Exception updating user")
489 h.flash(_('Error occurred during update of user %s')
490 h.flash(_('Error occurred during update of user %s')
490 % form_result.get('username'), category='error')
491 % form_result.get('username'), category='error')
491 raise HTTPFound(h.route_path('my_account_profile'))
492 raise HTTPFound(h.route_path('my_account_profile'))
492
493
493 raise HTTPFound(h.route_path('my_account_profile'))
494 raise HTTPFound(h.route_path('my_account_profile'))
494
495
495 def _get_pull_requests_list(self, statuses):
496 def _get_pull_requests_list(self, statuses):
496 draw, start, limit = self._extract_chunk(self.request)
497 draw, start, limit = self._extract_chunk(self.request)
497 search_q, order_by, order_dir = self._extract_ordering(self.request)
498 search_q, order_by, order_dir = self._extract_ordering(self.request)
498 _render = self.request.get_partial_renderer(
499 _render = self.request.get_partial_renderer(
499 'rhodecode:templates/data_table/_dt_elements.mako')
500 'rhodecode:templates/data_table/_dt_elements.mako')
500
501
501 pull_requests = PullRequestModel().get_im_participating_in(
502 pull_requests = PullRequestModel().get_im_participating_in(
502 user_id=self._rhodecode_user.user_id,
503 user_id=self._rhodecode_user.user_id,
503 statuses=statuses,
504 statuses=statuses,
504 offset=start, length=limit, order_by=order_by,
505 offset=start, length=limit, order_by=order_by,
505 order_dir=order_dir)
506 order_dir=order_dir)
506
507
507 pull_requests_total_count = PullRequestModel().count_im_participating_in(
508 pull_requests_total_count = PullRequestModel().count_im_participating_in(
508 user_id=self._rhodecode_user.user_id, statuses=statuses)
509 user_id=self._rhodecode_user.user_id, statuses=statuses)
509
510
510 data = []
511 data = []
511 comments_model = CommentsModel()
512 comments_model = CommentsModel()
512 for pr in pull_requests:
513 for pr in pull_requests:
513 repo_id = pr.target_repo_id
514 repo_id = pr.target_repo_id
514 comments = comments_model.get_all_comments(
515 comments = comments_model.get_all_comments(
515 repo_id, pull_request=pr)
516 repo_id, pull_request=pr)
516 owned = pr.user_id == self._rhodecode_user.user_id
517 owned = pr.user_id == self._rhodecode_user.user_id
517
518
518 data.append({
519 data.append({
519 'target_repo': _render('pullrequest_target_repo',
520 'target_repo': _render('pullrequest_target_repo',
520 pr.target_repo.repo_name),
521 pr.target_repo.repo_name),
521 'name': _render('pullrequest_name',
522 'name': _render('pullrequest_name',
522 pr.pull_request_id, pr.target_repo.repo_name,
523 pr.pull_request_id, pr.target_repo.repo_name,
523 short=True),
524 short=True),
524 'name_raw': pr.pull_request_id,
525 'name_raw': pr.pull_request_id,
525 'status': _render('pullrequest_status',
526 'status': _render('pullrequest_status',
526 pr.calculated_review_status()),
527 pr.calculated_review_status()),
527 'title': _render(
528 'title': _render(
528 'pullrequest_title', pr.title, pr.description),
529 'pullrequest_title', pr.title, pr.description),
529 'description': h.escape(pr.description),
530 'description': h.escape(pr.description),
530 'updated_on': _render('pullrequest_updated_on',
531 'updated_on': _render('pullrequest_updated_on',
531 h.datetime_to_time(pr.updated_on)),
532 h.datetime_to_time(pr.updated_on)),
532 'updated_on_raw': h.datetime_to_time(pr.updated_on),
533 'updated_on_raw': h.datetime_to_time(pr.updated_on),
533 'created_on': _render('pullrequest_updated_on',
534 'created_on': _render('pullrequest_updated_on',
534 h.datetime_to_time(pr.created_on)),
535 h.datetime_to_time(pr.created_on)),
535 'created_on_raw': h.datetime_to_time(pr.created_on),
536 'created_on_raw': h.datetime_to_time(pr.created_on),
536 'author': _render('pullrequest_author',
537 'author': _render('pullrequest_author',
537 pr.author.full_contact, ),
538 pr.author.full_contact, ),
538 'author_raw': pr.author.full_name,
539 'author_raw': pr.author.full_name,
539 'comments': _render('pullrequest_comments', len(comments)),
540 'comments': _render('pullrequest_comments', len(comments)),
540 'comments_raw': len(comments),
541 'comments_raw': len(comments),
541 'closed': pr.is_closed(),
542 'closed': pr.is_closed(),
542 'owned': owned
543 'owned': owned
543 })
544 })
544
545
545 # json used to render the grid
546 # json used to render the grid
546 data = ({
547 data = ({
547 'draw': draw,
548 'draw': draw,
548 'data': data,
549 'data': data,
549 'recordsTotal': pull_requests_total_count,
550 'recordsTotal': pull_requests_total_count,
550 'recordsFiltered': pull_requests_total_count,
551 'recordsFiltered': pull_requests_total_count,
551 })
552 })
552 return data
553 return data
553
554
554 @LoginRequired()
555 @LoginRequired()
555 @NotAnonymous()
556 @NotAnonymous()
556 @view_config(
557 @view_config(
557 route_name='my_account_pullrequests',
558 route_name='my_account_pullrequests',
558 request_method='GET',
559 request_method='GET',
559 renderer='rhodecode:templates/admin/my_account/my_account.mako')
560 renderer='rhodecode:templates/admin/my_account/my_account.mako')
560 def my_account_pullrequests(self):
561 def my_account_pullrequests(self):
561 c = self.load_default_context()
562 c = self.load_default_context()
562 c.active = 'pullrequests'
563 c.active = 'pullrequests'
563 req_get = self.request.GET
564 req_get = self.request.GET
564
565
565 c.closed = str2bool(req_get.get('pr_show_closed'))
566 c.closed = str2bool(req_get.get('pr_show_closed'))
566
567
567 return self._get_template_context(c)
568 return self._get_template_context(c)
568
569
569 @LoginRequired()
570 @LoginRequired()
570 @NotAnonymous()
571 @NotAnonymous()
571 @view_config(
572 @view_config(
572 route_name='my_account_pullrequests_data',
573 route_name='my_account_pullrequests_data',
573 request_method='GET', renderer='json_ext')
574 request_method='GET', renderer='json_ext')
574 def my_account_pullrequests_data(self):
575 def my_account_pullrequests_data(self):
575 self.load_default_context()
576 self.load_default_context()
576 req_get = self.request.GET
577 req_get = self.request.GET
577 closed = str2bool(req_get.get('closed'))
578 closed = str2bool(req_get.get('closed'))
578
579
579 statuses = [PullRequest.STATUS_NEW, PullRequest.STATUS_OPEN]
580 statuses = [PullRequest.STATUS_NEW, PullRequest.STATUS_OPEN]
580 if closed:
581 if closed:
581 statuses += [PullRequest.STATUS_CLOSED]
582 statuses += [PullRequest.STATUS_CLOSED]
582
583
583 data = self._get_pull_requests_list(statuses=statuses)
584 data = self._get_pull_requests_list(statuses=statuses)
584 return data
585 return data
585
586
587 @LoginRequired()
588 @NotAnonymous()
589 @view_config(
590 route_name='my_account_user_group_membership',
591 request_method='GET',
592 renderer='rhodecode:templates/admin/my_account/my_account.mako')
593 def my_account_user_group_membership(self):
594 c = self.load_default_context()
595 c.active = 'user_group_membership'
596 groups = [UserGroupModel.get_user_groups_as_dict(group.users_group)
597 for group in self._rhodecode_db_user.group_member]
598 c.user_groups = json.dumps(groups)
599 return self._get_template_context(c)
@@ -1,53 +1,55 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2 <%inherit file="/base/base.mako"/>
2 <%inherit file="/base/base.mako"/>
3
3
4 <%def name="title()">
4 <%def name="title()">
5 ${_('My account')} ${c.rhodecode_user.username}
5 ${_('My account')} ${c.rhodecode_user.username}
6 %if c.rhodecode_name:
6 %if c.rhodecode_name:
7 &middot; ${h.branding(c.rhodecode_name)}
7 &middot; ${h.branding(c.rhodecode_name)}
8 %endif
8 %endif
9 </%def>
9 </%def>
10
10
11 <%def name="breadcrumbs_links()">
11 <%def name="breadcrumbs_links()">
12 ${_('My Account')}
12 ${_('My Account')}
13 </%def>
13 </%def>
14
14
15 <%def name="menu_bar_nav()">
15 <%def name="menu_bar_nav()">
16 ${self.menu_items(active='my_account')}
16 ${self.menu_items(active='my_account')}
17 </%def>
17 </%def>
18
18
19 <%def name="main()">
19 <%def name="main()">
20 <div class="box">
20 <div class="box">
21 <div class="title">
21 <div class="title">
22 ${self.breadcrumbs()}
22 ${self.breadcrumbs()}
23 </div>
23 </div>
24
24
25 <div class="sidebar-col-wrapper scw-small">
25 <div class="sidebar-col-wrapper scw-small">
26 ##main
26 ##main
27 <div class="sidebar">
27 <div class="sidebar">
28 <ul class="nav nav-pills nav-stacked">
28 <ul class="nav nav-pills nav-stacked">
29 <li class="${'active' if c.active=='profile' or c.active=='profile_edit' else ''}"><a href="${h.route_path('my_account_profile')}">${_('Profile')}</a></li>
29 <li class="${'active' if c.active=='profile' or c.active=='profile_edit' else ''}"><a href="${h.route_path('my_account_profile')}">${_('Profile')}</a></li>
30 <li class="${'active' if c.active=='password' else ''}"><a href="${h.route_path('my_account_password')}">${_('Password')}</a></li>
30 <li class="${'active' if c.active=='password' else ''}"><a href="${h.route_path('my_account_password')}">${_('Password')}</a></li>
31 <li class="${'active' if c.active=='auth_tokens' else ''}"><a href="${h.route_path('my_account_auth_tokens')}">${_('Auth Tokens')}</a></li>
31 <li class="${'active' if c.active=='auth_tokens' else ''}"><a href="${h.route_path('my_account_auth_tokens')}">${_('Auth Tokens')}</a></li>
32 <li class="${'active' if c.active in ['ssh_keys', 'ssh_keys_generate'] else ''}"><a href="${h.route_path('my_account_ssh_keys')}">${_('SSH Keys')}</a></li>
32 <li class="${'active' if c.active in ['ssh_keys', 'ssh_keys_generate'] else ''}"><a href="${h.route_path('my_account_ssh_keys')}">${_('SSH Keys')}</a></li>
33 <li class="${'active' if c.active=='user_group_membership' else ''}"><a href="${h.route_path('my_account_user_group_membership')}">${_('User Group Membership')}</a></li>
34
33 ## TODO: Find a better integration of oauth views into navigation.
35 ## TODO: Find a better integration of oauth views into navigation.
34 <% my_account_oauth_url = h.route_path_or_none('my_account_oauth') %>
36 <% my_account_oauth_url = h.route_path_or_none('my_account_oauth') %>
35 % if my_account_oauth_url:
37 % if my_account_oauth_url:
36 <li class="${'active' if c.active=='oauth' else ''}"><a href="${my_account_oauth_url}">${_('OAuth Identities')}</a></li>
38 <li class="${'active' if c.active=='oauth' else ''}"><a href="${my_account_oauth_url}">${_('OAuth Identities')}</a></li>
37 % endif
39 % endif
38 <li class="${'active' if c.active=='emails' else ''}"><a href="${h.route_path('my_account_emails')}">${_('Emails')}</a></li>
40 <li class="${'active' if c.active=='emails' else ''}"><a href="${h.route_path('my_account_emails')}">${_('Emails')}</a></li>
39 <li class="${'active' if c.active=='repos' else ''}"><a href="${h.route_path('my_account_repos')}">${_('Repositories')}</a></li>
41 <li class="${'active' if c.active=='repos' else ''}"><a href="${h.route_path('my_account_repos')}">${_('Repositories')}</a></li>
40 <li class="${'active' if c.active=='watched' else ''}"><a href="${h.route_path('my_account_watched')}">${_('Watched')}</a></li>
42 <li class="${'active' if c.active=='watched' else ''}"><a href="${h.route_path('my_account_watched')}">${_('Watched')}</a></li>
41 <li class="${'active' if c.active=='pullrequests' else ''}"><a href="${h.route_path('my_account_pullrequests')}">${_('Pull Requests')}</a></li>
43 <li class="${'active' if c.active=='pullrequests' else ''}"><a href="${h.route_path('my_account_pullrequests')}">${_('Pull Requests')}</a></li>
42 <li class="${'active' if c.active=='perms' else ''}"><a href="${h.route_path('my_account_perms')}">${_('Permissions')}</a></li>
44 <li class="${'active' if c.active=='perms' else ''}"><a href="${h.route_path('my_account_perms')}">${_('Permissions')}</a></li>
43 <li class="${'active' if c.active=='my_notifications' else ''}"><a href="${h.route_path('my_account_notifications')}">${_('Live Notifications')}</a></li>
45 <li class="${'active' if c.active=='my_notifications' else ''}"><a href="${h.route_path('my_account_notifications')}">${_('Live Notifications')}</a></li>
44 </ul>
46 </ul>
45 </div>
47 </div>
46
48
47 <div class="main-content-full-width">
49 <div class="main-content-full-width">
48 <%include file="/admin/my_account/my_account_${c.active}.mako"/>
50 <%include file="/admin/my_account/my_account_${c.active}.mako"/>
49 </div>
51 </div>
50 </div>
52 </div>
51 </div>
53 </div>
52
54
53 </%def>
55 </%def>
General Comments 0
You need to be logged in to leave comments. Login now