##// END OF EJS Templates
my-account: moved emails config into pyramid views.
marcink -
r1816:7c5e9070 default
parent child Browse files
Show More
@@ -0,0 +1,93 b''
1 # -*- coding: utf-8 -*-
2
3 # Copyright (C) 2010-2017 RhodeCode GmbH
4 #
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
7 # (only), as published by the Free Software Foundation.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
13 #
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/>.
16 #
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
21 import pytest
22
23 from rhodecode.apps._base import ADMIN_PREFIX
24 from rhodecode.model.db import User, UserEmailMap
25 from rhodecode.tests import (
26 TestController, TEST_USER_ADMIN_LOGIN, TEST_USER_REGULAR_EMAIL,
27 assert_session_flash)
28 from rhodecode.tests.fixture import Fixture
29
30 fixture = Fixture()
31
32
33 def route_path(name, **kwargs):
34 return {
35 'my_account_emails':
36 ADMIN_PREFIX + '/my_account/emails',
37 'my_account_emails_add':
38 ADMIN_PREFIX + '/my_account/emails/new',
39 'my_account_emails_delete':
40 ADMIN_PREFIX + '/my_account/emails/delete',
41 }[name].format(**kwargs)
42
43
44 class TestMyAccountEmails(TestController):
45 def test_my_account_my_emails(self):
46 self.log_user()
47 response = self.app.get(route_path('my_account_emails'))
48 response.mustcontain('No additional emails specified')
49
50 def test_my_account_my_emails_add_existing_email(self):
51 self.log_user()
52 response = self.app.get(route_path('my_account_emails'))
53 response.mustcontain('No additional emails specified')
54 response = self.app.post(route_path('my_account_emails_add'),
55 {'new_email': TEST_USER_REGULAR_EMAIL,
56 'csrf_token': self.csrf_token})
57 assert_session_flash(response, 'This e-mail address is already taken')
58
59 def test_my_account_my_emails_add_mising_email_in_form(self):
60 self.log_user()
61 response = self.app.get(route_path('my_account_emails'))
62 response.mustcontain('No additional emails specified')
63 response = self.app.post(route_path('my_account_emails_add'),
64 {'csrf_token': self.csrf_token})
65 assert_session_flash(response, 'Please enter an email address')
66
67 def test_my_account_my_emails_add_remove(self):
68 self.log_user()
69 response = self.app.get(route_path('my_account_emails'))
70 response.mustcontain('No additional emails specified')
71
72 response = self.app.post(route_path('my_account_emails_add'),
73 {'new_email': 'foo@barz.com',
74 'csrf_token': self.csrf_token})
75
76 response = self.app.get(route_path('my_account_emails'))
77
78 email_id = UserEmailMap.query().filter(
79 UserEmailMap.user == User.get_by_username(
80 TEST_USER_ADMIN_LOGIN)).filter(
81 UserEmailMap.email == 'foo@barz.com').one().email_id
82
83 response.mustcontain('foo@barz.com')
84 response.mustcontain('<input id="del_email_id" name="del_email_id" '
85 'type="hidden" value="%s" />' % email_id)
86
87 response = self.app.post(
88 route_path('my_account_emails_delete'), {
89 'del_email_id': email_id,
90 'csrf_token': self.csrf_token})
91 assert_session_flash(response, 'Email successfully deleted')
92 response = self.app.get(route_path('my_account_emails'))
93 response.mustcontain('No additional emails specified')
@@ -1,55 +1,65 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2017 RhodeCode GmbH
3 # Copyright (C) 2016-2017 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 config.add_route(
31 config.add_route(
32 name='my_account_password',
32 name='my_account_password',
33 pattern=ADMIN_PREFIX + '/my_account/password')
33 pattern=ADMIN_PREFIX + '/my_account/password')
34
34
35 config.add_route(
35 config.add_route(
36 name='my_account_password_update',
36 name='my_account_password_update',
37 pattern=ADMIN_PREFIX + '/my_account/password')
37 pattern=ADMIN_PREFIX + '/my_account/password')
38
38
39 config.add_route(
39 config.add_route(
40 name='my_account_auth_tokens',
40 name='my_account_auth_tokens',
41 pattern=ADMIN_PREFIX + '/my_account/auth_tokens')
41 pattern=ADMIN_PREFIX + '/my_account/auth_tokens')
42 config.add_route(
42 config.add_route(
43 name='my_account_auth_tokens_add',
43 name='my_account_auth_tokens_add',
44 pattern=ADMIN_PREFIX + '/my_account/auth_tokens/new')
44 pattern=ADMIN_PREFIX + '/my_account/auth_tokens/new')
45 config.add_route(
45 config.add_route(
46 name='my_account_auth_tokens_delete',
46 name='my_account_auth_tokens_delete',
47 pattern=ADMIN_PREFIX + '/my_account/auth_tokens/delete')
47 pattern=ADMIN_PREFIX + '/my_account/auth_tokens/delete')
48
48
49 config.add_route(
50 name='my_account_emails',
51 pattern=ADMIN_PREFIX + '/my_account/emails')
52 config.add_route(
53 name='my_account_emails_add',
54 pattern=ADMIN_PREFIX + '/my_account/emails/new')
55 config.add_route(
56 name='my_account_emails_delete',
57 pattern=ADMIN_PREFIX + '/my_account/emails/delete')
58
49 # channelstream test
59 # channelstream test
50 config.add_route(
60 config.add_route(
51 name='my_account_notifications_test_channelstream',
61 name='my_account_notifications_test_channelstream',
52 pattern=ADMIN_PREFIX + '/my_account/test_channelstream')
62 pattern=ADMIN_PREFIX + '/my_account/test_channelstream')
53
63
54 # Scan module for configuration decorators.
64 # Scan module for configuration decorators.
55 config.scan()
65 config.scan()
@@ -1,231 +1,292 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2017 RhodeCode GmbH
3 # Copyright (C) 2016-2017 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 from pyramid.httpexceptions import HTTPFound
25 from pyramid.httpexceptions import HTTPFound
25 from pyramid.view import view_config
26 from pyramid.view import view_config
26
27
27 from rhodecode.apps._base import BaseAppView
28 from rhodecode.apps._base import BaseAppView
28 from rhodecode import forms
29 from rhodecode import forms
29 from rhodecode.lib import helpers as h
30 from rhodecode.lib import helpers as h
30 from rhodecode.lib.auth import LoginRequired, NotAnonymous, CSRFRequired
31 from rhodecode.lib.auth import LoginRequired, NotAnonymous, CSRFRequired
31 from rhodecode.lib.channelstream import channelstream_request, \
32 from rhodecode.lib.channelstream import channelstream_request, \
32 ChannelstreamException
33 ChannelstreamException
33 from rhodecode.lib.utils2 import safe_int, md5
34 from rhodecode.lib.utils2 import safe_int, md5
34 from rhodecode.model.auth_token import AuthTokenModel
35 from rhodecode.model.auth_token import AuthTokenModel
36 from rhodecode.model.db import UserEmailMap
35 from rhodecode.model.meta import Session
37 from rhodecode.model.meta import Session
36 from rhodecode.model.user import UserModel
38 from rhodecode.model.user import UserModel
37 from rhodecode.model.validation_schema.schemas import user_schema
39 from rhodecode.model.validation_schema.schemas import user_schema
38
40
39 log = logging.getLogger(__name__)
41 log = logging.getLogger(__name__)
40
42
41
43
42 class MyAccountView(BaseAppView):
44 class MyAccountView(BaseAppView):
43 ALLOW_SCOPED_TOKENS = False
45 ALLOW_SCOPED_TOKENS = False
44 """
46 """
45 This view has alternative version inside EE, if modified please take a look
47 This view has alternative version inside EE, if modified please take a look
46 in there as well.
48 in there as well.
47 """
49 """
48
50
49 def load_default_context(self):
51 def load_default_context(self):
50 c = self._get_local_tmpl_context()
52 c = self._get_local_tmpl_context()
51 c.user = c.auth_user.get_instance()
53 c.user = c.auth_user.get_instance()
52 c.allow_scoped_tokens = self.ALLOW_SCOPED_TOKENS
54 c.allow_scoped_tokens = self.ALLOW_SCOPED_TOKENS
53 self._register_global_c(c)
55 self._register_global_c(c)
54 return c
56 return c
55
57
56 @LoginRequired()
58 @LoginRequired()
57 @NotAnonymous()
59 @NotAnonymous()
58 @view_config(
60 @view_config(
59 route_name='my_account_profile', request_method='GET',
61 route_name='my_account_profile', request_method='GET',
60 renderer='rhodecode:templates/admin/my_account/my_account.mako')
62 renderer='rhodecode:templates/admin/my_account/my_account.mako')
61 def my_account_profile(self):
63 def my_account_profile(self):
62 c = self.load_default_context()
64 c = self.load_default_context()
63 c.active = 'profile'
65 c.active = 'profile'
64 return self._get_template_context(c)
66 return self._get_template_context(c)
65
67
66 @LoginRequired()
68 @LoginRequired()
67 @NotAnonymous()
69 @NotAnonymous()
68 @view_config(
70 @view_config(
69 route_name='my_account_password', request_method='GET',
71 route_name='my_account_password', request_method='GET',
70 renderer='rhodecode:templates/admin/my_account/my_account.mako')
72 renderer='rhodecode:templates/admin/my_account/my_account.mako')
71 def my_account_password(self):
73 def my_account_password(self):
72 c = self.load_default_context()
74 c = self.load_default_context()
73 c.active = 'password'
75 c.active = 'password'
74 c.extern_type = c.user.extern_type
76 c.extern_type = c.user.extern_type
75
77
76 schema = user_schema.ChangePasswordSchema().bind(
78 schema = user_schema.ChangePasswordSchema().bind(
77 username=c.user.username)
79 username=c.user.username)
78
80
79 form = forms.Form(
81 form = forms.Form(
80 schema, buttons=(forms.buttons.save, forms.buttons.reset))
82 schema, buttons=(forms.buttons.save, forms.buttons.reset))
81
83
82 c.form = form
84 c.form = form
83 return self._get_template_context(c)
85 return self._get_template_context(c)
84
86
85 @LoginRequired()
87 @LoginRequired()
86 @NotAnonymous()
88 @NotAnonymous()
87 @CSRFRequired()
89 @CSRFRequired()
88 @view_config(
90 @view_config(
89 route_name='my_account_password', request_method='POST',
91 route_name='my_account_password', request_method='POST',
90 renderer='rhodecode:templates/admin/my_account/my_account.mako')
92 renderer='rhodecode:templates/admin/my_account/my_account.mako')
91 def my_account_password_update(self):
93 def my_account_password_update(self):
92 _ = self.request.translate
94 _ = self.request.translate
93 c = self.load_default_context()
95 c = self.load_default_context()
94 c.active = 'password'
96 c.active = 'password'
95 c.extern_type = c.user.extern_type
97 c.extern_type = c.user.extern_type
96
98
97 schema = user_schema.ChangePasswordSchema().bind(
99 schema = user_schema.ChangePasswordSchema().bind(
98 username=c.user.username)
100 username=c.user.username)
99
101
100 form = forms.Form(
102 form = forms.Form(
101 schema, buttons=(forms.buttons.save, forms.buttons.reset))
103 schema, buttons=(forms.buttons.save, forms.buttons.reset))
102
104
103 if c.extern_type != 'rhodecode':
105 if c.extern_type != 'rhodecode':
104 raise HTTPFound(self.request.route_path('my_account_password'))
106 raise HTTPFound(self.request.route_path('my_account_password'))
105
107
106 controls = self.request.POST.items()
108 controls = self.request.POST.items()
107 try:
109 try:
108 valid_data = form.validate(controls)
110 valid_data = form.validate(controls)
109 UserModel().update_user(c.user.user_id, **valid_data)
111 UserModel().update_user(c.user.user_id, **valid_data)
110 c.user.update_userdata(force_password_change=False)
112 c.user.update_userdata(force_password_change=False)
111 Session().commit()
113 Session().commit()
112 except forms.ValidationFailure as e:
114 except forms.ValidationFailure as e:
113 c.form = e
115 c.form = e
114 return self._get_template_context(c)
116 return self._get_template_context(c)
115
117
116 except Exception:
118 except Exception:
117 log.exception("Exception updating password")
119 log.exception("Exception updating password")
118 h.flash(_('Error occurred during update of user password'),
120 h.flash(_('Error occurred during update of user password'),
119 category='error')
121 category='error')
120 else:
122 else:
121 instance = c.auth_user.get_instance()
123 instance = c.auth_user.get_instance()
122 self.session.setdefault('rhodecode_user', {}).update(
124 self.session.setdefault('rhodecode_user', {}).update(
123 {'password': md5(instance.password)})
125 {'password': md5(instance.password)})
124 self.session.save()
126 self.session.save()
125 h.flash(_("Successfully updated password"), category='success')
127 h.flash(_("Successfully updated password"), category='success')
126
128
127 raise HTTPFound(self.request.route_path('my_account_password'))
129 raise HTTPFound(self.request.route_path('my_account_password'))
128
130
129 @LoginRequired()
131 @LoginRequired()
130 @NotAnonymous()
132 @NotAnonymous()
131 @view_config(
133 @view_config(
132 route_name='my_account_auth_tokens', request_method='GET',
134 route_name='my_account_auth_tokens', request_method='GET',
133 renderer='rhodecode:templates/admin/my_account/my_account.mako')
135 renderer='rhodecode:templates/admin/my_account/my_account.mako')
134 def my_account_auth_tokens(self):
136 def my_account_auth_tokens(self):
135 _ = self.request.translate
137 _ = self.request.translate
136
138
137 c = self.load_default_context()
139 c = self.load_default_context()
138 c.active = 'auth_tokens'
140 c.active = 'auth_tokens'
139
141
140 c.lifetime_values = [
142 c.lifetime_values = [
141 (str(-1), _('forever')),
143 (str(-1), _('forever')),
142 (str(5), _('5 minutes')),
144 (str(5), _('5 minutes')),
143 (str(60), _('1 hour')),
145 (str(60), _('1 hour')),
144 (str(60 * 24), _('1 day')),
146 (str(60 * 24), _('1 day')),
145 (str(60 * 24 * 30), _('1 month')),
147 (str(60 * 24 * 30), _('1 month')),
146 ]
148 ]
147 c.lifetime_options = [(c.lifetime_values, _("Lifetime"))]
149 c.lifetime_options = [(c.lifetime_values, _("Lifetime"))]
148 c.role_values = [
150 c.role_values = [
149 (x, AuthTokenModel.cls._get_role_name(x))
151 (x, AuthTokenModel.cls._get_role_name(x))
150 for x in AuthTokenModel.cls.ROLES]
152 for x in AuthTokenModel.cls.ROLES]
151 c.role_options = [(c.role_values, _("Role"))]
153 c.role_options = [(c.role_values, _("Role"))]
152 c.user_auth_tokens = AuthTokenModel().get_auth_tokens(
154 c.user_auth_tokens = AuthTokenModel().get_auth_tokens(
153 c.user.user_id, show_expired=True)
155 c.user.user_id, show_expired=True)
154 return self._get_template_context(c)
156 return self._get_template_context(c)
155
157
156 def maybe_attach_token_scope(self, token):
158 def maybe_attach_token_scope(self, token):
157 # implemented in EE edition
159 # implemented in EE edition
158 pass
160 pass
159
161
160 @LoginRequired()
162 @LoginRequired()
161 @NotAnonymous()
163 @NotAnonymous()
162 @CSRFRequired()
164 @CSRFRequired()
163 @view_config(
165 @view_config(
164 route_name='my_account_auth_tokens_add', request_method='POST')
166 route_name='my_account_auth_tokens_add', request_method='POST',)
165 def my_account_auth_tokens_add(self):
167 def my_account_auth_tokens_add(self):
166 _ = self.request.translate
168 _ = self.request.translate
167 c = self.load_default_context()
169 c = self.load_default_context()
168
170
169 lifetime = safe_int(self.request.POST.get('lifetime'), -1)
171 lifetime = safe_int(self.request.POST.get('lifetime'), -1)
170 description = self.request.POST.get('description')
172 description = self.request.POST.get('description')
171 role = self.request.POST.get('role')
173 role = self.request.POST.get('role')
172
174
173 token = AuthTokenModel().create(
175 token = AuthTokenModel().create(
174 c.user.user_id, description, lifetime, role)
176 c.user.user_id, description, lifetime, role)
175 self.maybe_attach_token_scope(token)
177 self.maybe_attach_token_scope(token)
176 Session().commit()
178 Session().commit()
177
179
178 h.flash(_("Auth token successfully created"), category='success')
180 h.flash(_("Auth token successfully created"), category='success')
179 return HTTPFound(h.route_path('my_account_auth_tokens'))
181 return HTTPFound(h.route_path('my_account_auth_tokens'))
180
182
181 @LoginRequired()
183 @LoginRequired()
182 @NotAnonymous()
184 @NotAnonymous()
183 @CSRFRequired()
185 @CSRFRequired()
184 @view_config(
186 @view_config(
185 route_name='my_account_auth_tokens_delete', request_method='POST')
187 route_name='my_account_auth_tokens_delete', request_method='POST')
186 def my_account_auth_tokens_delete(self):
188 def my_account_auth_tokens_delete(self):
187 _ = self.request.translate
189 _ = self.request.translate
188 c = self.load_default_context()
190 c = self.load_default_context()
189
191
190 del_auth_token = self.request.POST.get('del_auth_token')
192 del_auth_token = self.request.POST.get('del_auth_token')
191
193
192 if del_auth_token:
194 if del_auth_token:
193 AuthTokenModel().delete(del_auth_token, c.user.user_id)
195 AuthTokenModel().delete(del_auth_token, c.user.user_id)
194 Session().commit()
196 Session().commit()
195 h.flash(_("Auth token successfully deleted"), category='success')
197 h.flash(_("Auth token successfully deleted"), category='success')
196
198
197 return HTTPFound(h.route_path('my_account_auth_tokens'))
199 return HTTPFound(h.route_path('my_account_auth_tokens'))
198
200
199 @LoginRequired()
201 @LoginRequired()
200 @NotAnonymous()
202 @NotAnonymous()
203 @view_config(
204 route_name='my_account_emails', request_method='GET',
205 renderer='rhodecode:templates/admin/my_account/my_account.mako')
206 def my_account_emails(self):
207 _ = self.request.translate
208
209 c = self.load_default_context()
210 c.active = 'emails'
211
212 c.user_email_map = UserEmailMap.query()\
213 .filter(UserEmailMap.user == c.user).all()
214 return self._get_template_context(c)
215
216 @LoginRequired()
217 @NotAnonymous()
218 @CSRFRequired()
219 @view_config(
220 route_name='my_account_emails_add', request_method='POST')
221 def my_account_emails_add(self):
222 _ = self.request.translate
223 c = self.load_default_context()
224
225 email = self.request.POST.get('new_email')
226
227 try:
228 UserModel().add_extra_email(c.user.user_id, email)
229 Session().commit()
230 h.flash(_("Added new email address `%s` for user account") % email,
231 category='success')
232 except formencode.Invalid as error:
233 msg = error.error_dict['email']
234 h.flash(msg, category='error')
235 except Exception:
236 log.exception("Exception in my_account_emails")
237 h.flash(_('An error occurred during email saving'),
238 category='error')
239 return HTTPFound(h.route_path('my_account_emails'))
240
241 @LoginRequired()
242 @NotAnonymous()
243 @CSRFRequired()
244 @view_config(
245 route_name='my_account_emails_delete', request_method='POST')
246 def my_account_emails_delete(self):
247 _ = self.request.translate
248 c = self.load_default_context()
249
250 del_email_id = self.request.POST.get('del_email_id')
251 if del_email_id:
252
253 UserModel().delete_extra_email(
254 c.user.user_id, del_email_id)
255 Session().commit()
256 h.flash(_("Email successfully deleted"),
257 category='success')
258 return HTTPFound(h.route_path('my_account_emails'))
259
260 @LoginRequired()
261 @NotAnonymous()
201 @CSRFRequired()
262 @CSRFRequired()
202 @view_config(
263 @view_config(
203 route_name='my_account_notifications_test_channelstream',
264 route_name='my_account_notifications_test_channelstream',
204 request_method='POST', renderer='json_ext')
265 request_method='POST', renderer='json_ext')
205 def my_account_notifications_test_channelstream(self):
266 def my_account_notifications_test_channelstream(self):
206 message = 'Test message sent via Channelstream by user: {}, on {}'.format(
267 message = 'Test message sent via Channelstream by user: {}, on {}'.format(
207 self._rhodecode_user.username, datetime.datetime.now())
268 self._rhodecode_user.username, datetime.datetime.now())
208 payload = {
269 payload = {
209 # 'channel': 'broadcast',
270 # 'channel': 'broadcast',
210 'type': 'message',
271 'type': 'message',
211 'timestamp': datetime.datetime.utcnow(),
272 'timestamp': datetime.datetime.utcnow(),
212 'user': 'system',
273 'user': 'system',
213 'pm_users': [self._rhodecode_user.username],
274 'pm_users': [self._rhodecode_user.username],
214 'message': {
275 'message': {
215 'message': message,
276 'message': message,
216 'level': 'info',
277 'level': 'info',
217 'topic': '/notifications'
278 'topic': '/notifications'
218 }
279 }
219 }
280 }
220
281
221 registry = self.request.registry
282 registry = self.request.registry
222 rhodecode_plugins = getattr(registry, 'rhodecode_plugins', {})
283 rhodecode_plugins = getattr(registry, 'rhodecode_plugins', {})
223 channelstream_config = rhodecode_plugins.get('channelstream', {})
284 channelstream_config = rhodecode_plugins.get('channelstream', {})
224
285
225 try:
286 try:
226 channelstream_request(channelstream_config, [payload], '/message')
287 channelstream_request(channelstream_config, [payload], '/message')
227 except ChannelstreamException as e:
288 except ChannelstreamException as e:
228 log.exception('Failed to send channelstream data')
289 log.exception('Failed to send channelstream data')
229 return {"response": 'ERROR: {}'.format(e.__class__.__name__)}
290 return {"response": 'ERROR: {}'.format(e.__class__.__name__)}
230 return {"response": 'Channelstream data sent. '
291 return {"response": 'Channelstream data sent. '
231 'You should see a new live message now.'}
292 'You should see a new live message now.'}
@@ -1,982 +1,975 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2017 RhodeCode GmbH
3 # Copyright (C) 2010-2017 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 Routes configuration
22 Routes configuration
23
23
24 The more specific and detailed routes should be defined first so they
24 The more specific and detailed routes should be defined first so they
25 may take precedent over the more generic routes. For more information
25 may take precedent over the more generic routes. For more information
26 refer to the routes manual at http://routes.groovie.org/docs/
26 refer to the routes manual at http://routes.groovie.org/docs/
27
27
28 IMPORTANT: if you change any routing here, make sure to take a look at lib/base.py
28 IMPORTANT: if you change any routing here, make sure to take a look at lib/base.py
29 and _route_name variable which uses some of stored naming here to do redirects.
29 and _route_name variable which uses some of stored naming here to do redirects.
30 """
30 """
31 import os
31 import os
32 import re
32 import re
33 from routes import Mapper
33 from routes import Mapper
34
34
35 # prefix for non repository related links needs to be prefixed with `/`
35 # prefix for non repository related links needs to be prefixed with `/`
36 ADMIN_PREFIX = '/_admin'
36 ADMIN_PREFIX = '/_admin'
37 STATIC_FILE_PREFIX = '/_static'
37 STATIC_FILE_PREFIX = '/_static'
38
38
39 # Default requirements for URL parts
39 # Default requirements for URL parts
40 URL_NAME_REQUIREMENTS = {
40 URL_NAME_REQUIREMENTS = {
41 # group name can have a slash in them, but they must not end with a slash
41 # group name can have a slash in them, but they must not end with a slash
42 'group_name': r'.*?[^/]',
42 'group_name': r'.*?[^/]',
43 'repo_group_name': r'.*?[^/]',
43 'repo_group_name': r'.*?[^/]',
44 # repo names can have a slash in them, but they must not end with a slash
44 # repo names can have a slash in them, but they must not end with a slash
45 'repo_name': r'.*?[^/]',
45 'repo_name': r'.*?[^/]',
46 # file path eats up everything at the end
46 # file path eats up everything at the end
47 'f_path': r'.*',
47 'f_path': r'.*',
48 # reference types
48 # reference types
49 'source_ref_type': '(branch|book|tag|rev|\%\(source_ref_type\)s)',
49 'source_ref_type': '(branch|book|tag|rev|\%\(source_ref_type\)s)',
50 'target_ref_type': '(branch|book|tag|rev|\%\(target_ref_type\)s)',
50 'target_ref_type': '(branch|book|tag|rev|\%\(target_ref_type\)s)',
51 }
51 }
52
52
53
53
54 def add_route_requirements(route_path, requirements):
54 def add_route_requirements(route_path, requirements):
55 """
55 """
56 Adds regex requirements to pyramid routes using a mapping dict
56 Adds regex requirements to pyramid routes using a mapping dict
57
57
58 >>> add_route_requirements('/{action}/{id}', {'id': r'\d+'})
58 >>> add_route_requirements('/{action}/{id}', {'id': r'\d+'})
59 '/{action}/{id:\d+}'
59 '/{action}/{id:\d+}'
60
60
61 """
61 """
62 for key, regex in requirements.items():
62 for key, regex in requirements.items():
63 route_path = route_path.replace('{%s}' % key, '{%s:%s}' % (key, regex))
63 route_path = route_path.replace('{%s}' % key, '{%s:%s}' % (key, regex))
64 return route_path
64 return route_path
65
65
66
66
67 class JSRoutesMapper(Mapper):
67 class JSRoutesMapper(Mapper):
68 """
68 """
69 Wrapper for routes.Mapper to make pyroutes compatible url definitions
69 Wrapper for routes.Mapper to make pyroutes compatible url definitions
70 """
70 """
71 _named_route_regex = re.compile(r'^[a-z-_0-9A-Z]+$')
71 _named_route_regex = re.compile(r'^[a-z-_0-9A-Z]+$')
72 _argument_prog = re.compile('\{(.*?)\}|:\((.*)\)')
72 _argument_prog = re.compile('\{(.*?)\}|:\((.*)\)')
73 def __init__(self, *args, **kw):
73 def __init__(self, *args, **kw):
74 super(JSRoutesMapper, self).__init__(*args, **kw)
74 super(JSRoutesMapper, self).__init__(*args, **kw)
75 self._jsroutes = []
75 self._jsroutes = []
76
76
77 def connect(self, *args, **kw):
77 def connect(self, *args, **kw):
78 """
78 """
79 Wrapper for connect to take an extra argument jsroute=True
79 Wrapper for connect to take an extra argument jsroute=True
80
80
81 :param jsroute: boolean, if True will add the route to the pyroutes list
81 :param jsroute: boolean, if True will add the route to the pyroutes list
82 """
82 """
83 if kw.pop('jsroute', False):
83 if kw.pop('jsroute', False):
84 if not self._named_route_regex.match(args[0]):
84 if not self._named_route_regex.match(args[0]):
85 raise Exception('only named routes can be added to pyroutes')
85 raise Exception('only named routes can be added to pyroutes')
86 self._jsroutes.append(args[0])
86 self._jsroutes.append(args[0])
87
87
88 super(JSRoutesMapper, self).connect(*args, **kw)
88 super(JSRoutesMapper, self).connect(*args, **kw)
89
89
90 def _extract_route_information(self, route):
90 def _extract_route_information(self, route):
91 """
91 """
92 Convert a route into tuple(name, path, args), eg:
92 Convert a route into tuple(name, path, args), eg:
93 ('show_user', '/profile/%(username)s', ['username'])
93 ('show_user', '/profile/%(username)s', ['username'])
94 """
94 """
95 routepath = route.routepath
95 routepath = route.routepath
96 def replace(matchobj):
96 def replace(matchobj):
97 if matchobj.group(1):
97 if matchobj.group(1):
98 return "%%(%s)s" % matchobj.group(1).split(':')[0]
98 return "%%(%s)s" % matchobj.group(1).split(':')[0]
99 else:
99 else:
100 return "%%(%s)s" % matchobj.group(2)
100 return "%%(%s)s" % matchobj.group(2)
101
101
102 routepath = self._argument_prog.sub(replace, routepath)
102 routepath = self._argument_prog.sub(replace, routepath)
103 return (
103 return (
104 route.name,
104 route.name,
105 routepath,
105 routepath,
106 [(arg[0].split(':')[0] if arg[0] != '' else arg[1])
106 [(arg[0].split(':')[0] if arg[0] != '' else arg[1])
107 for arg in self._argument_prog.findall(route.routepath)]
107 for arg in self._argument_prog.findall(route.routepath)]
108 )
108 )
109
109
110 def jsroutes(self):
110 def jsroutes(self):
111 """
111 """
112 Return a list of pyroutes.js compatible routes
112 Return a list of pyroutes.js compatible routes
113 """
113 """
114 for route_name in self._jsroutes:
114 for route_name in self._jsroutes:
115 yield self._extract_route_information(self._routenames[route_name])
115 yield self._extract_route_information(self._routenames[route_name])
116
116
117
117
118 def make_map(config):
118 def make_map(config):
119 """Create, configure and return the routes Mapper"""
119 """Create, configure and return the routes Mapper"""
120 rmap = JSRoutesMapper(
120 rmap = JSRoutesMapper(
121 directory=config['pylons.paths']['controllers'],
121 directory=config['pylons.paths']['controllers'],
122 always_scan=config['debug'])
122 always_scan=config['debug'])
123 rmap.minimization = False
123 rmap.minimization = False
124 rmap.explicit = False
124 rmap.explicit = False
125
125
126 from rhodecode.lib.utils2 import str2bool
126 from rhodecode.lib.utils2 import str2bool
127 from rhodecode.model import repo, repo_group
127 from rhodecode.model import repo, repo_group
128
128
129 def check_repo(environ, match_dict):
129 def check_repo(environ, match_dict):
130 """
130 """
131 check for valid repository for proper 404 handling
131 check for valid repository for proper 404 handling
132
132
133 :param environ:
133 :param environ:
134 :param match_dict:
134 :param match_dict:
135 """
135 """
136 repo_name = match_dict.get('repo_name')
136 repo_name = match_dict.get('repo_name')
137
137
138 if match_dict.get('f_path'):
138 if match_dict.get('f_path'):
139 # fix for multiple initial slashes that causes errors
139 # fix for multiple initial slashes that causes errors
140 match_dict['f_path'] = match_dict['f_path'].lstrip('/')
140 match_dict['f_path'] = match_dict['f_path'].lstrip('/')
141 repo_model = repo.RepoModel()
141 repo_model = repo.RepoModel()
142 by_name_match = repo_model.get_by_repo_name(repo_name)
142 by_name_match = repo_model.get_by_repo_name(repo_name)
143 # if we match quickly from database, short circuit the operation,
143 # if we match quickly from database, short circuit the operation,
144 # and validate repo based on the type.
144 # and validate repo based on the type.
145 if by_name_match:
145 if by_name_match:
146 return True
146 return True
147
147
148 by_id_match = repo_model.get_repo_by_id(repo_name)
148 by_id_match = repo_model.get_repo_by_id(repo_name)
149 if by_id_match:
149 if by_id_match:
150 repo_name = by_id_match.repo_name
150 repo_name = by_id_match.repo_name
151 match_dict['repo_name'] = repo_name
151 match_dict['repo_name'] = repo_name
152 return True
152 return True
153
153
154 return False
154 return False
155
155
156 def check_group(environ, match_dict):
156 def check_group(environ, match_dict):
157 """
157 """
158 check for valid repository group path for proper 404 handling
158 check for valid repository group path for proper 404 handling
159
159
160 :param environ:
160 :param environ:
161 :param match_dict:
161 :param match_dict:
162 """
162 """
163 repo_group_name = match_dict.get('group_name')
163 repo_group_name = match_dict.get('group_name')
164 repo_group_model = repo_group.RepoGroupModel()
164 repo_group_model = repo_group.RepoGroupModel()
165 by_name_match = repo_group_model.get_by_group_name(repo_group_name)
165 by_name_match = repo_group_model.get_by_group_name(repo_group_name)
166 if by_name_match:
166 if by_name_match:
167 return True
167 return True
168
168
169 return False
169 return False
170
170
171 def check_user_group(environ, match_dict):
171 def check_user_group(environ, match_dict):
172 """
172 """
173 check for valid user group for proper 404 handling
173 check for valid user group for proper 404 handling
174
174
175 :param environ:
175 :param environ:
176 :param match_dict:
176 :param match_dict:
177 """
177 """
178 return True
178 return True
179
179
180 def check_int(environ, match_dict):
180 def check_int(environ, match_dict):
181 return match_dict.get('id').isdigit()
181 return match_dict.get('id').isdigit()
182
182
183
183
184 #==========================================================================
184 #==========================================================================
185 # CUSTOM ROUTES HERE
185 # CUSTOM ROUTES HERE
186 #==========================================================================
186 #==========================================================================
187
187
188 # ping and pylons error test
188 # ping and pylons error test
189 rmap.connect('ping', '%s/ping' % (ADMIN_PREFIX,), controller='home', action='ping')
189 rmap.connect('ping', '%s/ping' % (ADMIN_PREFIX,), controller='home', action='ping')
190 rmap.connect('error_test', '%s/error_test' % (ADMIN_PREFIX,), controller='home', action='error_test')
190 rmap.connect('error_test', '%s/error_test' % (ADMIN_PREFIX,), controller='home', action='error_test')
191
191
192 # ADMIN REPOSITORY ROUTES
192 # ADMIN REPOSITORY ROUTES
193 with rmap.submapper(path_prefix=ADMIN_PREFIX,
193 with rmap.submapper(path_prefix=ADMIN_PREFIX,
194 controller='admin/repos') as m:
194 controller='admin/repos') as m:
195 m.connect('repos', '/repos',
195 m.connect('repos', '/repos',
196 action='create', conditions={'method': ['POST']})
196 action='create', conditions={'method': ['POST']})
197 m.connect('repos', '/repos',
197 m.connect('repos', '/repos',
198 action='index', conditions={'method': ['GET']})
198 action='index', conditions={'method': ['GET']})
199 m.connect('new_repo', '/create_repository', jsroute=True,
199 m.connect('new_repo', '/create_repository', jsroute=True,
200 action='create_repository', conditions={'method': ['GET']})
200 action='create_repository', conditions={'method': ['GET']})
201 m.connect('delete_repo', '/repos/{repo_name}',
201 m.connect('delete_repo', '/repos/{repo_name}',
202 action='delete', conditions={'method': ['DELETE']},
202 action='delete', conditions={'method': ['DELETE']},
203 requirements=URL_NAME_REQUIREMENTS)
203 requirements=URL_NAME_REQUIREMENTS)
204 m.connect('repo', '/repos/{repo_name}',
204 m.connect('repo', '/repos/{repo_name}',
205 action='show', conditions={'method': ['GET'],
205 action='show', conditions={'method': ['GET'],
206 'function': check_repo},
206 'function': check_repo},
207 requirements=URL_NAME_REQUIREMENTS)
207 requirements=URL_NAME_REQUIREMENTS)
208
208
209 # ADMIN REPOSITORY GROUPS ROUTES
209 # ADMIN REPOSITORY GROUPS ROUTES
210 with rmap.submapper(path_prefix=ADMIN_PREFIX,
210 with rmap.submapper(path_prefix=ADMIN_PREFIX,
211 controller='admin/repo_groups') as m:
211 controller='admin/repo_groups') as m:
212 m.connect('repo_groups', '/repo_groups',
212 m.connect('repo_groups', '/repo_groups',
213 action='create', conditions={'method': ['POST']})
213 action='create', conditions={'method': ['POST']})
214 m.connect('repo_groups', '/repo_groups',
214 m.connect('repo_groups', '/repo_groups',
215 action='index', conditions={'method': ['GET']})
215 action='index', conditions={'method': ['GET']})
216 m.connect('new_repo_group', '/repo_groups/new',
216 m.connect('new_repo_group', '/repo_groups/new',
217 action='new', conditions={'method': ['GET']})
217 action='new', conditions={'method': ['GET']})
218 m.connect('update_repo_group', '/repo_groups/{group_name}',
218 m.connect('update_repo_group', '/repo_groups/{group_name}',
219 action='update', conditions={'method': ['PUT'],
219 action='update', conditions={'method': ['PUT'],
220 'function': check_group},
220 'function': check_group},
221 requirements=URL_NAME_REQUIREMENTS)
221 requirements=URL_NAME_REQUIREMENTS)
222
222
223 # EXTRAS REPO GROUP ROUTES
223 # EXTRAS REPO GROUP ROUTES
224 m.connect('edit_repo_group', '/repo_groups/{group_name}/edit',
224 m.connect('edit_repo_group', '/repo_groups/{group_name}/edit',
225 action='edit',
225 action='edit',
226 conditions={'method': ['GET'], 'function': check_group},
226 conditions={'method': ['GET'], 'function': check_group},
227 requirements=URL_NAME_REQUIREMENTS)
227 requirements=URL_NAME_REQUIREMENTS)
228 m.connect('edit_repo_group', '/repo_groups/{group_name}/edit',
228 m.connect('edit_repo_group', '/repo_groups/{group_name}/edit',
229 action='edit',
229 action='edit',
230 conditions={'method': ['PUT'], 'function': check_group},
230 conditions={'method': ['PUT'], 'function': check_group},
231 requirements=URL_NAME_REQUIREMENTS)
231 requirements=URL_NAME_REQUIREMENTS)
232
232
233 m.connect('edit_repo_group_advanced', '/repo_groups/{group_name}/edit/advanced',
233 m.connect('edit_repo_group_advanced', '/repo_groups/{group_name}/edit/advanced',
234 action='edit_repo_group_advanced',
234 action='edit_repo_group_advanced',
235 conditions={'method': ['GET'], 'function': check_group},
235 conditions={'method': ['GET'], 'function': check_group},
236 requirements=URL_NAME_REQUIREMENTS)
236 requirements=URL_NAME_REQUIREMENTS)
237 m.connect('edit_repo_group_advanced', '/repo_groups/{group_name}/edit/advanced',
237 m.connect('edit_repo_group_advanced', '/repo_groups/{group_name}/edit/advanced',
238 action='edit_repo_group_advanced',
238 action='edit_repo_group_advanced',
239 conditions={'method': ['PUT'], 'function': check_group},
239 conditions={'method': ['PUT'], 'function': check_group},
240 requirements=URL_NAME_REQUIREMENTS)
240 requirements=URL_NAME_REQUIREMENTS)
241
241
242 m.connect('edit_repo_group_perms', '/repo_groups/{group_name}/edit/permissions',
242 m.connect('edit_repo_group_perms', '/repo_groups/{group_name}/edit/permissions',
243 action='edit_repo_group_perms',
243 action='edit_repo_group_perms',
244 conditions={'method': ['GET'], 'function': check_group},
244 conditions={'method': ['GET'], 'function': check_group},
245 requirements=URL_NAME_REQUIREMENTS)
245 requirements=URL_NAME_REQUIREMENTS)
246 m.connect('edit_repo_group_perms', '/repo_groups/{group_name}/edit/permissions',
246 m.connect('edit_repo_group_perms', '/repo_groups/{group_name}/edit/permissions',
247 action='update_perms',
247 action='update_perms',
248 conditions={'method': ['PUT'], 'function': check_group},
248 conditions={'method': ['PUT'], 'function': check_group},
249 requirements=URL_NAME_REQUIREMENTS)
249 requirements=URL_NAME_REQUIREMENTS)
250
250
251 m.connect('delete_repo_group', '/repo_groups/{group_name}',
251 m.connect('delete_repo_group', '/repo_groups/{group_name}',
252 action='delete', conditions={'method': ['DELETE'],
252 action='delete', conditions={'method': ['DELETE'],
253 'function': check_group},
253 'function': check_group},
254 requirements=URL_NAME_REQUIREMENTS)
254 requirements=URL_NAME_REQUIREMENTS)
255
255
256 # ADMIN USER ROUTES
256 # ADMIN USER ROUTES
257 with rmap.submapper(path_prefix=ADMIN_PREFIX,
257 with rmap.submapper(path_prefix=ADMIN_PREFIX,
258 controller='admin/users') as m:
258 controller='admin/users') as m:
259 m.connect('users', '/users',
259 m.connect('users', '/users',
260 action='create', conditions={'method': ['POST']})
260 action='create', conditions={'method': ['POST']})
261 m.connect('new_user', '/users/new',
261 m.connect('new_user', '/users/new',
262 action='new', conditions={'method': ['GET']})
262 action='new', conditions={'method': ['GET']})
263 m.connect('update_user', '/users/{user_id}',
263 m.connect('update_user', '/users/{user_id}',
264 action='update', conditions={'method': ['PUT']})
264 action='update', conditions={'method': ['PUT']})
265 m.connect('delete_user', '/users/{user_id}',
265 m.connect('delete_user', '/users/{user_id}',
266 action='delete', conditions={'method': ['DELETE']})
266 action='delete', conditions={'method': ['DELETE']})
267 m.connect('edit_user', '/users/{user_id}/edit',
267 m.connect('edit_user', '/users/{user_id}/edit',
268 action='edit', conditions={'method': ['GET']}, jsroute=True)
268 action='edit', conditions={'method': ['GET']}, jsroute=True)
269 m.connect('user', '/users/{user_id}',
269 m.connect('user', '/users/{user_id}',
270 action='show', conditions={'method': ['GET']})
270 action='show', conditions={'method': ['GET']})
271 m.connect('force_password_reset_user', '/users/{user_id}/password_reset',
271 m.connect('force_password_reset_user', '/users/{user_id}/password_reset',
272 action='reset_password', conditions={'method': ['POST']})
272 action='reset_password', conditions={'method': ['POST']})
273 m.connect('create_personal_repo_group', '/users/{user_id}/create_repo_group',
273 m.connect('create_personal_repo_group', '/users/{user_id}/create_repo_group',
274 action='create_personal_repo_group', conditions={'method': ['POST']})
274 action='create_personal_repo_group', conditions={'method': ['POST']})
275
275
276 # EXTRAS USER ROUTES
276 # EXTRAS USER ROUTES
277 m.connect('edit_user_advanced', '/users/{user_id}/edit/advanced',
277 m.connect('edit_user_advanced', '/users/{user_id}/edit/advanced',
278 action='edit_advanced', conditions={'method': ['GET']})
278 action='edit_advanced', conditions={'method': ['GET']})
279 m.connect('edit_user_advanced', '/users/{user_id}/edit/advanced',
279 m.connect('edit_user_advanced', '/users/{user_id}/edit/advanced',
280 action='update_advanced', conditions={'method': ['PUT']})
280 action='update_advanced', conditions={'method': ['PUT']})
281
281
282 m.connect('edit_user_global_perms', '/users/{user_id}/edit/global_permissions',
282 m.connect('edit_user_global_perms', '/users/{user_id}/edit/global_permissions',
283 action='edit_global_perms', conditions={'method': ['GET']})
283 action='edit_global_perms', conditions={'method': ['GET']})
284 m.connect('edit_user_global_perms', '/users/{user_id}/edit/global_permissions',
284 m.connect('edit_user_global_perms', '/users/{user_id}/edit/global_permissions',
285 action='update_global_perms', conditions={'method': ['PUT']})
285 action='update_global_perms', conditions={'method': ['PUT']})
286
286
287 m.connect('edit_user_perms_summary', '/users/{user_id}/edit/permissions_summary',
287 m.connect('edit_user_perms_summary', '/users/{user_id}/edit/permissions_summary',
288 action='edit_perms_summary', conditions={'method': ['GET']})
288 action='edit_perms_summary', conditions={'method': ['GET']})
289
289
290 m.connect('edit_user_emails', '/users/{user_id}/edit/emails',
290 m.connect('edit_user_emails', '/users/{user_id}/edit/emails',
291 action='edit_emails', conditions={'method': ['GET']})
291 action='edit_emails', conditions={'method': ['GET']})
292 m.connect('edit_user_emails', '/users/{user_id}/edit/emails',
292 m.connect('edit_user_emails', '/users/{user_id}/edit/emails',
293 action='add_email', conditions={'method': ['PUT']})
293 action='add_email', conditions={'method': ['PUT']})
294 m.connect('edit_user_emails', '/users/{user_id}/edit/emails',
294 m.connect('edit_user_emails', '/users/{user_id}/edit/emails',
295 action='delete_email', conditions={'method': ['DELETE']})
295 action='delete_email', conditions={'method': ['DELETE']})
296
296
297 m.connect('edit_user_ips', '/users/{user_id}/edit/ips',
297 m.connect('edit_user_ips', '/users/{user_id}/edit/ips',
298 action='edit_ips', conditions={'method': ['GET']})
298 action='edit_ips', conditions={'method': ['GET']})
299 m.connect('edit_user_ips', '/users/{user_id}/edit/ips',
299 m.connect('edit_user_ips', '/users/{user_id}/edit/ips',
300 action='add_ip', conditions={'method': ['PUT']})
300 action='add_ip', conditions={'method': ['PUT']})
301 m.connect('edit_user_ips', '/users/{user_id}/edit/ips',
301 m.connect('edit_user_ips', '/users/{user_id}/edit/ips',
302 action='delete_ip', conditions={'method': ['DELETE']})
302 action='delete_ip', conditions={'method': ['DELETE']})
303
303
304 # ADMIN USER GROUPS REST ROUTES
304 # ADMIN USER GROUPS REST ROUTES
305 with rmap.submapper(path_prefix=ADMIN_PREFIX,
305 with rmap.submapper(path_prefix=ADMIN_PREFIX,
306 controller='admin/user_groups') as m:
306 controller='admin/user_groups') as m:
307 m.connect('users_groups', '/user_groups',
307 m.connect('users_groups', '/user_groups',
308 action='create', conditions={'method': ['POST']})
308 action='create', conditions={'method': ['POST']})
309 m.connect('users_groups', '/user_groups',
309 m.connect('users_groups', '/user_groups',
310 action='index', conditions={'method': ['GET']})
310 action='index', conditions={'method': ['GET']})
311 m.connect('new_users_group', '/user_groups/new',
311 m.connect('new_users_group', '/user_groups/new',
312 action='new', conditions={'method': ['GET']})
312 action='new', conditions={'method': ['GET']})
313 m.connect('update_users_group', '/user_groups/{user_group_id}',
313 m.connect('update_users_group', '/user_groups/{user_group_id}',
314 action='update', conditions={'method': ['PUT']})
314 action='update', conditions={'method': ['PUT']})
315 m.connect('delete_users_group', '/user_groups/{user_group_id}',
315 m.connect('delete_users_group', '/user_groups/{user_group_id}',
316 action='delete', conditions={'method': ['DELETE']})
316 action='delete', conditions={'method': ['DELETE']})
317 m.connect('edit_users_group', '/user_groups/{user_group_id}/edit',
317 m.connect('edit_users_group', '/user_groups/{user_group_id}/edit',
318 action='edit', conditions={'method': ['GET']},
318 action='edit', conditions={'method': ['GET']},
319 function=check_user_group)
319 function=check_user_group)
320
320
321 # EXTRAS USER GROUP ROUTES
321 # EXTRAS USER GROUP ROUTES
322 m.connect('edit_user_group_global_perms',
322 m.connect('edit_user_group_global_perms',
323 '/user_groups/{user_group_id}/edit/global_permissions',
323 '/user_groups/{user_group_id}/edit/global_permissions',
324 action='edit_global_perms', conditions={'method': ['GET']})
324 action='edit_global_perms', conditions={'method': ['GET']})
325 m.connect('edit_user_group_global_perms',
325 m.connect('edit_user_group_global_perms',
326 '/user_groups/{user_group_id}/edit/global_permissions',
326 '/user_groups/{user_group_id}/edit/global_permissions',
327 action='update_global_perms', conditions={'method': ['PUT']})
327 action='update_global_perms', conditions={'method': ['PUT']})
328 m.connect('edit_user_group_perms_summary',
328 m.connect('edit_user_group_perms_summary',
329 '/user_groups/{user_group_id}/edit/permissions_summary',
329 '/user_groups/{user_group_id}/edit/permissions_summary',
330 action='edit_perms_summary', conditions={'method': ['GET']})
330 action='edit_perms_summary', conditions={'method': ['GET']})
331
331
332 m.connect('edit_user_group_perms',
332 m.connect('edit_user_group_perms',
333 '/user_groups/{user_group_id}/edit/permissions',
333 '/user_groups/{user_group_id}/edit/permissions',
334 action='edit_perms', conditions={'method': ['GET']})
334 action='edit_perms', conditions={'method': ['GET']})
335 m.connect('edit_user_group_perms',
335 m.connect('edit_user_group_perms',
336 '/user_groups/{user_group_id}/edit/permissions',
336 '/user_groups/{user_group_id}/edit/permissions',
337 action='update_perms', conditions={'method': ['PUT']})
337 action='update_perms', conditions={'method': ['PUT']})
338
338
339 m.connect('edit_user_group_advanced',
339 m.connect('edit_user_group_advanced',
340 '/user_groups/{user_group_id}/edit/advanced',
340 '/user_groups/{user_group_id}/edit/advanced',
341 action='edit_advanced', conditions={'method': ['GET']})
341 action='edit_advanced', conditions={'method': ['GET']})
342
342
343 m.connect('edit_user_group_advanced_sync',
343 m.connect('edit_user_group_advanced_sync',
344 '/user_groups/{user_group_id}/edit/advanced/sync',
344 '/user_groups/{user_group_id}/edit/advanced/sync',
345 action='edit_advanced_set_synchronization', conditions={'method': ['POST']})
345 action='edit_advanced_set_synchronization', conditions={'method': ['POST']})
346
346
347 m.connect('edit_user_group_members',
347 m.connect('edit_user_group_members',
348 '/user_groups/{user_group_id}/edit/members', jsroute=True,
348 '/user_groups/{user_group_id}/edit/members', jsroute=True,
349 action='user_group_members', conditions={'method': ['GET']})
349 action='user_group_members', conditions={'method': ['GET']})
350
350
351 # ADMIN PERMISSIONS ROUTES
351 # ADMIN PERMISSIONS ROUTES
352 with rmap.submapper(path_prefix=ADMIN_PREFIX,
352 with rmap.submapper(path_prefix=ADMIN_PREFIX,
353 controller='admin/permissions') as m:
353 controller='admin/permissions') as m:
354 m.connect('admin_permissions_application', '/permissions/application',
354 m.connect('admin_permissions_application', '/permissions/application',
355 action='permission_application_update', conditions={'method': ['POST']})
355 action='permission_application_update', conditions={'method': ['POST']})
356 m.connect('admin_permissions_application', '/permissions/application',
356 m.connect('admin_permissions_application', '/permissions/application',
357 action='permission_application', conditions={'method': ['GET']})
357 action='permission_application', conditions={'method': ['GET']})
358
358
359 m.connect('admin_permissions_global', '/permissions/global',
359 m.connect('admin_permissions_global', '/permissions/global',
360 action='permission_global_update', conditions={'method': ['POST']})
360 action='permission_global_update', conditions={'method': ['POST']})
361 m.connect('admin_permissions_global', '/permissions/global',
361 m.connect('admin_permissions_global', '/permissions/global',
362 action='permission_global', conditions={'method': ['GET']})
362 action='permission_global', conditions={'method': ['GET']})
363
363
364 m.connect('admin_permissions_object', '/permissions/object',
364 m.connect('admin_permissions_object', '/permissions/object',
365 action='permission_objects_update', conditions={'method': ['POST']})
365 action='permission_objects_update', conditions={'method': ['POST']})
366 m.connect('admin_permissions_object', '/permissions/object',
366 m.connect('admin_permissions_object', '/permissions/object',
367 action='permission_objects', conditions={'method': ['GET']})
367 action='permission_objects', conditions={'method': ['GET']})
368
368
369 m.connect('admin_permissions_ips', '/permissions/ips',
369 m.connect('admin_permissions_ips', '/permissions/ips',
370 action='permission_ips', conditions={'method': ['POST']})
370 action='permission_ips', conditions={'method': ['POST']})
371 m.connect('admin_permissions_ips', '/permissions/ips',
371 m.connect('admin_permissions_ips', '/permissions/ips',
372 action='permission_ips', conditions={'method': ['GET']})
372 action='permission_ips', conditions={'method': ['GET']})
373
373
374 m.connect('admin_permissions_overview', '/permissions/overview',
374 m.connect('admin_permissions_overview', '/permissions/overview',
375 action='permission_perms', conditions={'method': ['GET']})
375 action='permission_perms', conditions={'method': ['GET']})
376
376
377 # ADMIN DEFAULTS REST ROUTES
377 # ADMIN DEFAULTS REST ROUTES
378 with rmap.submapper(path_prefix=ADMIN_PREFIX,
378 with rmap.submapper(path_prefix=ADMIN_PREFIX,
379 controller='admin/defaults') as m:
379 controller='admin/defaults') as m:
380 m.connect('admin_defaults_repositories', '/defaults/repositories',
380 m.connect('admin_defaults_repositories', '/defaults/repositories',
381 action='update_repository_defaults', conditions={'method': ['POST']})
381 action='update_repository_defaults', conditions={'method': ['POST']})
382 m.connect('admin_defaults_repositories', '/defaults/repositories',
382 m.connect('admin_defaults_repositories', '/defaults/repositories',
383 action='index', conditions={'method': ['GET']})
383 action='index', conditions={'method': ['GET']})
384
384
385 # ADMIN DEBUG STYLE ROUTES
385 # ADMIN DEBUG STYLE ROUTES
386 if str2bool(config.get('debug_style')):
386 if str2bool(config.get('debug_style')):
387 with rmap.submapper(path_prefix=ADMIN_PREFIX + '/debug_style',
387 with rmap.submapper(path_prefix=ADMIN_PREFIX + '/debug_style',
388 controller='debug_style') as m:
388 controller='debug_style') as m:
389 m.connect('debug_style_home', '',
389 m.connect('debug_style_home', '',
390 action='index', conditions={'method': ['GET']})
390 action='index', conditions={'method': ['GET']})
391 m.connect('debug_style_template', '/t/{t_path}',
391 m.connect('debug_style_template', '/t/{t_path}',
392 action='template', conditions={'method': ['GET']})
392 action='template', conditions={'method': ['GET']})
393
393
394 # ADMIN SETTINGS ROUTES
394 # ADMIN SETTINGS ROUTES
395 with rmap.submapper(path_prefix=ADMIN_PREFIX,
395 with rmap.submapper(path_prefix=ADMIN_PREFIX,
396 controller='admin/settings') as m:
396 controller='admin/settings') as m:
397
397
398 # default
398 # default
399 m.connect('admin_settings', '/settings',
399 m.connect('admin_settings', '/settings',
400 action='settings_global_update',
400 action='settings_global_update',
401 conditions={'method': ['POST']})
401 conditions={'method': ['POST']})
402 m.connect('admin_settings', '/settings',
402 m.connect('admin_settings', '/settings',
403 action='settings_global', conditions={'method': ['GET']})
403 action='settings_global', conditions={'method': ['GET']})
404
404
405 m.connect('admin_settings_vcs', '/settings/vcs',
405 m.connect('admin_settings_vcs', '/settings/vcs',
406 action='settings_vcs_update',
406 action='settings_vcs_update',
407 conditions={'method': ['POST']})
407 conditions={'method': ['POST']})
408 m.connect('admin_settings_vcs', '/settings/vcs',
408 m.connect('admin_settings_vcs', '/settings/vcs',
409 action='settings_vcs',
409 action='settings_vcs',
410 conditions={'method': ['GET']})
410 conditions={'method': ['GET']})
411 m.connect('admin_settings_vcs', '/settings/vcs',
411 m.connect('admin_settings_vcs', '/settings/vcs',
412 action='delete_svn_pattern',
412 action='delete_svn_pattern',
413 conditions={'method': ['DELETE']})
413 conditions={'method': ['DELETE']})
414
414
415 m.connect('admin_settings_mapping', '/settings/mapping',
415 m.connect('admin_settings_mapping', '/settings/mapping',
416 action='settings_mapping_update',
416 action='settings_mapping_update',
417 conditions={'method': ['POST']})
417 conditions={'method': ['POST']})
418 m.connect('admin_settings_mapping', '/settings/mapping',
418 m.connect('admin_settings_mapping', '/settings/mapping',
419 action='settings_mapping', conditions={'method': ['GET']})
419 action='settings_mapping', conditions={'method': ['GET']})
420
420
421 m.connect('admin_settings_global', '/settings/global',
421 m.connect('admin_settings_global', '/settings/global',
422 action='settings_global_update',
422 action='settings_global_update',
423 conditions={'method': ['POST']})
423 conditions={'method': ['POST']})
424 m.connect('admin_settings_global', '/settings/global',
424 m.connect('admin_settings_global', '/settings/global',
425 action='settings_global', conditions={'method': ['GET']})
425 action='settings_global', conditions={'method': ['GET']})
426
426
427 m.connect('admin_settings_visual', '/settings/visual',
427 m.connect('admin_settings_visual', '/settings/visual',
428 action='settings_visual_update',
428 action='settings_visual_update',
429 conditions={'method': ['POST']})
429 conditions={'method': ['POST']})
430 m.connect('admin_settings_visual', '/settings/visual',
430 m.connect('admin_settings_visual', '/settings/visual',
431 action='settings_visual', conditions={'method': ['GET']})
431 action='settings_visual', conditions={'method': ['GET']})
432
432
433 m.connect('admin_settings_issuetracker',
433 m.connect('admin_settings_issuetracker',
434 '/settings/issue-tracker', action='settings_issuetracker',
434 '/settings/issue-tracker', action='settings_issuetracker',
435 conditions={'method': ['GET']})
435 conditions={'method': ['GET']})
436 m.connect('admin_settings_issuetracker_save',
436 m.connect('admin_settings_issuetracker_save',
437 '/settings/issue-tracker/save',
437 '/settings/issue-tracker/save',
438 action='settings_issuetracker_save',
438 action='settings_issuetracker_save',
439 conditions={'method': ['POST']})
439 conditions={'method': ['POST']})
440 m.connect('admin_issuetracker_test', '/settings/issue-tracker/test',
440 m.connect('admin_issuetracker_test', '/settings/issue-tracker/test',
441 action='settings_issuetracker_test',
441 action='settings_issuetracker_test',
442 conditions={'method': ['POST']})
442 conditions={'method': ['POST']})
443 m.connect('admin_issuetracker_delete',
443 m.connect('admin_issuetracker_delete',
444 '/settings/issue-tracker/delete',
444 '/settings/issue-tracker/delete',
445 action='settings_issuetracker_delete',
445 action='settings_issuetracker_delete',
446 conditions={'method': ['DELETE']})
446 conditions={'method': ['DELETE']})
447
447
448 m.connect('admin_settings_email', '/settings/email',
448 m.connect('admin_settings_email', '/settings/email',
449 action='settings_email_update',
449 action='settings_email_update',
450 conditions={'method': ['POST']})
450 conditions={'method': ['POST']})
451 m.connect('admin_settings_email', '/settings/email',
451 m.connect('admin_settings_email', '/settings/email',
452 action='settings_email', conditions={'method': ['GET']})
452 action='settings_email', conditions={'method': ['GET']})
453
453
454 m.connect('admin_settings_hooks', '/settings/hooks',
454 m.connect('admin_settings_hooks', '/settings/hooks',
455 action='settings_hooks_update',
455 action='settings_hooks_update',
456 conditions={'method': ['POST', 'DELETE']})
456 conditions={'method': ['POST', 'DELETE']})
457 m.connect('admin_settings_hooks', '/settings/hooks',
457 m.connect('admin_settings_hooks', '/settings/hooks',
458 action='settings_hooks', conditions={'method': ['GET']})
458 action='settings_hooks', conditions={'method': ['GET']})
459
459
460 m.connect('admin_settings_search', '/settings/search',
460 m.connect('admin_settings_search', '/settings/search',
461 action='settings_search', conditions={'method': ['GET']})
461 action='settings_search', conditions={'method': ['GET']})
462
462
463 m.connect('admin_settings_supervisor', '/settings/supervisor',
463 m.connect('admin_settings_supervisor', '/settings/supervisor',
464 action='settings_supervisor', conditions={'method': ['GET']})
464 action='settings_supervisor', conditions={'method': ['GET']})
465 m.connect('admin_settings_supervisor_log', '/settings/supervisor/{procid}/log',
465 m.connect('admin_settings_supervisor_log', '/settings/supervisor/{procid}/log',
466 action='settings_supervisor_log', conditions={'method': ['GET']})
466 action='settings_supervisor_log', conditions={'method': ['GET']})
467
467
468 m.connect('admin_settings_labs', '/settings/labs',
468 m.connect('admin_settings_labs', '/settings/labs',
469 action='settings_labs_update',
469 action='settings_labs_update',
470 conditions={'method': ['POST']})
470 conditions={'method': ['POST']})
471 m.connect('admin_settings_labs', '/settings/labs',
471 m.connect('admin_settings_labs', '/settings/labs',
472 action='settings_labs', conditions={'method': ['GET']})
472 action='settings_labs', conditions={'method': ['GET']})
473
473
474 # ADMIN MY ACCOUNT
474 # ADMIN MY ACCOUNT
475 with rmap.submapper(path_prefix=ADMIN_PREFIX,
475 with rmap.submapper(path_prefix=ADMIN_PREFIX,
476 controller='admin/my_account') as m:
476 controller='admin/my_account') as m:
477
477
478 m.connect('my_account_edit', '/my_account/edit',
478 m.connect('my_account_edit', '/my_account/edit',
479 action='my_account_edit', conditions={'method': ['GET']})
479 action='my_account_edit', conditions={'method': ['GET']})
480 m.connect('my_account', '/my_account/update',
480 m.connect('my_account', '/my_account/update',
481 action='my_account_update', conditions={'method': ['POST']})
481 action='my_account_update', conditions={'method': ['POST']})
482
482
483 # NOTE(marcink): this needs to be kept for password force flag to be
483 # NOTE(marcink): this needs to be kept for password force flag to be
484 # handler, remove after migration to pyramid
484 # handler, remove after migration to pyramid
485 m.connect('my_account_password', '/my_account/password',
485 m.connect('my_account_password', '/my_account/password',
486 action='my_account_password', conditions={'method': ['GET']})
486 action='my_account_password', conditions={'method': ['GET']})
487
487
488 m.connect('my_account_repos', '/my_account/repos',
488 m.connect('my_account_repos', '/my_account/repos',
489 action='my_account_repos', conditions={'method': ['GET']})
489 action='my_account_repos', conditions={'method': ['GET']})
490
490
491 m.connect('my_account_watched', '/my_account/watched',
491 m.connect('my_account_watched', '/my_account/watched',
492 action='my_account_watched', conditions={'method': ['GET']})
492 action='my_account_watched', conditions={'method': ['GET']})
493
493
494 m.connect('my_account_pullrequests', '/my_account/pull_requests',
494 m.connect('my_account_pullrequests', '/my_account/pull_requests',
495 action='my_account_pullrequests', conditions={'method': ['GET']})
495 action='my_account_pullrequests', conditions={'method': ['GET']})
496
496
497 m.connect('my_account_perms', '/my_account/perms',
497 m.connect('my_account_perms', '/my_account/perms',
498 action='my_account_perms', conditions={'method': ['GET']})
498 action='my_account_perms', conditions={'method': ['GET']})
499
499
500 m.connect('my_account_emails', '/my_account/emails',
501 action='my_account_emails', conditions={'method': ['GET']})
502 m.connect('my_account_emails', '/my_account/emails',
503 action='my_account_emails_add', conditions={'method': ['POST']})
504 m.connect('my_account_emails', '/my_account/emails',
505 action='my_account_emails_delete', conditions={'method': ['DELETE']})
506
507 m.connect('my_account_notifications', '/my_account/notifications',
500 m.connect('my_account_notifications', '/my_account/notifications',
508 action='my_notifications',
501 action='my_notifications',
509 conditions={'method': ['GET']})
502 conditions={'method': ['GET']})
510 m.connect('my_account_notifications_toggle_visibility',
503 m.connect('my_account_notifications_toggle_visibility',
511 '/my_account/toggle_visibility',
504 '/my_account/toggle_visibility',
512 action='my_notifications_toggle_visibility',
505 action='my_notifications_toggle_visibility',
513 conditions={'method': ['POST']})
506 conditions={'method': ['POST']})
514
507
515 # NOTIFICATION REST ROUTES
508 # NOTIFICATION REST ROUTES
516 with rmap.submapper(path_prefix=ADMIN_PREFIX,
509 with rmap.submapper(path_prefix=ADMIN_PREFIX,
517 controller='admin/notifications') as m:
510 controller='admin/notifications') as m:
518 m.connect('notifications', '/notifications',
511 m.connect('notifications', '/notifications',
519 action='index', conditions={'method': ['GET']})
512 action='index', conditions={'method': ['GET']})
520 m.connect('notifications_mark_all_read', '/notifications/mark_all_read',
513 m.connect('notifications_mark_all_read', '/notifications/mark_all_read',
521 action='mark_all_read', conditions={'method': ['POST']})
514 action='mark_all_read', conditions={'method': ['POST']})
522 m.connect('/notifications/{notification_id}',
515 m.connect('/notifications/{notification_id}',
523 action='update', conditions={'method': ['PUT']})
516 action='update', conditions={'method': ['PUT']})
524 m.connect('/notifications/{notification_id}',
517 m.connect('/notifications/{notification_id}',
525 action='delete', conditions={'method': ['DELETE']})
518 action='delete', conditions={'method': ['DELETE']})
526 m.connect('notification', '/notifications/{notification_id}',
519 m.connect('notification', '/notifications/{notification_id}',
527 action='show', conditions={'method': ['GET']})
520 action='show', conditions={'method': ['GET']})
528
521
529 # ADMIN GIST
522 # ADMIN GIST
530 with rmap.submapper(path_prefix=ADMIN_PREFIX,
523 with rmap.submapper(path_prefix=ADMIN_PREFIX,
531 controller='admin/gists') as m:
524 controller='admin/gists') as m:
532 m.connect('gists', '/gists',
525 m.connect('gists', '/gists',
533 action='create', conditions={'method': ['POST']})
526 action='create', conditions={'method': ['POST']})
534 m.connect('gists', '/gists', jsroute=True,
527 m.connect('gists', '/gists', jsroute=True,
535 action='index', conditions={'method': ['GET']})
528 action='index', conditions={'method': ['GET']})
536 m.connect('new_gist', '/gists/new', jsroute=True,
529 m.connect('new_gist', '/gists/new', jsroute=True,
537 action='new', conditions={'method': ['GET']})
530 action='new', conditions={'method': ['GET']})
538
531
539 m.connect('/gists/{gist_id}',
532 m.connect('/gists/{gist_id}',
540 action='delete', conditions={'method': ['DELETE']})
533 action='delete', conditions={'method': ['DELETE']})
541 m.connect('edit_gist', '/gists/{gist_id}/edit',
534 m.connect('edit_gist', '/gists/{gist_id}/edit',
542 action='edit_form', conditions={'method': ['GET']})
535 action='edit_form', conditions={'method': ['GET']})
543 m.connect('edit_gist', '/gists/{gist_id}/edit',
536 m.connect('edit_gist', '/gists/{gist_id}/edit',
544 action='edit', conditions={'method': ['POST']})
537 action='edit', conditions={'method': ['POST']})
545 m.connect(
538 m.connect(
546 'edit_gist_check_revision', '/gists/{gist_id}/edit/check_revision',
539 'edit_gist_check_revision', '/gists/{gist_id}/edit/check_revision',
547 action='check_revision', conditions={'method': ['GET']})
540 action='check_revision', conditions={'method': ['GET']})
548
541
549 m.connect('gist', '/gists/{gist_id}',
542 m.connect('gist', '/gists/{gist_id}',
550 action='show', conditions={'method': ['GET']})
543 action='show', conditions={'method': ['GET']})
551 m.connect('gist_rev', '/gists/{gist_id}/{revision}',
544 m.connect('gist_rev', '/gists/{gist_id}/{revision}',
552 revision='tip',
545 revision='tip',
553 action='show', conditions={'method': ['GET']})
546 action='show', conditions={'method': ['GET']})
554 m.connect('formatted_gist', '/gists/{gist_id}/{revision}/{format}',
547 m.connect('formatted_gist', '/gists/{gist_id}/{revision}/{format}',
555 revision='tip',
548 revision='tip',
556 action='show', conditions={'method': ['GET']})
549 action='show', conditions={'method': ['GET']})
557 m.connect('formatted_gist_file', '/gists/{gist_id}/{revision}/{format}/{f_path}',
550 m.connect('formatted_gist_file', '/gists/{gist_id}/{revision}/{format}/{f_path}',
558 revision='tip',
551 revision='tip',
559 action='show', conditions={'method': ['GET']},
552 action='show', conditions={'method': ['GET']},
560 requirements=URL_NAME_REQUIREMENTS)
553 requirements=URL_NAME_REQUIREMENTS)
561
554
562 # USER JOURNAL
555 # USER JOURNAL
563 rmap.connect('journal', '%s/journal' % (ADMIN_PREFIX,),
556 rmap.connect('journal', '%s/journal' % (ADMIN_PREFIX,),
564 controller='journal', action='index')
557 controller='journal', action='index')
565 rmap.connect('journal_rss', '%s/journal/rss' % (ADMIN_PREFIX,),
558 rmap.connect('journal_rss', '%s/journal/rss' % (ADMIN_PREFIX,),
566 controller='journal', action='journal_rss')
559 controller='journal', action='journal_rss')
567 rmap.connect('journal_atom', '%s/journal/atom' % (ADMIN_PREFIX,),
560 rmap.connect('journal_atom', '%s/journal/atom' % (ADMIN_PREFIX,),
568 controller='journal', action='journal_atom')
561 controller='journal', action='journal_atom')
569
562
570 rmap.connect('public_journal', '%s/public_journal' % (ADMIN_PREFIX,),
563 rmap.connect('public_journal', '%s/public_journal' % (ADMIN_PREFIX,),
571 controller='journal', action='public_journal')
564 controller='journal', action='public_journal')
572
565
573 rmap.connect('public_journal_rss', '%s/public_journal/rss' % (ADMIN_PREFIX,),
566 rmap.connect('public_journal_rss', '%s/public_journal/rss' % (ADMIN_PREFIX,),
574 controller='journal', action='public_journal_rss')
567 controller='journal', action='public_journal_rss')
575
568
576 rmap.connect('public_journal_rss_old', '%s/public_journal_rss' % (ADMIN_PREFIX,),
569 rmap.connect('public_journal_rss_old', '%s/public_journal_rss' % (ADMIN_PREFIX,),
577 controller='journal', action='public_journal_rss')
570 controller='journal', action='public_journal_rss')
578
571
579 rmap.connect('public_journal_atom',
572 rmap.connect('public_journal_atom',
580 '%s/public_journal/atom' % (ADMIN_PREFIX,), controller='journal',
573 '%s/public_journal/atom' % (ADMIN_PREFIX,), controller='journal',
581 action='public_journal_atom')
574 action='public_journal_atom')
582
575
583 rmap.connect('public_journal_atom_old',
576 rmap.connect('public_journal_atom_old',
584 '%s/public_journal_atom' % (ADMIN_PREFIX,), controller='journal',
577 '%s/public_journal_atom' % (ADMIN_PREFIX,), controller='journal',
585 action='public_journal_atom')
578 action='public_journal_atom')
586
579
587 rmap.connect('toggle_following', '%s/toggle_following' % (ADMIN_PREFIX,),
580 rmap.connect('toggle_following', '%s/toggle_following' % (ADMIN_PREFIX,),
588 controller='journal', action='toggle_following', jsroute=True,
581 controller='journal', action='toggle_following', jsroute=True,
589 conditions={'method': ['POST']})
582 conditions={'method': ['POST']})
590
583
591 # FEEDS
584 # FEEDS
592 rmap.connect('rss_feed_home', '/{repo_name}/feed/rss',
585 rmap.connect('rss_feed_home', '/{repo_name}/feed/rss',
593 controller='feed', action='rss',
586 controller='feed', action='rss',
594 conditions={'function': check_repo},
587 conditions={'function': check_repo},
595 requirements=URL_NAME_REQUIREMENTS)
588 requirements=URL_NAME_REQUIREMENTS)
596
589
597 rmap.connect('atom_feed_home', '/{repo_name}/feed/atom',
590 rmap.connect('atom_feed_home', '/{repo_name}/feed/atom',
598 controller='feed', action='atom',
591 controller='feed', action='atom',
599 conditions={'function': check_repo},
592 conditions={'function': check_repo},
600 requirements=URL_NAME_REQUIREMENTS)
593 requirements=URL_NAME_REQUIREMENTS)
601
594
602 #==========================================================================
595 #==========================================================================
603 # REPOSITORY ROUTES
596 # REPOSITORY ROUTES
604 #==========================================================================
597 #==========================================================================
605
598
606 rmap.connect('repo_creating_home', '/{repo_name}/repo_creating',
599 rmap.connect('repo_creating_home', '/{repo_name}/repo_creating',
607 controller='admin/repos', action='repo_creating',
600 controller='admin/repos', action='repo_creating',
608 requirements=URL_NAME_REQUIREMENTS)
601 requirements=URL_NAME_REQUIREMENTS)
609 rmap.connect('repo_check_home', '/{repo_name}/crepo_check',
602 rmap.connect('repo_check_home', '/{repo_name}/crepo_check',
610 controller='admin/repos', action='repo_check',
603 controller='admin/repos', action='repo_check',
611 requirements=URL_NAME_REQUIREMENTS)
604 requirements=URL_NAME_REQUIREMENTS)
612
605
613 rmap.connect('changeset_home', '/{repo_name}/changeset/{revision}',
606 rmap.connect('changeset_home', '/{repo_name}/changeset/{revision}',
614 controller='changeset', revision='tip',
607 controller='changeset', revision='tip',
615 conditions={'function': check_repo},
608 conditions={'function': check_repo},
616 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
609 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
617 rmap.connect('changeset_children', '/{repo_name}/changeset_children/{revision}',
610 rmap.connect('changeset_children', '/{repo_name}/changeset_children/{revision}',
618 controller='changeset', revision='tip', action='changeset_children',
611 controller='changeset', revision='tip', action='changeset_children',
619 conditions={'function': check_repo},
612 conditions={'function': check_repo},
620 requirements=URL_NAME_REQUIREMENTS)
613 requirements=URL_NAME_REQUIREMENTS)
621 rmap.connect('changeset_parents', '/{repo_name}/changeset_parents/{revision}',
614 rmap.connect('changeset_parents', '/{repo_name}/changeset_parents/{revision}',
622 controller='changeset', revision='tip', action='changeset_parents',
615 controller='changeset', revision='tip', action='changeset_parents',
623 conditions={'function': check_repo},
616 conditions={'function': check_repo},
624 requirements=URL_NAME_REQUIREMENTS)
617 requirements=URL_NAME_REQUIREMENTS)
625
618
626 # repo edit options
619 # repo edit options
627 rmap.connect('edit_repo_fields', '/{repo_name}/settings/fields',
620 rmap.connect('edit_repo_fields', '/{repo_name}/settings/fields',
628 controller='admin/repos', action='edit_fields',
621 controller='admin/repos', action='edit_fields',
629 conditions={'method': ['GET'], 'function': check_repo},
622 conditions={'method': ['GET'], 'function': check_repo},
630 requirements=URL_NAME_REQUIREMENTS)
623 requirements=URL_NAME_REQUIREMENTS)
631 rmap.connect('create_repo_fields', '/{repo_name}/settings/fields/new',
624 rmap.connect('create_repo_fields', '/{repo_name}/settings/fields/new',
632 controller='admin/repos', action='create_repo_field',
625 controller='admin/repos', action='create_repo_field',
633 conditions={'method': ['PUT'], 'function': check_repo},
626 conditions={'method': ['PUT'], 'function': check_repo},
634 requirements=URL_NAME_REQUIREMENTS)
627 requirements=URL_NAME_REQUIREMENTS)
635 rmap.connect('delete_repo_fields', '/{repo_name}/settings/fields/{field_id}',
628 rmap.connect('delete_repo_fields', '/{repo_name}/settings/fields/{field_id}',
636 controller='admin/repos', action='delete_repo_field',
629 controller='admin/repos', action='delete_repo_field',
637 conditions={'method': ['DELETE'], 'function': check_repo},
630 conditions={'method': ['DELETE'], 'function': check_repo},
638 requirements=URL_NAME_REQUIREMENTS)
631 requirements=URL_NAME_REQUIREMENTS)
639
632
640 rmap.connect('toggle_locking', '/{repo_name}/settings/advanced/locking_toggle',
633 rmap.connect('toggle_locking', '/{repo_name}/settings/advanced/locking_toggle',
641 controller='admin/repos', action='toggle_locking',
634 controller='admin/repos', action='toggle_locking',
642 conditions={'method': ['GET'], 'function': check_repo},
635 conditions={'method': ['GET'], 'function': check_repo},
643 requirements=URL_NAME_REQUIREMENTS)
636 requirements=URL_NAME_REQUIREMENTS)
644
637
645 rmap.connect('edit_repo_remote', '/{repo_name}/settings/remote',
638 rmap.connect('edit_repo_remote', '/{repo_name}/settings/remote',
646 controller='admin/repos', action='edit_remote_form',
639 controller='admin/repos', action='edit_remote_form',
647 conditions={'method': ['GET'], 'function': check_repo},
640 conditions={'method': ['GET'], 'function': check_repo},
648 requirements=URL_NAME_REQUIREMENTS)
641 requirements=URL_NAME_REQUIREMENTS)
649 rmap.connect('edit_repo_remote', '/{repo_name}/settings/remote',
642 rmap.connect('edit_repo_remote', '/{repo_name}/settings/remote',
650 controller='admin/repos', action='edit_remote',
643 controller='admin/repos', action='edit_remote',
651 conditions={'method': ['PUT'], 'function': check_repo},
644 conditions={'method': ['PUT'], 'function': check_repo},
652 requirements=URL_NAME_REQUIREMENTS)
645 requirements=URL_NAME_REQUIREMENTS)
653
646
654 rmap.connect('edit_repo_statistics', '/{repo_name}/settings/statistics',
647 rmap.connect('edit_repo_statistics', '/{repo_name}/settings/statistics',
655 controller='admin/repos', action='edit_statistics_form',
648 controller='admin/repos', action='edit_statistics_form',
656 conditions={'method': ['GET'], 'function': check_repo},
649 conditions={'method': ['GET'], 'function': check_repo},
657 requirements=URL_NAME_REQUIREMENTS)
650 requirements=URL_NAME_REQUIREMENTS)
658 rmap.connect('edit_repo_statistics', '/{repo_name}/settings/statistics',
651 rmap.connect('edit_repo_statistics', '/{repo_name}/settings/statistics',
659 controller='admin/repos', action='edit_statistics',
652 controller='admin/repos', action='edit_statistics',
660 conditions={'method': ['PUT'], 'function': check_repo},
653 conditions={'method': ['PUT'], 'function': check_repo},
661 requirements=URL_NAME_REQUIREMENTS)
654 requirements=URL_NAME_REQUIREMENTS)
662 rmap.connect('repo_settings_issuetracker',
655 rmap.connect('repo_settings_issuetracker',
663 '/{repo_name}/settings/issue-tracker',
656 '/{repo_name}/settings/issue-tracker',
664 controller='admin/repos', action='repo_issuetracker',
657 controller='admin/repos', action='repo_issuetracker',
665 conditions={'method': ['GET'], 'function': check_repo},
658 conditions={'method': ['GET'], 'function': check_repo},
666 requirements=URL_NAME_REQUIREMENTS)
659 requirements=URL_NAME_REQUIREMENTS)
667 rmap.connect('repo_issuetracker_test',
660 rmap.connect('repo_issuetracker_test',
668 '/{repo_name}/settings/issue-tracker/test',
661 '/{repo_name}/settings/issue-tracker/test',
669 controller='admin/repos', action='repo_issuetracker_test',
662 controller='admin/repos', action='repo_issuetracker_test',
670 conditions={'method': ['POST'], 'function': check_repo},
663 conditions={'method': ['POST'], 'function': check_repo},
671 requirements=URL_NAME_REQUIREMENTS)
664 requirements=URL_NAME_REQUIREMENTS)
672 rmap.connect('repo_issuetracker_delete',
665 rmap.connect('repo_issuetracker_delete',
673 '/{repo_name}/settings/issue-tracker/delete',
666 '/{repo_name}/settings/issue-tracker/delete',
674 controller='admin/repos', action='repo_issuetracker_delete',
667 controller='admin/repos', action='repo_issuetracker_delete',
675 conditions={'method': ['DELETE'], 'function': check_repo},
668 conditions={'method': ['DELETE'], 'function': check_repo},
676 requirements=URL_NAME_REQUIREMENTS)
669 requirements=URL_NAME_REQUIREMENTS)
677 rmap.connect('repo_issuetracker_save',
670 rmap.connect('repo_issuetracker_save',
678 '/{repo_name}/settings/issue-tracker/save',
671 '/{repo_name}/settings/issue-tracker/save',
679 controller='admin/repos', action='repo_issuetracker_save',
672 controller='admin/repos', action='repo_issuetracker_save',
680 conditions={'method': ['POST'], 'function': check_repo},
673 conditions={'method': ['POST'], 'function': check_repo},
681 requirements=URL_NAME_REQUIREMENTS)
674 requirements=URL_NAME_REQUIREMENTS)
682 rmap.connect('repo_vcs_settings', '/{repo_name}/settings/vcs',
675 rmap.connect('repo_vcs_settings', '/{repo_name}/settings/vcs',
683 controller='admin/repos', action='repo_settings_vcs_update',
676 controller='admin/repos', action='repo_settings_vcs_update',
684 conditions={'method': ['POST'], 'function': check_repo},
677 conditions={'method': ['POST'], 'function': check_repo},
685 requirements=URL_NAME_REQUIREMENTS)
678 requirements=URL_NAME_REQUIREMENTS)
686 rmap.connect('repo_vcs_settings', '/{repo_name}/settings/vcs',
679 rmap.connect('repo_vcs_settings', '/{repo_name}/settings/vcs',
687 controller='admin/repos', action='repo_settings_vcs',
680 controller='admin/repos', action='repo_settings_vcs',
688 conditions={'method': ['GET'], 'function': check_repo},
681 conditions={'method': ['GET'], 'function': check_repo},
689 requirements=URL_NAME_REQUIREMENTS)
682 requirements=URL_NAME_REQUIREMENTS)
690 rmap.connect('repo_vcs_settings', '/{repo_name}/settings/vcs',
683 rmap.connect('repo_vcs_settings', '/{repo_name}/settings/vcs',
691 controller='admin/repos', action='repo_delete_svn_pattern',
684 controller='admin/repos', action='repo_delete_svn_pattern',
692 conditions={'method': ['DELETE'], 'function': check_repo},
685 conditions={'method': ['DELETE'], 'function': check_repo},
693 requirements=URL_NAME_REQUIREMENTS)
686 requirements=URL_NAME_REQUIREMENTS)
694 rmap.connect('repo_pullrequest_settings', '/{repo_name}/settings/pullrequest',
687 rmap.connect('repo_pullrequest_settings', '/{repo_name}/settings/pullrequest',
695 controller='admin/repos', action='repo_settings_pullrequest',
688 controller='admin/repos', action='repo_settings_pullrequest',
696 conditions={'method': ['GET', 'POST'], 'function': check_repo},
689 conditions={'method': ['GET', 'POST'], 'function': check_repo},
697 requirements=URL_NAME_REQUIREMENTS)
690 requirements=URL_NAME_REQUIREMENTS)
698
691
699 # still working url for backward compat.
692 # still working url for backward compat.
700 rmap.connect('raw_changeset_home_depraced',
693 rmap.connect('raw_changeset_home_depraced',
701 '/{repo_name}/raw-changeset/{revision}',
694 '/{repo_name}/raw-changeset/{revision}',
702 controller='changeset', action='changeset_raw',
695 controller='changeset', action='changeset_raw',
703 revision='tip', conditions={'function': check_repo},
696 revision='tip', conditions={'function': check_repo},
704 requirements=URL_NAME_REQUIREMENTS)
697 requirements=URL_NAME_REQUIREMENTS)
705
698
706 # new URLs
699 # new URLs
707 rmap.connect('changeset_raw_home',
700 rmap.connect('changeset_raw_home',
708 '/{repo_name}/changeset-diff/{revision}',
701 '/{repo_name}/changeset-diff/{revision}',
709 controller='changeset', action='changeset_raw',
702 controller='changeset', action='changeset_raw',
710 revision='tip', conditions={'function': check_repo},
703 revision='tip', conditions={'function': check_repo},
711 requirements=URL_NAME_REQUIREMENTS)
704 requirements=URL_NAME_REQUIREMENTS)
712
705
713 rmap.connect('changeset_patch_home',
706 rmap.connect('changeset_patch_home',
714 '/{repo_name}/changeset-patch/{revision}',
707 '/{repo_name}/changeset-patch/{revision}',
715 controller='changeset', action='changeset_patch',
708 controller='changeset', action='changeset_patch',
716 revision='tip', conditions={'function': check_repo},
709 revision='tip', conditions={'function': check_repo},
717 requirements=URL_NAME_REQUIREMENTS)
710 requirements=URL_NAME_REQUIREMENTS)
718
711
719 rmap.connect('changeset_download_home',
712 rmap.connect('changeset_download_home',
720 '/{repo_name}/changeset-download/{revision}',
713 '/{repo_name}/changeset-download/{revision}',
721 controller='changeset', action='changeset_download',
714 controller='changeset', action='changeset_download',
722 revision='tip', conditions={'function': check_repo},
715 revision='tip', conditions={'function': check_repo},
723 requirements=URL_NAME_REQUIREMENTS)
716 requirements=URL_NAME_REQUIREMENTS)
724
717
725 rmap.connect('changeset_comment',
718 rmap.connect('changeset_comment',
726 '/{repo_name}/changeset/{revision}/comment', jsroute=True,
719 '/{repo_name}/changeset/{revision}/comment', jsroute=True,
727 controller='changeset', revision='tip', action='comment',
720 controller='changeset', revision='tip', action='comment',
728 conditions={'function': check_repo},
721 conditions={'function': check_repo},
729 requirements=URL_NAME_REQUIREMENTS)
722 requirements=URL_NAME_REQUIREMENTS)
730
723
731 rmap.connect('changeset_comment_preview',
724 rmap.connect('changeset_comment_preview',
732 '/{repo_name}/changeset/comment/preview', jsroute=True,
725 '/{repo_name}/changeset/comment/preview', jsroute=True,
733 controller='changeset', action='preview_comment',
726 controller='changeset', action='preview_comment',
734 conditions={'function': check_repo, 'method': ['POST']},
727 conditions={'function': check_repo, 'method': ['POST']},
735 requirements=URL_NAME_REQUIREMENTS)
728 requirements=URL_NAME_REQUIREMENTS)
736
729
737 rmap.connect('changeset_comment_delete',
730 rmap.connect('changeset_comment_delete',
738 '/{repo_name}/changeset/comment/{comment_id}/delete',
731 '/{repo_name}/changeset/comment/{comment_id}/delete',
739 controller='changeset', action='delete_comment',
732 controller='changeset', action='delete_comment',
740 conditions={'function': check_repo, 'method': ['DELETE']},
733 conditions={'function': check_repo, 'method': ['DELETE']},
741 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
734 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
742
735
743 rmap.connect('changeset_info', '/{repo_name}/changeset_info/{revision}',
736 rmap.connect('changeset_info', '/{repo_name}/changeset_info/{revision}',
744 controller='changeset', action='changeset_info',
737 controller='changeset', action='changeset_info',
745 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
738 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
746
739
747 rmap.connect('compare_home',
740 rmap.connect('compare_home',
748 '/{repo_name}/compare',
741 '/{repo_name}/compare',
749 controller='compare', action='index',
742 controller='compare', action='index',
750 conditions={'function': check_repo},
743 conditions={'function': check_repo},
751 requirements=URL_NAME_REQUIREMENTS)
744 requirements=URL_NAME_REQUIREMENTS)
752
745
753 rmap.connect('compare_url',
746 rmap.connect('compare_url',
754 '/{repo_name}/compare/{source_ref_type}@{source_ref:.*?}...{target_ref_type}@{target_ref:.*?}',
747 '/{repo_name}/compare/{source_ref_type}@{source_ref:.*?}...{target_ref_type}@{target_ref:.*?}',
755 controller='compare', action='compare',
748 controller='compare', action='compare',
756 conditions={'function': check_repo},
749 conditions={'function': check_repo},
757 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
750 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
758
751
759 rmap.connect('pullrequest_home',
752 rmap.connect('pullrequest_home',
760 '/{repo_name}/pull-request/new', controller='pullrequests',
753 '/{repo_name}/pull-request/new', controller='pullrequests',
761 action='index', conditions={'function': check_repo,
754 action='index', conditions={'function': check_repo,
762 'method': ['GET']},
755 'method': ['GET']},
763 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
756 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
764
757
765 rmap.connect('pullrequest',
758 rmap.connect('pullrequest',
766 '/{repo_name}/pull-request/new', controller='pullrequests',
759 '/{repo_name}/pull-request/new', controller='pullrequests',
767 action='create', conditions={'function': check_repo,
760 action='create', conditions={'function': check_repo,
768 'method': ['POST']},
761 'method': ['POST']},
769 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
762 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
770
763
771 rmap.connect('pullrequest_repo_refs',
764 rmap.connect('pullrequest_repo_refs',
772 '/{repo_name}/pull-request/refs/{target_repo_name:.*?[^/]}',
765 '/{repo_name}/pull-request/refs/{target_repo_name:.*?[^/]}',
773 controller='pullrequests',
766 controller='pullrequests',
774 action='get_repo_refs',
767 action='get_repo_refs',
775 conditions={'function': check_repo, 'method': ['GET']},
768 conditions={'function': check_repo, 'method': ['GET']},
776 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
769 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
777
770
778 rmap.connect('pullrequest_repo_destinations',
771 rmap.connect('pullrequest_repo_destinations',
779 '/{repo_name}/pull-request/repo-destinations',
772 '/{repo_name}/pull-request/repo-destinations',
780 controller='pullrequests',
773 controller='pullrequests',
781 action='get_repo_destinations',
774 action='get_repo_destinations',
782 conditions={'function': check_repo, 'method': ['GET']},
775 conditions={'function': check_repo, 'method': ['GET']},
783 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
776 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
784
777
785 rmap.connect('pullrequest_show',
778 rmap.connect('pullrequest_show',
786 '/{repo_name}/pull-request/{pull_request_id}',
779 '/{repo_name}/pull-request/{pull_request_id}',
787 controller='pullrequests',
780 controller='pullrequests',
788 action='show', conditions={'function': check_repo,
781 action='show', conditions={'function': check_repo,
789 'method': ['GET']},
782 'method': ['GET']},
790 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
783 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
791
784
792 rmap.connect('pullrequest_update',
785 rmap.connect('pullrequest_update',
793 '/{repo_name}/pull-request/{pull_request_id}',
786 '/{repo_name}/pull-request/{pull_request_id}',
794 controller='pullrequests',
787 controller='pullrequests',
795 action='update', conditions={'function': check_repo,
788 action='update', conditions={'function': check_repo,
796 'method': ['PUT']},
789 'method': ['PUT']},
797 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
790 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
798
791
799 rmap.connect('pullrequest_merge',
792 rmap.connect('pullrequest_merge',
800 '/{repo_name}/pull-request/{pull_request_id}',
793 '/{repo_name}/pull-request/{pull_request_id}',
801 controller='pullrequests',
794 controller='pullrequests',
802 action='merge', conditions={'function': check_repo,
795 action='merge', conditions={'function': check_repo,
803 'method': ['POST']},
796 'method': ['POST']},
804 requirements=URL_NAME_REQUIREMENTS)
797 requirements=URL_NAME_REQUIREMENTS)
805
798
806 rmap.connect('pullrequest_delete',
799 rmap.connect('pullrequest_delete',
807 '/{repo_name}/pull-request/{pull_request_id}',
800 '/{repo_name}/pull-request/{pull_request_id}',
808 controller='pullrequests',
801 controller='pullrequests',
809 action='delete', conditions={'function': check_repo,
802 action='delete', conditions={'function': check_repo,
810 'method': ['DELETE']},
803 'method': ['DELETE']},
811 requirements=URL_NAME_REQUIREMENTS)
804 requirements=URL_NAME_REQUIREMENTS)
812
805
813 rmap.connect('pullrequest_comment',
806 rmap.connect('pullrequest_comment',
814 '/{repo_name}/pull-request-comment/{pull_request_id}',
807 '/{repo_name}/pull-request-comment/{pull_request_id}',
815 controller='pullrequests',
808 controller='pullrequests',
816 action='comment', conditions={'function': check_repo,
809 action='comment', conditions={'function': check_repo,
817 'method': ['POST']},
810 'method': ['POST']},
818 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
811 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
819
812
820 rmap.connect('pullrequest_comment_delete',
813 rmap.connect('pullrequest_comment_delete',
821 '/{repo_name}/pull-request-comment/{comment_id}/delete',
814 '/{repo_name}/pull-request-comment/{comment_id}/delete',
822 controller='pullrequests', action='delete_comment',
815 controller='pullrequests', action='delete_comment',
823 conditions={'function': check_repo, 'method': ['DELETE']},
816 conditions={'function': check_repo, 'method': ['DELETE']},
824 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
817 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
825
818
826 rmap.connect('changelog_home', '/{repo_name}/changelog', jsroute=True,
819 rmap.connect('changelog_home', '/{repo_name}/changelog', jsroute=True,
827 controller='changelog', conditions={'function': check_repo},
820 controller='changelog', conditions={'function': check_repo},
828 requirements=URL_NAME_REQUIREMENTS)
821 requirements=URL_NAME_REQUIREMENTS)
829
822
830 rmap.connect('changelog_file_home',
823 rmap.connect('changelog_file_home',
831 '/{repo_name}/changelog/{revision}/{f_path}',
824 '/{repo_name}/changelog/{revision}/{f_path}',
832 controller='changelog', f_path=None,
825 controller='changelog', f_path=None,
833 conditions={'function': check_repo},
826 conditions={'function': check_repo},
834 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
827 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
835
828
836 rmap.connect('changelog_elements', '/{repo_name}/changelog_details',
829 rmap.connect('changelog_elements', '/{repo_name}/changelog_details',
837 controller='changelog', action='changelog_elements',
830 controller='changelog', action='changelog_elements',
838 conditions={'function': check_repo},
831 conditions={'function': check_repo},
839 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
832 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
840
833
841 rmap.connect('files_home', '/{repo_name}/files/{revision}/{f_path}',
834 rmap.connect('files_home', '/{repo_name}/files/{revision}/{f_path}',
842 controller='files', revision='tip', f_path='',
835 controller='files', revision='tip', f_path='',
843 conditions={'function': check_repo},
836 conditions={'function': check_repo},
844 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
837 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
845
838
846 rmap.connect('files_home_simple_catchrev',
839 rmap.connect('files_home_simple_catchrev',
847 '/{repo_name}/files/{revision}',
840 '/{repo_name}/files/{revision}',
848 controller='files', revision='tip', f_path='',
841 controller='files', revision='tip', f_path='',
849 conditions={'function': check_repo},
842 conditions={'function': check_repo},
850 requirements=URL_NAME_REQUIREMENTS)
843 requirements=URL_NAME_REQUIREMENTS)
851
844
852 rmap.connect('files_home_simple_catchall',
845 rmap.connect('files_home_simple_catchall',
853 '/{repo_name}/files',
846 '/{repo_name}/files',
854 controller='files', revision='tip', f_path='',
847 controller='files', revision='tip', f_path='',
855 conditions={'function': check_repo},
848 conditions={'function': check_repo},
856 requirements=URL_NAME_REQUIREMENTS)
849 requirements=URL_NAME_REQUIREMENTS)
857
850
858 rmap.connect('files_history_home',
851 rmap.connect('files_history_home',
859 '/{repo_name}/history/{revision}/{f_path}',
852 '/{repo_name}/history/{revision}/{f_path}',
860 controller='files', action='history', revision='tip', f_path='',
853 controller='files', action='history', revision='tip', f_path='',
861 conditions={'function': check_repo},
854 conditions={'function': check_repo},
862 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
855 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
863
856
864 rmap.connect('files_authors_home',
857 rmap.connect('files_authors_home',
865 '/{repo_name}/authors/{revision}/{f_path}',
858 '/{repo_name}/authors/{revision}/{f_path}',
866 controller='files', action='authors', revision='tip', f_path='',
859 controller='files', action='authors', revision='tip', f_path='',
867 conditions={'function': check_repo},
860 conditions={'function': check_repo},
868 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
861 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
869
862
870 rmap.connect('files_diff_home', '/{repo_name}/diff/{f_path}',
863 rmap.connect('files_diff_home', '/{repo_name}/diff/{f_path}',
871 controller='files', action='diff', f_path='',
864 controller='files', action='diff', f_path='',
872 conditions={'function': check_repo},
865 conditions={'function': check_repo},
873 requirements=URL_NAME_REQUIREMENTS)
866 requirements=URL_NAME_REQUIREMENTS)
874
867
875 rmap.connect('files_diff_2way_home',
868 rmap.connect('files_diff_2way_home',
876 '/{repo_name}/diff-2way/{f_path}',
869 '/{repo_name}/diff-2way/{f_path}',
877 controller='files', action='diff_2way', f_path='',
870 controller='files', action='diff_2way', f_path='',
878 conditions={'function': check_repo},
871 conditions={'function': check_repo},
879 requirements=URL_NAME_REQUIREMENTS)
872 requirements=URL_NAME_REQUIREMENTS)
880
873
881 rmap.connect('files_rawfile_home',
874 rmap.connect('files_rawfile_home',
882 '/{repo_name}/rawfile/{revision}/{f_path}',
875 '/{repo_name}/rawfile/{revision}/{f_path}',
883 controller='files', action='rawfile', revision='tip',
876 controller='files', action='rawfile', revision='tip',
884 f_path='', conditions={'function': check_repo},
877 f_path='', conditions={'function': check_repo},
885 requirements=URL_NAME_REQUIREMENTS)
878 requirements=URL_NAME_REQUIREMENTS)
886
879
887 rmap.connect('files_raw_home',
880 rmap.connect('files_raw_home',
888 '/{repo_name}/raw/{revision}/{f_path}',
881 '/{repo_name}/raw/{revision}/{f_path}',
889 controller='files', action='raw', revision='tip', f_path='',
882 controller='files', action='raw', revision='tip', f_path='',
890 conditions={'function': check_repo},
883 conditions={'function': check_repo},
891 requirements=URL_NAME_REQUIREMENTS)
884 requirements=URL_NAME_REQUIREMENTS)
892
885
893 rmap.connect('files_render_home',
886 rmap.connect('files_render_home',
894 '/{repo_name}/render/{revision}/{f_path}',
887 '/{repo_name}/render/{revision}/{f_path}',
895 controller='files', action='index', revision='tip', f_path='',
888 controller='files', action='index', revision='tip', f_path='',
896 rendered=True, conditions={'function': check_repo},
889 rendered=True, conditions={'function': check_repo},
897 requirements=URL_NAME_REQUIREMENTS)
890 requirements=URL_NAME_REQUIREMENTS)
898
891
899 rmap.connect('files_annotate_home',
892 rmap.connect('files_annotate_home',
900 '/{repo_name}/annotate/{revision}/{f_path}',
893 '/{repo_name}/annotate/{revision}/{f_path}',
901 controller='files', action='index', revision='tip',
894 controller='files', action='index', revision='tip',
902 f_path='', annotate=True, conditions={'function': check_repo},
895 f_path='', annotate=True, conditions={'function': check_repo},
903 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
896 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
904
897
905 rmap.connect('files_annotate_previous',
898 rmap.connect('files_annotate_previous',
906 '/{repo_name}/annotate-previous/{revision}/{f_path}',
899 '/{repo_name}/annotate-previous/{revision}/{f_path}',
907 controller='files', action='annotate_previous', revision='tip',
900 controller='files', action='annotate_previous', revision='tip',
908 f_path='', annotate=True, conditions={'function': check_repo},
901 f_path='', annotate=True, conditions={'function': check_repo},
909 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
902 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
910
903
911 rmap.connect('files_edit',
904 rmap.connect('files_edit',
912 '/{repo_name}/edit/{revision}/{f_path}',
905 '/{repo_name}/edit/{revision}/{f_path}',
913 controller='files', action='edit', revision='tip',
906 controller='files', action='edit', revision='tip',
914 f_path='',
907 f_path='',
915 conditions={'function': check_repo, 'method': ['POST']},
908 conditions={'function': check_repo, 'method': ['POST']},
916 requirements=URL_NAME_REQUIREMENTS)
909 requirements=URL_NAME_REQUIREMENTS)
917
910
918 rmap.connect('files_edit_home',
911 rmap.connect('files_edit_home',
919 '/{repo_name}/edit/{revision}/{f_path}',
912 '/{repo_name}/edit/{revision}/{f_path}',
920 controller='files', action='edit_home', revision='tip',
913 controller='files', action='edit_home', revision='tip',
921 f_path='', conditions={'function': check_repo},
914 f_path='', conditions={'function': check_repo},
922 requirements=URL_NAME_REQUIREMENTS)
915 requirements=URL_NAME_REQUIREMENTS)
923
916
924 rmap.connect('files_add',
917 rmap.connect('files_add',
925 '/{repo_name}/add/{revision}/{f_path}',
918 '/{repo_name}/add/{revision}/{f_path}',
926 controller='files', action='add', revision='tip',
919 controller='files', action='add', revision='tip',
927 f_path='',
920 f_path='',
928 conditions={'function': check_repo, 'method': ['POST']},
921 conditions={'function': check_repo, 'method': ['POST']},
929 requirements=URL_NAME_REQUIREMENTS)
922 requirements=URL_NAME_REQUIREMENTS)
930
923
931 rmap.connect('files_add_home',
924 rmap.connect('files_add_home',
932 '/{repo_name}/add/{revision}/{f_path}',
925 '/{repo_name}/add/{revision}/{f_path}',
933 controller='files', action='add_home', revision='tip',
926 controller='files', action='add_home', revision='tip',
934 f_path='', conditions={'function': check_repo},
927 f_path='', conditions={'function': check_repo},
935 requirements=URL_NAME_REQUIREMENTS)
928 requirements=URL_NAME_REQUIREMENTS)
936
929
937 rmap.connect('files_delete',
930 rmap.connect('files_delete',
938 '/{repo_name}/delete/{revision}/{f_path}',
931 '/{repo_name}/delete/{revision}/{f_path}',
939 controller='files', action='delete', revision='tip',
932 controller='files', action='delete', revision='tip',
940 f_path='',
933 f_path='',
941 conditions={'function': check_repo, 'method': ['POST']},
934 conditions={'function': check_repo, 'method': ['POST']},
942 requirements=URL_NAME_REQUIREMENTS)
935 requirements=URL_NAME_REQUIREMENTS)
943
936
944 rmap.connect('files_delete_home',
937 rmap.connect('files_delete_home',
945 '/{repo_name}/delete/{revision}/{f_path}',
938 '/{repo_name}/delete/{revision}/{f_path}',
946 controller='files', action='delete_home', revision='tip',
939 controller='files', action='delete_home', revision='tip',
947 f_path='', conditions={'function': check_repo},
940 f_path='', conditions={'function': check_repo},
948 requirements=URL_NAME_REQUIREMENTS)
941 requirements=URL_NAME_REQUIREMENTS)
949
942
950 rmap.connect('files_archive_home', '/{repo_name}/archive/{fname}',
943 rmap.connect('files_archive_home', '/{repo_name}/archive/{fname}',
951 controller='files', action='archivefile',
944 controller='files', action='archivefile',
952 conditions={'function': check_repo},
945 conditions={'function': check_repo},
953 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
946 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
954
947
955 rmap.connect('files_nodelist_home',
948 rmap.connect('files_nodelist_home',
956 '/{repo_name}/nodelist/{revision}/{f_path}',
949 '/{repo_name}/nodelist/{revision}/{f_path}',
957 controller='files', action='nodelist',
950 controller='files', action='nodelist',
958 conditions={'function': check_repo},
951 conditions={'function': check_repo},
959 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
952 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
960
953
961 rmap.connect('files_nodetree_full',
954 rmap.connect('files_nodetree_full',
962 '/{repo_name}/nodetree_full/{commit_id}/{f_path}',
955 '/{repo_name}/nodetree_full/{commit_id}/{f_path}',
963 controller='files', action='nodetree_full',
956 controller='files', action='nodetree_full',
964 conditions={'function': check_repo},
957 conditions={'function': check_repo},
965 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
958 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
966
959
967 rmap.connect('repo_fork_create_home', '/{repo_name}/fork',
960 rmap.connect('repo_fork_create_home', '/{repo_name}/fork',
968 controller='forks', action='fork_create',
961 controller='forks', action='fork_create',
969 conditions={'function': check_repo, 'method': ['POST']},
962 conditions={'function': check_repo, 'method': ['POST']},
970 requirements=URL_NAME_REQUIREMENTS)
963 requirements=URL_NAME_REQUIREMENTS)
971
964
972 rmap.connect('repo_fork_home', '/{repo_name}/fork',
965 rmap.connect('repo_fork_home', '/{repo_name}/fork',
973 controller='forks', action='fork',
966 controller='forks', action='fork',
974 conditions={'function': check_repo},
967 conditions={'function': check_repo},
975 requirements=URL_NAME_REQUIREMENTS)
968 requirements=URL_NAME_REQUIREMENTS)
976
969
977 rmap.connect('repo_forks_home', '/{repo_name}/forks',
970 rmap.connect('repo_forks_home', '/{repo_name}/forks',
978 controller='forks', action='forks',
971 controller='forks', action='forks',
979 conditions={'function': check_repo},
972 conditions={'function': check_repo},
980 requirements=URL_NAME_REQUIREMENTS)
973 requirements=URL_NAME_REQUIREMENTS)
981
974
982 return rmap
975 return rmap
@@ -1,330 +1,294 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2013-2017 RhodeCode GmbH
3 # Copyright (C) 2013-2017 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 """
22 """
23 my account controller for RhodeCode admin
23 my account controller for RhodeCode admin
24 """
24 """
25
25
26 import logging
26 import logging
27
27
28 import formencode
28 import formencode
29 from formencode import htmlfill
29 from formencode import htmlfill
30 from pyramid.httpexceptions import HTTPFound
30 from pyramid.httpexceptions import HTTPFound
31
31
32 from pylons import request, tmpl_context as c, url
32 from pylons import request, tmpl_context as c, url
33 from pylons.controllers.util import redirect
33 from pylons.controllers.util import redirect
34 from pylons.i18n.translation import _
34 from pylons.i18n.translation import _
35 from sqlalchemy.orm import joinedload
35 from sqlalchemy.orm import joinedload
36
36
37 from rhodecode.lib import helpers as h
37 from rhodecode.lib import helpers as h
38 from rhodecode.lib import auth
38 from rhodecode.lib import auth
39 from rhodecode.lib.auth import (
39 from rhodecode.lib.auth import (
40 LoginRequired, NotAnonymous, AuthUser)
40 LoginRequired, NotAnonymous, AuthUser)
41 from rhodecode.lib.base import BaseController, render
41 from rhodecode.lib.base import BaseController, render
42 from rhodecode.lib.utils import jsonify
42 from rhodecode.lib.utils import jsonify
43 from rhodecode.lib.utils2 import safe_int, str2bool
43 from rhodecode.lib.utils2 import safe_int, str2bool
44 from rhodecode.lib.ext_json import json
44 from rhodecode.lib.ext_json import json
45
45
46 from rhodecode.model.db import (
46 from rhodecode.model.db import (
47 Repository, PullRequest, UserEmailMap, User, UserFollowing)
47 Repository, PullRequest, UserEmailMap, User, UserFollowing)
48 from rhodecode.model.forms import UserForm
48 from rhodecode.model.forms import UserForm
49 from rhodecode.model.scm import RepoList
49 from rhodecode.model.scm import RepoList
50 from rhodecode.model.user import UserModel
50 from rhodecode.model.user import UserModel
51 from rhodecode.model.repo import RepoModel
51 from rhodecode.model.repo import RepoModel
52 from rhodecode.model.meta import Session
52 from rhodecode.model.meta import Session
53 from rhodecode.model.pull_request import PullRequestModel
53 from rhodecode.model.pull_request import PullRequestModel
54 from rhodecode.model.comment import CommentsModel
54 from rhodecode.model.comment import CommentsModel
55
55
56 log = logging.getLogger(__name__)
56 log = logging.getLogger(__name__)
57
57
58
58
59 class MyAccountController(BaseController):
59 class MyAccountController(BaseController):
60 """REST Controller styled on the Atom Publishing Protocol"""
60 """REST Controller styled on the Atom Publishing Protocol"""
61 # To properly map this controller, ensure your config/routing.py
61 # To properly map this controller, ensure your config/routing.py
62 # file has a resource setup:
62 # file has a resource setup:
63 # map.resource('setting', 'settings', controller='admin/settings',
63 # map.resource('setting', 'settings', controller='admin/settings',
64 # path_prefix='/admin', name_prefix='admin_')
64 # path_prefix='/admin', name_prefix='admin_')
65
65
66 @LoginRequired()
66 @LoginRequired()
67 @NotAnonymous()
67 @NotAnonymous()
68 def __before__(self):
68 def __before__(self):
69 super(MyAccountController, self).__before__()
69 super(MyAccountController, self).__before__()
70
70
71 def __load_data(self):
71 def __load_data(self):
72 c.user = User.get(c.rhodecode_user.user_id)
72 c.user = User.get(c.rhodecode_user.user_id)
73 if c.user.username == User.DEFAULT_USER:
73 if c.user.username == User.DEFAULT_USER:
74 h.flash(_("You can't edit this user since it's"
74 h.flash(_("You can't edit this user since it's"
75 " crucial for entire application"), category='warning')
75 " crucial for entire application"), category='warning')
76 return redirect(h.route_path('users'))
76 return redirect(h.route_path('users'))
77
77
78 c.auth_user = AuthUser(
78 c.auth_user = AuthUser(
79 user_id=c.rhodecode_user.user_id, ip_addr=self.ip_addr)
79 user_id=c.rhodecode_user.user_id, ip_addr=self.ip_addr)
80
80
81 def _load_my_repos_data(self, watched=False):
81 def _load_my_repos_data(self, watched=False):
82 if watched:
82 if watched:
83 admin = False
83 admin = False
84 follows_repos = Session().query(UserFollowing)\
84 follows_repos = Session().query(UserFollowing)\
85 .filter(UserFollowing.user_id == c.rhodecode_user.user_id)\
85 .filter(UserFollowing.user_id == c.rhodecode_user.user_id)\
86 .options(joinedload(UserFollowing.follows_repository))\
86 .options(joinedload(UserFollowing.follows_repository))\
87 .all()
87 .all()
88 repo_list = [x.follows_repository for x in follows_repos]
88 repo_list = [x.follows_repository for x in follows_repos]
89 else:
89 else:
90 admin = True
90 admin = True
91 repo_list = Repository.get_all_repos(
91 repo_list = Repository.get_all_repos(
92 user_id=c.rhodecode_user.user_id)
92 user_id=c.rhodecode_user.user_id)
93 repo_list = RepoList(repo_list, perm_set=[
93 repo_list = RepoList(repo_list, perm_set=[
94 'repository.read', 'repository.write', 'repository.admin'])
94 'repository.read', 'repository.write', 'repository.admin'])
95
95
96 repos_data = RepoModel().get_repos_as_dict(
96 repos_data = RepoModel().get_repos_as_dict(
97 repo_list=repo_list, admin=admin)
97 repo_list=repo_list, admin=admin)
98 # json used to render the grid
98 # json used to render the grid
99 return json.dumps(repos_data)
99 return json.dumps(repos_data)
100
100
101 @auth.CSRFRequired()
101 @auth.CSRFRequired()
102 def my_account_update(self):
102 def my_account_update(self):
103 """
103 """
104 POST /_admin/my_account Updates info of my account
104 POST /_admin/my_account Updates info of my account
105 """
105 """
106 # url('my_account')
106 # url('my_account')
107 c.active = 'profile_edit'
107 c.active = 'profile_edit'
108 self.__load_data()
108 self.__load_data()
109 c.perm_user = c.auth_user
109 c.perm_user = c.auth_user
110 c.extern_type = c.user.extern_type
110 c.extern_type = c.user.extern_type
111 c.extern_name = c.user.extern_name
111 c.extern_name = c.user.extern_name
112
112
113 defaults = c.user.get_dict()
113 defaults = c.user.get_dict()
114 update = False
114 update = False
115 _form = UserForm(edit=True,
115 _form = UserForm(edit=True,
116 old_data={'user_id': c.rhodecode_user.user_id,
116 old_data={'user_id': c.rhodecode_user.user_id,
117 'email': c.rhodecode_user.email})()
117 'email': c.rhodecode_user.email})()
118 form_result = {}
118 form_result = {}
119 try:
119 try:
120 post_data = dict(request.POST)
120 post_data = dict(request.POST)
121 post_data['new_password'] = ''
121 post_data['new_password'] = ''
122 post_data['password_confirmation'] = ''
122 post_data['password_confirmation'] = ''
123 form_result = _form.to_python(post_data)
123 form_result = _form.to_python(post_data)
124 # skip updating those attrs for my account
124 # skip updating those attrs for my account
125 skip_attrs = ['admin', 'active', 'extern_type', 'extern_name',
125 skip_attrs = ['admin', 'active', 'extern_type', 'extern_name',
126 'new_password', 'password_confirmation']
126 'new_password', 'password_confirmation']
127 # TODO: plugin should define if username can be updated
127 # TODO: plugin should define if username can be updated
128 if c.extern_type != "rhodecode":
128 if c.extern_type != "rhodecode":
129 # forbid updating username for external accounts
129 # forbid updating username for external accounts
130 skip_attrs.append('username')
130 skip_attrs.append('username')
131
131
132 UserModel().update_user(
132 UserModel().update_user(
133 c.rhodecode_user.user_id, skip_attrs=skip_attrs, **form_result)
133 c.rhodecode_user.user_id, skip_attrs=skip_attrs, **form_result)
134 h.flash(_('Your account was updated successfully'),
134 h.flash(_('Your account was updated successfully'),
135 category='success')
135 category='success')
136 Session().commit()
136 Session().commit()
137 update = True
137 update = True
138
138
139 except formencode.Invalid as errors:
139 except formencode.Invalid as errors:
140 return htmlfill.render(
140 return htmlfill.render(
141 render('admin/my_account/my_account.mako'),
141 render('admin/my_account/my_account.mako'),
142 defaults=errors.value,
142 defaults=errors.value,
143 errors=errors.error_dict or {},
143 errors=errors.error_dict or {},
144 prefix_error=False,
144 prefix_error=False,
145 encoding="UTF-8",
145 encoding="UTF-8",
146 force_defaults=False)
146 force_defaults=False)
147 except Exception:
147 except Exception:
148 log.exception("Exception updating user")
148 log.exception("Exception updating user")
149 h.flash(_('Error occurred during update of user %s')
149 h.flash(_('Error occurred during update of user %s')
150 % form_result.get('username'), category='error')
150 % form_result.get('username'), category='error')
151
151
152 if update:
152 if update:
153 raise HTTPFound(h.route_path('my_account_profile'))
153 raise HTTPFound(h.route_path('my_account_profile'))
154
154
155 return htmlfill.render(
155 return htmlfill.render(
156 render('admin/my_account/my_account.mako'),
156 render('admin/my_account/my_account.mako'),
157 defaults=defaults,
157 defaults=defaults,
158 encoding="UTF-8",
158 encoding="UTF-8",
159 force_defaults=False
159 force_defaults=False
160 )
160 )
161
161
162 def my_account_edit(self):
162 def my_account_edit(self):
163 """
163 """
164 GET /_admin/my_account/edit Displays edit form of my account
164 GET /_admin/my_account/edit Displays edit form of my account
165 """
165 """
166 c.active = 'profile_edit'
166 c.active = 'profile_edit'
167 self.__load_data()
167 self.__load_data()
168 c.perm_user = c.auth_user
168 c.perm_user = c.auth_user
169 c.extern_type = c.user.extern_type
169 c.extern_type = c.user.extern_type
170 c.extern_name = c.user.extern_name
170 c.extern_name = c.user.extern_name
171
171
172 defaults = c.user.get_dict()
172 defaults = c.user.get_dict()
173 return htmlfill.render(
173 return htmlfill.render(
174 render('admin/my_account/my_account.mako'),
174 render('admin/my_account/my_account.mako'),
175 defaults=defaults,
175 defaults=defaults,
176 encoding="UTF-8",
176 encoding="UTF-8",
177 force_defaults=False
177 force_defaults=False
178 )
178 )
179
179
180 def my_account_repos(self):
180 def my_account_repos(self):
181 c.active = 'repos'
181 c.active = 'repos'
182 self.__load_data()
182 self.__load_data()
183
183
184 # json used to render the grid
184 # json used to render the grid
185 c.data = self._load_my_repos_data()
185 c.data = self._load_my_repos_data()
186 return render('admin/my_account/my_account.mako')
186 return render('admin/my_account/my_account.mako')
187
187
188 def my_account_watched(self):
188 def my_account_watched(self):
189 c.active = 'watched'
189 c.active = 'watched'
190 self.__load_data()
190 self.__load_data()
191
191
192 # json used to render the grid
192 # json used to render the grid
193 c.data = self._load_my_repos_data(watched=True)
193 c.data = self._load_my_repos_data(watched=True)
194 return render('admin/my_account/my_account.mako')
194 return render('admin/my_account/my_account.mako')
195
195
196 def my_account_perms(self):
196 def my_account_perms(self):
197 c.active = 'perms'
197 c.active = 'perms'
198 self.__load_data()
198 self.__load_data()
199 c.perm_user = c.auth_user
199 c.perm_user = c.auth_user
200
200
201 return render('admin/my_account/my_account.mako')
201 return render('admin/my_account/my_account.mako')
202
202
203 def my_account_emails(self):
204 c.active = 'emails'
205 self.__load_data()
206
207 c.user_email_map = UserEmailMap.query()\
208 .filter(UserEmailMap.user == c.user).all()
209 return render('admin/my_account/my_account.mako')
210
211 @auth.CSRFRequired()
212 def my_account_emails_add(self):
213 email = request.POST.get('new_email')
214
215 try:
216 UserModel().add_extra_email(c.rhodecode_user.user_id, email)
217 Session().commit()
218 h.flash(_("Added new email address `%s` for user account") % email,
219 category='success')
220 except formencode.Invalid as error:
221 msg = error.error_dict['email']
222 h.flash(msg, category='error')
223 except Exception:
224 log.exception("Exception in my_account_emails")
225 h.flash(_('An error occurred during email saving'),
226 category='error')
227 return redirect(url('my_account_emails'))
228
229 @auth.CSRFRequired()
230 def my_account_emails_delete(self):
231 email_id = request.POST.get('del_email_id')
232 user_model = UserModel()
233 user_model.delete_extra_email(c.rhodecode_user.user_id, email_id)
234 Session().commit()
235 h.flash(_("Removed email address from user account"),
236 category='success')
237 return redirect(url('my_account_emails'))
238
239 def _extract_ordering(self, request):
203 def _extract_ordering(self, request):
240 column_index = safe_int(request.GET.get('order[0][column]'))
204 column_index = safe_int(request.GET.get('order[0][column]'))
241 order_dir = request.GET.get('order[0][dir]', 'desc')
205 order_dir = request.GET.get('order[0][dir]', 'desc')
242 order_by = request.GET.get(
206 order_by = request.GET.get(
243 'columns[%s][data][sort]' % column_index, 'name_raw')
207 'columns[%s][data][sort]' % column_index, 'name_raw')
244 return order_by, order_dir
208 return order_by, order_dir
245
209
246 def _get_pull_requests_list(self, statuses):
210 def _get_pull_requests_list(self, statuses):
247 start = safe_int(request.GET.get('start'), 0)
211 start = safe_int(request.GET.get('start'), 0)
248 length = safe_int(request.GET.get('length'), c.visual.dashboard_items)
212 length = safe_int(request.GET.get('length'), c.visual.dashboard_items)
249 order_by, order_dir = self._extract_ordering(request)
213 order_by, order_dir = self._extract_ordering(request)
250
214
251 pull_requests = PullRequestModel().get_im_participating_in(
215 pull_requests = PullRequestModel().get_im_participating_in(
252 user_id=c.rhodecode_user.user_id,
216 user_id=c.rhodecode_user.user_id,
253 statuses=statuses,
217 statuses=statuses,
254 offset=start, length=length, order_by=order_by,
218 offset=start, length=length, order_by=order_by,
255 order_dir=order_dir)
219 order_dir=order_dir)
256
220
257 pull_requests_total_count = PullRequestModel().count_im_participating_in(
221 pull_requests_total_count = PullRequestModel().count_im_participating_in(
258 user_id=c.rhodecode_user.user_id, statuses=statuses)
222 user_id=c.rhodecode_user.user_id, statuses=statuses)
259
223
260 from rhodecode.lib.utils import PartialRenderer
224 from rhodecode.lib.utils import PartialRenderer
261 _render = PartialRenderer('data_table/_dt_elements.mako')
225 _render = PartialRenderer('data_table/_dt_elements.mako')
262 data = []
226 data = []
263 for pr in pull_requests:
227 for pr in pull_requests:
264 repo_id = pr.target_repo_id
228 repo_id = pr.target_repo_id
265 comments = CommentsModel().get_all_comments(
229 comments = CommentsModel().get_all_comments(
266 repo_id, pull_request=pr)
230 repo_id, pull_request=pr)
267 owned = pr.user_id == c.rhodecode_user.user_id
231 owned = pr.user_id == c.rhodecode_user.user_id
268 status = pr.calculated_review_status()
232 status = pr.calculated_review_status()
269
233
270 data.append({
234 data.append({
271 'target_repo': _render('pullrequest_target_repo',
235 'target_repo': _render('pullrequest_target_repo',
272 pr.target_repo.repo_name),
236 pr.target_repo.repo_name),
273 'name': _render('pullrequest_name',
237 'name': _render('pullrequest_name',
274 pr.pull_request_id, pr.target_repo.repo_name,
238 pr.pull_request_id, pr.target_repo.repo_name,
275 short=True),
239 short=True),
276 'name_raw': pr.pull_request_id,
240 'name_raw': pr.pull_request_id,
277 'status': _render('pullrequest_status', status),
241 'status': _render('pullrequest_status', status),
278 'title': _render(
242 'title': _render(
279 'pullrequest_title', pr.title, pr.description),
243 'pullrequest_title', pr.title, pr.description),
280 'description': h.escape(pr.description),
244 'description': h.escape(pr.description),
281 'updated_on': _render('pullrequest_updated_on',
245 'updated_on': _render('pullrequest_updated_on',
282 h.datetime_to_time(pr.updated_on)),
246 h.datetime_to_time(pr.updated_on)),
283 'updated_on_raw': h.datetime_to_time(pr.updated_on),
247 'updated_on_raw': h.datetime_to_time(pr.updated_on),
284 'created_on': _render('pullrequest_updated_on',
248 'created_on': _render('pullrequest_updated_on',
285 h.datetime_to_time(pr.created_on)),
249 h.datetime_to_time(pr.created_on)),
286 'created_on_raw': h.datetime_to_time(pr.created_on),
250 'created_on_raw': h.datetime_to_time(pr.created_on),
287 'author': _render('pullrequest_author',
251 'author': _render('pullrequest_author',
288 pr.author.full_contact, ),
252 pr.author.full_contact, ),
289 'author_raw': pr.author.full_name,
253 'author_raw': pr.author.full_name,
290 'comments': _render('pullrequest_comments', len(comments)),
254 'comments': _render('pullrequest_comments', len(comments)),
291 'comments_raw': len(comments),
255 'comments_raw': len(comments),
292 'closed': pr.is_closed(),
256 'closed': pr.is_closed(),
293 'owned': owned
257 'owned': owned
294 })
258 })
295 # json used to render the grid
259 # json used to render the grid
296 data = ({
260 data = ({
297 'data': data,
261 'data': data,
298 'recordsTotal': pull_requests_total_count,
262 'recordsTotal': pull_requests_total_count,
299 'recordsFiltered': pull_requests_total_count,
263 'recordsFiltered': pull_requests_total_count,
300 })
264 })
301 return data
265 return data
302
266
303 def my_account_pullrequests(self):
267 def my_account_pullrequests(self):
304 c.active = 'pullrequests'
268 c.active = 'pullrequests'
305 self.__load_data()
269 self.__load_data()
306 c.show_closed = str2bool(request.GET.get('pr_show_closed'))
270 c.show_closed = str2bool(request.GET.get('pr_show_closed'))
307
271
308 statuses = [PullRequest.STATUS_NEW, PullRequest.STATUS_OPEN]
272 statuses = [PullRequest.STATUS_NEW, PullRequest.STATUS_OPEN]
309 if c.show_closed:
273 if c.show_closed:
310 statuses += [PullRequest.STATUS_CLOSED]
274 statuses += [PullRequest.STATUS_CLOSED]
311 data = self._get_pull_requests_list(statuses)
275 data = self._get_pull_requests_list(statuses)
312 if not request.is_xhr:
276 if not request.is_xhr:
313 c.data_participate = json.dumps(data['data'])
277 c.data_participate = json.dumps(data['data'])
314 c.records_total_participate = data['recordsTotal']
278 c.records_total_participate = data['recordsTotal']
315 return render('admin/my_account/my_account.mako')
279 return render('admin/my_account/my_account.mako')
316 else:
280 else:
317 return json.dumps(data)
281 return json.dumps(data)
318
282
319 def my_notifications(self):
283 def my_notifications(self):
320 c.active = 'notifications'
284 c.active = 'notifications'
321 return render('admin/my_account/my_account.mako')
285 return render('admin/my_account/my_account.mako')
322
286
323 @auth.CSRFRequired()
287 @auth.CSRFRequired()
324 @jsonify
288 @jsonify
325 def my_notifications_toggle_visibility(self):
289 def my_notifications_toggle_visibility(self):
326 user = c.rhodecode_user.get_instance()
290 user = c.rhodecode_user.get_instance()
327 new_status = not user.user_data.get('notification_status', True)
291 new_status = not user.user_data.get('notification_status', True)
328 user.update_userdata(notification_status=new_status)
292 user.update_userdata(notification_status=new_status)
329 Session().commit()
293 Session().commit()
330 return user.user_data['notification_status']
294 return user.user_data['notification_status']
@@ -1,52 +1,52 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 ## TODO: Find a better integration of oauth views into navigation.
32 ## TODO: Find a better integration of oauth views into navigation.
33 <% my_account_oauth_url = h.route_path_or_none('my_account_oauth') %>
33 <% my_account_oauth_url = h.route_path_or_none('my_account_oauth') %>
34 % if my_account_oauth_url:
34 % if my_account_oauth_url:
35 <li class="${'active' if c.active=='oauth' else ''}"><a href="${my_account_oauth_url}">${_('OAuth Identities')}</a></li>
35 <li class="${'active' if c.active=='oauth' else ''}"><a href="${my_account_oauth_url}">${_('OAuth Identities')}</a></li>
36 % endif
36 % endif
37 <li class="${'active' if c.active=='emails' else ''}"><a href="${h.url('my_account_emails')}">${_('Emails')}</a></li>
37 <li class="${'active' if c.active=='emails' else ''}"><a href="${h.route_path('my_account_emails')}">${_('Emails')}</a></li>
38 <li class="${'active' if c.active=='repos' else ''}"><a href="${h.url('my_account_repos')}">${_('Repositories')}</a></li>
38 <li class="${'active' if c.active=='repos' else ''}"><a href="${h.url('my_account_repos')}">${_('Repositories')}</a></li>
39 <li class="${'active' if c.active=='watched' else ''}"><a href="${h.url('my_account_watched')}">${_('Watched')}</a></li>
39 <li class="${'active' if c.active=='watched' else ''}"><a href="${h.url('my_account_watched')}">${_('Watched')}</a></li>
40 <li class="${'active' if c.active=='pullrequests' else ''}"><a href="${h.url('my_account_pullrequests')}">${_('Pull Requests')}</a></li>
40 <li class="${'active' if c.active=='pullrequests' else ''}"><a href="${h.url('my_account_pullrequests')}">${_('Pull Requests')}</a></li>
41 <li class="${'active' if c.active=='perms' else ''}"><a href="${h.url('my_account_perms')}">${_('Permissions')}</a></li>
41 <li class="${'active' if c.active=='perms' else ''}"><a href="${h.url('my_account_perms')}">${_('Permissions')}</a></li>
42 <li class="${'active' if c.active=='my_notifications' else ''}"><a href="${h.url('my_account_notifications')}">${_('Live Notifications')}</a></li>
42 <li class="${'active' if c.active=='my_notifications' else ''}"><a href="${h.url('my_account_notifications')}">${_('Live Notifications')}</a></li>
43 </ul>
43 </ul>
44 </div>
44 </div>
45
45
46 <div class="main-content-full-width">
46 <div class="main-content-full-width">
47 <%include file="/admin/my_account/my_account_${c.active}.mako"/>
47 <%include file="/admin/my_account/my_account_${c.active}.mako"/>
48 </div>
48 </div>
49 </div>
49 </div>
50 </div>
50 </div>
51
51
52 </%def>
52 </%def>
@@ -1,72 +1,72 b''
1 <%namespace name="base" file="/base/base.mako"/>
1 <%namespace name="base" file="/base/base.mako"/>
2
2
3 <div class="panel panel-default">
3 <div class="panel panel-default">
4 <div class="panel-heading">
4 <div class="panel-heading">
5 <h3 class="panel-title">${_('Account Emails')}</h3>
5 <h3 class="panel-title">${_('Account Emails')}</h3>
6 </div>
6 </div>
7
7
8 <div class="panel-body">
8 <div class="panel-body">
9 <div class="emails_wrap">
9 <div class="emails_wrap">
10 <table class="rctable account_emails">
10 <table class="rctable account_emails">
11 <tr>
11 <tr>
12 <td class="td-user">
12 <td class="td-user">
13 ${base.gravatar(c.user.email, 16)}
13 ${base.gravatar(c.user.email, 16)}
14 <span class="user email">${c.user.email}</span>
14 <span class="user email">${c.user.email}</span>
15 </td>
15 </td>
16 <td class="td-tags">
16 <td class="td-tags">
17 <span class="tag tag1">${_('Primary')}</span>
17 <span class="tag tag1">${_('Primary')}</span>
18 </td>
18 </td>
19 </tr>
19 </tr>
20 %if c.user_email_map:
20 %if c.user_email_map:
21 %for em in c.user_email_map:
21 %for em in c.user_email_map:
22 <tr>
22 <tr>
23 <td class="td-user">
23 <td class="td-user">
24 ${base.gravatar(em.email, 16)}
24 ${base.gravatar(em.email, 16)}
25 <span class="user email">${em.email}</span>
25 <span class="user email">${em.email}</span>
26 </td>
26 </td>
27 <td class="td-action">
27 <td class="td-action">
28 ${h.secure_form(url('my_account_emails'),method='delete')}
28 ${h.secure_form(h.route_path('my_account_emails_delete'), method='POST')}
29 ${h.hidden('del_email_id',em.email_id)}
29 ${h.hidden('del_email_id',em.email_id)}
30 <button class="btn btn-link btn-danger" type="submit" id="remove_email_%s" % em.email_id
30 <button class="btn btn-link btn-danger" type="submit" id="${'remove_email_%s'.format(em.email_id)}"
31 onclick="return confirm('${_('Confirm to delete this email: %s') % em.email}');">
31 onclick="return confirm('${_('Confirm to delete this email: {}').format(em.email)}');">
32 ${_('Delete')}
32 ${_('Delete')}
33 </button>
33 </button>
34 ${h.end_form()}
34 ${h.end_form()}
35 </td>
35 </td>
36 </tr>
36 </tr>
37 %endfor
37 %endfor
38 %else:
38 %else:
39 <tr class="noborder">
39 <tr class="noborder">
40 <td colspan="3">
40 <td colspan="3">
41 <div class="td-email">
41 <div class="td-email">
42 ${_('No additional emails specified')}
42 ${_('No additional emails specified')}
43 </div>
43 </div>
44 </td>
44 </td>
45 </tr>
45 </tr>
46 %endif
46 %endif
47 </table>
47 </table>
48 </div>
48 </div>
49
49
50 <div>
50 <div>
51 ${h.secure_form(url('my_account_emails'), method='post')}
51 ${h.secure_form(h.route_path('my_account_emails_add'), method='POST')}
52 <div class="form">
52 <div class="form">
53 <!-- fields -->
53 <!-- fields -->
54 <div class="fields">
54 <div class="fields">
55 <div class="field">
55 <div class="field">
56 <div class="label">
56 <div class="label">
57 <label for="new_email">${_('New email address')}:</label>
57 <label for="new_email">${_('New email address')}:</label>
58 </div>
58 </div>
59 <div class="input">
59 <div class="input">
60 ${h.text('new_email', class_='medium')}
60 ${h.text('new_email', class_='medium')}
61 </div>
61 </div>
62 </div>
62 </div>
63 <div class="buttons">
63 <div class="buttons">
64 ${h.submit('save',_('Add'),class_="btn")}
64 ${h.submit('save',_('Add'),class_="btn")}
65 ${h.reset('reset',_('Reset'),class_="btn")}
65 ${h.reset('reset',_('Reset'),class_="btn")}
66 </div>
66 </div>
67 </div>
67 </div>
68 </div>
68 </div>
69 ${h.end_form()}
69 ${h.end_form()}
70 </div>
70 </div>
71 </div>
71 </div>
72 </div>
72 </div>
@@ -1,253 +1,203 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2017 RhodeCode GmbH
3 # Copyright (C) 2010-2017 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 pytest
21 import pytest
22
22
23 from rhodecode.lib import helpers as h
23 from rhodecode.lib import helpers as h
24 from rhodecode.model.db import User, UserFollowing, Repository
24 from rhodecode.model.db import User, UserFollowing, Repository
25 from rhodecode.tests import (
25 from rhodecode.tests import (
26 TestController, url, TEST_USER_ADMIN_LOGIN, TEST_USER_REGULAR_EMAIL,
26 TestController, url, TEST_USER_ADMIN_LOGIN, assert_session_flash)
27 assert_session_flash)
28 from rhodecode.tests.fixture import Fixture
27 from rhodecode.tests.fixture import Fixture
29 from rhodecode.tests.utils import AssertResponse
28 from rhodecode.tests.utils import AssertResponse
30
29
31 fixture = Fixture()
30 fixture = Fixture()
32
31
33
32
34 def route_path(name, **kwargs):
33 def route_path(name, **kwargs):
35 return {
34 return {
36 'home': '/',
35 'home': '/',
37 }[name].format(**kwargs)
36 }[name].format(**kwargs)
38
37
39
38
40 class TestMyAccountController(TestController):
39 class TestMyAccountController(TestController):
41 test_user_1 = 'testme'
40 test_user_1 = 'testme'
42 test_user_1_password = '0jd83nHNS/d23n'
41 test_user_1_password = '0jd83nHNS/d23n'
43 destroy_users = set()
42 destroy_users = set()
44
43
45 @classmethod
44 @classmethod
46 def teardown_class(cls):
45 def teardown_class(cls):
47 fixture.destroy_users(cls.destroy_users)
46 fixture.destroy_users(cls.destroy_users)
48
47
49 def test_logout_form_contains_csrf(self, autologin_user, csrf_token):
48 def test_logout_form_contains_csrf(self, autologin_user, csrf_token):
50 response = self.app.get(route_path('home'))
49 response = self.app.get(route_path('home'))
51 assert_response = AssertResponse(response)
50 assert_response = AssertResponse(response)
52 element = assert_response.get_element('.logout #csrf_token')
51 element = assert_response.get_element('.logout #csrf_token')
53 assert element.value == csrf_token
52 assert element.value == csrf_token
54
53
55 def test_my_account_edit(self):
54 def test_my_account_edit(self):
56 self.log_user()
55 self.log_user()
57 response = self.app.get(url('my_account_edit'))
56 response = self.app.get(url('my_account_edit'))
58
57
59 response.mustcontain('value="test_admin')
58 response.mustcontain('value="test_admin')
60
59
61 def test_my_account_my_repos(self):
60 def test_my_account_my_repos(self):
62 self.log_user()
61 self.log_user()
63 response = self.app.get(url('my_account_repos'))
62 response = self.app.get(url('my_account_repos'))
64 repos = Repository.query().filter(
63 repos = Repository.query().filter(
65 Repository.user == User.get_by_username(
64 Repository.user == User.get_by_username(
66 TEST_USER_ADMIN_LOGIN)).all()
65 TEST_USER_ADMIN_LOGIN)).all()
67 for repo in repos:
66 for repo in repos:
68 response.mustcontain('"name_raw": "%s"' % repo.repo_name)
67 response.mustcontain('"name_raw": "%s"' % repo.repo_name)
69
68
70 def test_my_account_my_watched(self):
69 def test_my_account_my_watched(self):
71 self.log_user()
70 self.log_user()
72 response = self.app.get(url('my_account_watched'))
71 response = self.app.get(url('my_account_watched'))
73
72
74 repos = UserFollowing.query().filter(
73 repos = UserFollowing.query().filter(
75 UserFollowing.user == User.get_by_username(
74 UserFollowing.user == User.get_by_username(
76 TEST_USER_ADMIN_LOGIN)).all()
75 TEST_USER_ADMIN_LOGIN)).all()
77 for repo in repos:
76 for repo in repos:
78 response.mustcontain(
77 response.mustcontain(
79 '"name_raw": "%s"' % repo.follows_repository.repo_name)
78 '"name_raw": "%s"' % repo.follows_repository.repo_name)
80
79
81 @pytest.mark.backends("git", "hg")
80 @pytest.mark.backends("git", "hg")
82 def test_my_account_my_pullrequests(self, pr_util):
81 def test_my_account_my_pullrequests(self, pr_util):
83 self.log_user()
82 self.log_user()
84 response = self.app.get(url('my_account_pullrequests'))
83 response = self.app.get(url('my_account_pullrequests'))
85 response.mustcontain('There are currently no open pull '
84 response.mustcontain('There are currently no open pull '
86 'requests requiring your participation.')
85 'requests requiring your participation.')
87
86
88 pr = pr_util.create_pull_request(title='TestMyAccountPR')
87 pr = pr_util.create_pull_request(title='TestMyAccountPR')
89 response = self.app.get(url('my_account_pullrequests'))
88 response = self.app.get(url('my_account_pullrequests'))
90 response.mustcontain('"name_raw": %s' % pr.pull_request_id)
89 response.mustcontain('"name_raw": %s' % pr.pull_request_id)
91 response.mustcontain('TestMyAccountPR')
90 response.mustcontain('TestMyAccountPR')
92
91
93 def test_my_account_my_emails(self):
94 self.log_user()
95 response = self.app.get(url('my_account_emails'))
96 response.mustcontain('No additional emails specified')
97
92
98 def test_my_account_my_emails_add_existing_email(self):
99 self.log_user()
100 response = self.app.get(url('my_account_emails'))
101 response.mustcontain('No additional emails specified')
102 response = self.app.post(url('my_account_emails'),
103 {'new_email': TEST_USER_REGULAR_EMAIL,
104 'csrf_token': self.csrf_token})
105 assert_session_flash(response, 'This e-mail address is already taken')
106
107 def test_my_account_my_emails_add_mising_email_in_form(self):
108 self.log_user()
109 response = self.app.get(url('my_account_emails'))
110 response.mustcontain('No additional emails specified')
111 response = self.app.post(url('my_account_emails'),
112 {'csrf_token': self.csrf_token})
113 assert_session_flash(response, 'Please enter an email address')
114
115 def test_my_account_my_emails_add_remove(self):
116 self.log_user()
117 response = self.app.get(url('my_account_emails'))
118 response.mustcontain('No additional emails specified')
119
120 response = self.app.post(url('my_account_emails'),
121 {'new_email': 'foo@barz.com',
122 'csrf_token': self.csrf_token})
123
124 response = self.app.get(url('my_account_emails'))
125
126 from rhodecode.model.db import UserEmailMap
127 email_id = UserEmailMap.query().filter(
128 UserEmailMap.user == User.get_by_username(
129 TEST_USER_ADMIN_LOGIN)).filter(
130 UserEmailMap.email == 'foo@barz.com').one().email_id
131
132 response.mustcontain('foo@barz.com')
133 response.mustcontain('<input id="del_email_id" name="del_email_id" '
134 'type="hidden" value="%s" />' % email_id)
135
136 response = self.app.post(
137 url('my_account_emails'), {
138 'del_email_id': email_id, '_method': 'delete',
139 'csrf_token': self.csrf_token})
140 assert_session_flash(response, 'Removed email address from user account')
141 response = self.app.get(url('my_account_emails'))
142 response.mustcontain('No additional emails specified')
143
93
144 @pytest.mark.parametrize(
94 @pytest.mark.parametrize(
145 "name, attrs", [
95 "name, attrs", [
146 ('firstname', {'firstname': 'new_username'}),
96 ('firstname', {'firstname': 'new_username'}),
147 ('lastname', {'lastname': 'new_username'}),
97 ('lastname', {'lastname': 'new_username'}),
148 ('admin', {'admin': True}),
98 ('admin', {'admin': True}),
149 ('admin', {'admin': False}),
99 ('admin', {'admin': False}),
150 ('extern_type', {'extern_type': 'ldap'}),
100 ('extern_type', {'extern_type': 'ldap'}),
151 ('extern_type', {'extern_type': None}),
101 ('extern_type', {'extern_type': None}),
152 # ('extern_name', {'extern_name': 'test'}),
102 # ('extern_name', {'extern_name': 'test'}),
153 # ('extern_name', {'extern_name': None}),
103 # ('extern_name', {'extern_name': None}),
154 ('active', {'active': False}),
104 ('active', {'active': False}),
155 ('active', {'active': True}),
105 ('active', {'active': True}),
156 ('email', {'email': 'some@email.com'}),
106 ('email', {'email': 'some@email.com'}),
157 ])
107 ])
158 def test_my_account_update(self, name, attrs):
108 def test_my_account_update(self, name, attrs):
159 usr = fixture.create_user(self.test_user_1,
109 usr = fixture.create_user(self.test_user_1,
160 password=self.test_user_1_password,
110 password=self.test_user_1_password,
161 email='testme@rhodecode.org',
111 email='testme@rhodecode.org',
162 extern_type='rhodecode',
112 extern_type='rhodecode',
163 extern_name=self.test_user_1,
113 extern_name=self.test_user_1,
164 skip_if_exists=True)
114 skip_if_exists=True)
165 self.destroy_users.add(self.test_user_1)
115 self.destroy_users.add(self.test_user_1)
166
116
167 params = usr.get_api_data() # current user data
117 params = usr.get_api_data() # current user data
168 user_id = usr.user_id
118 user_id = usr.user_id
169 self.log_user(
119 self.log_user(
170 username=self.test_user_1, password=self.test_user_1_password)
120 username=self.test_user_1, password=self.test_user_1_password)
171
121
172 params.update({'password_confirmation': ''})
122 params.update({'password_confirmation': ''})
173 params.update({'new_password': ''})
123 params.update({'new_password': ''})
174 params.update({'extern_type': 'rhodecode'})
124 params.update({'extern_type': 'rhodecode'})
175 params.update({'extern_name': self.test_user_1})
125 params.update({'extern_name': self.test_user_1})
176 params.update({'csrf_token': self.csrf_token})
126 params.update({'csrf_token': self.csrf_token})
177
127
178 params.update(attrs)
128 params.update(attrs)
179 # my account page cannot set language param yet, only for admins
129 # my account page cannot set language param yet, only for admins
180 del params['language']
130 del params['language']
181 response = self.app.post(url('my_account'), params)
131 response = self.app.post(url('my_account'), params)
182
132
183 assert_session_flash(
133 assert_session_flash(
184 response, 'Your account was updated successfully')
134 response, 'Your account was updated successfully')
185
135
186 del params['csrf_token']
136 del params['csrf_token']
187
137
188 updated_user = User.get_by_username(self.test_user_1)
138 updated_user = User.get_by_username(self.test_user_1)
189 updated_params = updated_user.get_api_data()
139 updated_params = updated_user.get_api_data()
190 updated_params.update({'password_confirmation': ''})
140 updated_params.update({'password_confirmation': ''})
191 updated_params.update({'new_password': ''})
141 updated_params.update({'new_password': ''})
192
142
193 params['last_login'] = updated_params['last_login']
143 params['last_login'] = updated_params['last_login']
194 params['last_activity'] = updated_params['last_activity']
144 params['last_activity'] = updated_params['last_activity']
195 # my account page cannot set language param yet, only for admins
145 # my account page cannot set language param yet, only for admins
196 # but we get this info from API anyway
146 # but we get this info from API anyway
197 params['language'] = updated_params['language']
147 params['language'] = updated_params['language']
198
148
199 if name == 'email':
149 if name == 'email':
200 params['emails'] = [attrs['email']]
150 params['emails'] = [attrs['email']]
201 if name == 'extern_type':
151 if name == 'extern_type':
202 # cannot update this via form, expected value is original one
152 # cannot update this via form, expected value is original one
203 params['extern_type'] = "rhodecode"
153 params['extern_type'] = "rhodecode"
204 if name == 'extern_name':
154 if name == 'extern_name':
205 # cannot update this via form, expected value is original one
155 # cannot update this via form, expected value is original one
206 params['extern_name'] = str(user_id)
156 params['extern_name'] = str(user_id)
207 if name == 'active':
157 if name == 'active':
208 # my account cannot deactivate account
158 # my account cannot deactivate account
209 params['active'] = True
159 params['active'] = True
210 if name == 'admin':
160 if name == 'admin':
211 # my account cannot make you an admin !
161 # my account cannot make you an admin !
212 params['admin'] = False
162 params['admin'] = False
213
163
214 assert params == updated_params
164 assert params == updated_params
215
165
216 def test_my_account_update_err_email_exists(self):
166 def test_my_account_update_err_email_exists(self):
217 self.log_user()
167 self.log_user()
218
168
219 new_email = 'test_regular@mail.com' # already exisitn email
169 new_email = 'test_regular@mail.com' # already exisitn email
220 response = self.app.post(url('my_account'),
170 response = self.app.post(url('my_account'),
221 params={
171 params={
222 'username': 'test_admin',
172 'username': 'test_admin',
223 'new_password': 'test12',
173 'new_password': 'test12',
224 'password_confirmation': 'test122',
174 'password_confirmation': 'test122',
225 'firstname': 'NewName',
175 'firstname': 'NewName',
226 'lastname': 'NewLastname',
176 'lastname': 'NewLastname',
227 'email': new_email,
177 'email': new_email,
228 'csrf_token': self.csrf_token,
178 'csrf_token': self.csrf_token,
229 })
179 })
230
180
231 response.mustcontain('This e-mail address is already taken')
181 response.mustcontain('This e-mail address is already taken')
232
182
233 def test_my_account_update_err(self):
183 def test_my_account_update_err(self):
234 self.log_user('test_regular2', 'test12')
184 self.log_user('test_regular2', 'test12')
235
185
236 new_email = 'newmail.pl'
186 new_email = 'newmail.pl'
237 response = self.app.post(url('my_account'),
187 response = self.app.post(url('my_account'),
238 params={
188 params={
239 'username': 'test_admin',
189 'username': 'test_admin',
240 'new_password': 'test12',
190 'new_password': 'test12',
241 'password_confirmation': 'test122',
191 'password_confirmation': 'test122',
242 'firstname': 'NewName',
192 'firstname': 'NewName',
243 'lastname': 'NewLastname',
193 'lastname': 'NewLastname',
244 'email': new_email,
194 'email': new_email,
245 'csrf_token': self.csrf_token,
195 'csrf_token': self.csrf_token,
246 })
196 })
247
197
248 response.mustcontain('An email address must contain a single @')
198 response.mustcontain('An email address must contain a single @')
249 from rhodecode.model import validators
199 from rhodecode.model import validators
250 msg = validators.ValidUsername(
200 msg = validators.ValidUsername(
251 edit=False, old_data={})._messages['username_exists']
201 edit=False, old_data={})._messages['username_exists']
252 msg = h.html_escape(msg % {'username': 'test_admin'})
202 msg = h.html_escape(msg % {'username': 'test_admin'})
253 response.mustcontain(u"%s" % msg)
203 response.mustcontain(u"%s" % msg)
General Comments 0
You need to be logged in to leave comments. Login now