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 | · ${h.branding(c.rhodecode_name)} |
|
7 | · ${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