##// END OF EJS Templates
user-bookmarks: add ${server_url} template to be used in the links.
dan -
r3526:0c9e5d56 default
parent child Browse files
Show More
@@ -1,740 +1,743 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2019 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 import string
23 import string
24
24
25 import formencode
25 import formencode
26 import formencode.htmlfill
26 import formencode.htmlfill
27 import peppercorn
27 import peppercorn
28 from pyramid.httpexceptions import HTTPFound
28 from pyramid.httpexceptions import HTTPFound
29 from pyramid.view import view_config
29 from pyramid.view import view_config
30
30
31 from rhodecode.apps._base import BaseAppView, DataGridAppView
31 from rhodecode.apps._base import BaseAppView, DataGridAppView
32 from rhodecode import forms
32 from rhodecode import forms
33 from rhodecode.lib import helpers as h
33 from rhodecode.lib import helpers as h
34 from rhodecode.lib import audit_logger
34 from rhodecode.lib import audit_logger
35 from rhodecode.lib.ext_json import json
35 from rhodecode.lib.ext_json import json
36 from rhodecode.lib.auth import LoginRequired, NotAnonymous, CSRFRequired, \
36 from rhodecode.lib.auth import LoginRequired, NotAnonymous, CSRFRequired, \
37 HasRepoPermissionAny, HasRepoGroupPermissionAny
37 HasRepoPermissionAny, HasRepoGroupPermissionAny
38 from rhodecode.lib.channelstream import (
38 from rhodecode.lib.channelstream import (
39 channelstream_request, ChannelstreamException)
39 channelstream_request, ChannelstreamException)
40 from rhodecode.lib.utils2 import safe_int, md5, str2bool
40 from rhodecode.lib.utils2 import safe_int, md5, str2bool
41 from rhodecode.model.auth_token import AuthTokenModel
41 from rhodecode.model.auth_token import AuthTokenModel
42 from rhodecode.model.comment import CommentsModel
42 from rhodecode.model.comment import CommentsModel
43 from rhodecode.model.db import (
43 from rhodecode.model.db import (
44 IntegrityError, joinedload,
44 IntegrityError, joinedload,
45 Repository, UserEmailMap, UserApiKeys, UserFollowing,
45 Repository, UserEmailMap, UserApiKeys, UserFollowing,
46 PullRequest, UserBookmark, RepoGroup)
46 PullRequest, UserBookmark, RepoGroup)
47 from rhodecode.model.meta import Session
47 from rhodecode.model.meta import Session
48 from rhodecode.model.pull_request import PullRequestModel
48 from rhodecode.model.pull_request import PullRequestModel
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.user_group import UserGroupModel
52 from rhodecode.model.user_group import UserGroupModel
53 from rhodecode.model.validation_schema.schemas import user_schema
53 from rhodecode.model.validation_schema.schemas import user_schema
54
54
55 log = logging.getLogger(__name__)
55 log = logging.getLogger(__name__)
56
56
57
57
58 class MyAccountView(BaseAppView, DataGridAppView):
58 class MyAccountView(BaseAppView, DataGridAppView):
59 ALLOW_SCOPED_TOKENS = False
59 ALLOW_SCOPED_TOKENS = False
60 """
60 """
61 This view has alternative version inside EE, if modified please take a look
61 This view has alternative version inside EE, if modified please take a look
62 in there as well.
62 in there as well.
63 """
63 """
64
64
65 def load_default_context(self):
65 def load_default_context(self):
66 c = self._get_local_tmpl_context()
66 c = self._get_local_tmpl_context()
67 c.user = c.auth_user.get_instance()
67 c.user = c.auth_user.get_instance()
68 c.allow_scoped_tokens = self.ALLOW_SCOPED_TOKENS
68 c.allow_scoped_tokens = self.ALLOW_SCOPED_TOKENS
69
69
70 return c
70 return c
71
71
72 @LoginRequired()
72 @LoginRequired()
73 @NotAnonymous()
73 @NotAnonymous()
74 @view_config(
74 @view_config(
75 route_name='my_account_profile', request_method='GET',
75 route_name='my_account_profile', request_method='GET',
76 renderer='rhodecode:templates/admin/my_account/my_account.mako')
76 renderer='rhodecode:templates/admin/my_account/my_account.mako')
77 def my_account_profile(self):
77 def my_account_profile(self):
78 c = self.load_default_context()
78 c = self.load_default_context()
79 c.active = 'profile'
79 c.active = 'profile'
80 return self._get_template_context(c)
80 return self._get_template_context(c)
81
81
82 @LoginRequired()
82 @LoginRequired()
83 @NotAnonymous()
83 @NotAnonymous()
84 @view_config(
84 @view_config(
85 route_name='my_account_password', request_method='GET',
85 route_name='my_account_password', request_method='GET',
86 renderer='rhodecode:templates/admin/my_account/my_account.mako')
86 renderer='rhodecode:templates/admin/my_account/my_account.mako')
87 def my_account_password(self):
87 def my_account_password(self):
88 c = self.load_default_context()
88 c = self.load_default_context()
89 c.active = 'password'
89 c.active = 'password'
90 c.extern_type = c.user.extern_type
90 c.extern_type = c.user.extern_type
91
91
92 schema = user_schema.ChangePasswordSchema().bind(
92 schema = user_schema.ChangePasswordSchema().bind(
93 username=c.user.username)
93 username=c.user.username)
94
94
95 form = forms.Form(
95 form = forms.Form(
96 schema,
96 schema,
97 action=h.route_path('my_account_password_update'),
97 action=h.route_path('my_account_password_update'),
98 buttons=(forms.buttons.save, forms.buttons.reset))
98 buttons=(forms.buttons.save, forms.buttons.reset))
99
99
100 c.form = form
100 c.form = form
101 return self._get_template_context(c)
101 return self._get_template_context(c)
102
102
103 @LoginRequired()
103 @LoginRequired()
104 @NotAnonymous()
104 @NotAnonymous()
105 @CSRFRequired()
105 @CSRFRequired()
106 @view_config(
106 @view_config(
107 route_name='my_account_password_update', request_method='POST',
107 route_name='my_account_password_update', request_method='POST',
108 renderer='rhodecode:templates/admin/my_account/my_account.mako')
108 renderer='rhodecode:templates/admin/my_account/my_account.mako')
109 def my_account_password_update(self):
109 def my_account_password_update(self):
110 _ = self.request.translate
110 _ = self.request.translate
111 c = self.load_default_context()
111 c = self.load_default_context()
112 c.active = 'password'
112 c.active = 'password'
113 c.extern_type = c.user.extern_type
113 c.extern_type = c.user.extern_type
114
114
115 schema = user_schema.ChangePasswordSchema().bind(
115 schema = user_schema.ChangePasswordSchema().bind(
116 username=c.user.username)
116 username=c.user.username)
117
117
118 form = forms.Form(
118 form = forms.Form(
119 schema, buttons=(forms.buttons.save, forms.buttons.reset))
119 schema, buttons=(forms.buttons.save, forms.buttons.reset))
120
120
121 if c.extern_type != 'rhodecode':
121 if c.extern_type != 'rhodecode':
122 raise HTTPFound(self.request.route_path('my_account_password'))
122 raise HTTPFound(self.request.route_path('my_account_password'))
123
123
124 controls = self.request.POST.items()
124 controls = self.request.POST.items()
125 try:
125 try:
126 valid_data = form.validate(controls)
126 valid_data = form.validate(controls)
127 UserModel().update_user(c.user.user_id, **valid_data)
127 UserModel().update_user(c.user.user_id, **valid_data)
128 c.user.update_userdata(force_password_change=False)
128 c.user.update_userdata(force_password_change=False)
129 Session().commit()
129 Session().commit()
130 except forms.ValidationFailure as e:
130 except forms.ValidationFailure as e:
131 c.form = e
131 c.form = e
132 return self._get_template_context(c)
132 return self._get_template_context(c)
133
133
134 except Exception:
134 except Exception:
135 log.exception("Exception updating password")
135 log.exception("Exception updating password")
136 h.flash(_('Error occurred during update of user password'),
136 h.flash(_('Error occurred during update of user password'),
137 category='error')
137 category='error')
138 else:
138 else:
139 instance = c.auth_user.get_instance()
139 instance = c.auth_user.get_instance()
140 self.session.setdefault('rhodecode_user', {}).update(
140 self.session.setdefault('rhodecode_user', {}).update(
141 {'password': md5(instance.password)})
141 {'password': md5(instance.password)})
142 self.session.save()
142 self.session.save()
143 h.flash(_("Successfully updated password"), category='success')
143 h.flash(_("Successfully updated password"), category='success')
144
144
145 raise HTTPFound(self.request.route_path('my_account_password'))
145 raise HTTPFound(self.request.route_path('my_account_password'))
146
146
147 @LoginRequired()
147 @LoginRequired()
148 @NotAnonymous()
148 @NotAnonymous()
149 @view_config(
149 @view_config(
150 route_name='my_account_auth_tokens', request_method='GET',
150 route_name='my_account_auth_tokens', request_method='GET',
151 renderer='rhodecode:templates/admin/my_account/my_account.mako')
151 renderer='rhodecode:templates/admin/my_account/my_account.mako')
152 def my_account_auth_tokens(self):
152 def my_account_auth_tokens(self):
153 _ = self.request.translate
153 _ = self.request.translate
154
154
155 c = self.load_default_context()
155 c = self.load_default_context()
156 c.active = 'auth_tokens'
156 c.active = 'auth_tokens'
157 c.lifetime_values = AuthTokenModel.get_lifetime_values(translator=_)
157 c.lifetime_values = AuthTokenModel.get_lifetime_values(translator=_)
158 c.role_values = [
158 c.role_values = [
159 (x, AuthTokenModel.cls._get_role_name(x))
159 (x, AuthTokenModel.cls._get_role_name(x))
160 for x in AuthTokenModel.cls.ROLES]
160 for x in AuthTokenModel.cls.ROLES]
161 c.role_options = [(c.role_values, _("Role"))]
161 c.role_options = [(c.role_values, _("Role"))]
162 c.user_auth_tokens = AuthTokenModel().get_auth_tokens(
162 c.user_auth_tokens = AuthTokenModel().get_auth_tokens(
163 c.user.user_id, show_expired=True)
163 c.user.user_id, show_expired=True)
164 c.role_vcs = AuthTokenModel.cls.ROLE_VCS
164 c.role_vcs = AuthTokenModel.cls.ROLE_VCS
165 return self._get_template_context(c)
165 return self._get_template_context(c)
166
166
167 def maybe_attach_token_scope(self, token):
167 def maybe_attach_token_scope(self, token):
168 # implemented in EE edition
168 # implemented in EE edition
169 pass
169 pass
170
170
171 @LoginRequired()
171 @LoginRequired()
172 @NotAnonymous()
172 @NotAnonymous()
173 @CSRFRequired()
173 @CSRFRequired()
174 @view_config(
174 @view_config(
175 route_name='my_account_auth_tokens_add', request_method='POST',)
175 route_name='my_account_auth_tokens_add', request_method='POST',)
176 def my_account_auth_tokens_add(self):
176 def my_account_auth_tokens_add(self):
177 _ = self.request.translate
177 _ = self.request.translate
178 c = self.load_default_context()
178 c = self.load_default_context()
179
179
180 lifetime = safe_int(self.request.POST.get('lifetime'), -1)
180 lifetime = safe_int(self.request.POST.get('lifetime'), -1)
181 description = self.request.POST.get('description')
181 description = self.request.POST.get('description')
182 role = self.request.POST.get('role')
182 role = self.request.POST.get('role')
183
183
184 token = UserModel().add_auth_token(
184 token = UserModel().add_auth_token(
185 user=c.user.user_id,
185 user=c.user.user_id,
186 lifetime_minutes=lifetime, role=role, description=description,
186 lifetime_minutes=lifetime, role=role, description=description,
187 scope_callback=self.maybe_attach_token_scope)
187 scope_callback=self.maybe_attach_token_scope)
188 token_data = token.get_api_data()
188 token_data = token.get_api_data()
189
189
190 audit_logger.store_web(
190 audit_logger.store_web(
191 'user.edit.token.add', action_data={
191 'user.edit.token.add', action_data={
192 'data': {'token': token_data, 'user': 'self'}},
192 'data': {'token': token_data, 'user': 'self'}},
193 user=self._rhodecode_user, )
193 user=self._rhodecode_user, )
194 Session().commit()
194 Session().commit()
195
195
196 h.flash(_("Auth token successfully created"), category='success')
196 h.flash(_("Auth token successfully created"), category='success')
197 return HTTPFound(h.route_path('my_account_auth_tokens'))
197 return HTTPFound(h.route_path('my_account_auth_tokens'))
198
198
199 @LoginRequired()
199 @LoginRequired()
200 @NotAnonymous()
200 @NotAnonymous()
201 @CSRFRequired()
201 @CSRFRequired()
202 @view_config(
202 @view_config(
203 route_name='my_account_auth_tokens_delete', request_method='POST')
203 route_name='my_account_auth_tokens_delete', request_method='POST')
204 def my_account_auth_tokens_delete(self):
204 def my_account_auth_tokens_delete(self):
205 _ = self.request.translate
205 _ = self.request.translate
206 c = self.load_default_context()
206 c = self.load_default_context()
207
207
208 del_auth_token = self.request.POST.get('del_auth_token')
208 del_auth_token = self.request.POST.get('del_auth_token')
209
209
210 if del_auth_token:
210 if del_auth_token:
211 token = UserApiKeys.get_or_404(del_auth_token)
211 token = UserApiKeys.get_or_404(del_auth_token)
212 token_data = token.get_api_data()
212 token_data = token.get_api_data()
213
213
214 AuthTokenModel().delete(del_auth_token, c.user.user_id)
214 AuthTokenModel().delete(del_auth_token, c.user.user_id)
215 audit_logger.store_web(
215 audit_logger.store_web(
216 'user.edit.token.delete', action_data={
216 'user.edit.token.delete', action_data={
217 'data': {'token': token_data, 'user': 'self'}},
217 'data': {'token': token_data, 'user': 'self'}},
218 user=self._rhodecode_user,)
218 user=self._rhodecode_user,)
219 Session().commit()
219 Session().commit()
220 h.flash(_("Auth token successfully deleted"), category='success')
220 h.flash(_("Auth token successfully deleted"), category='success')
221
221
222 return HTTPFound(h.route_path('my_account_auth_tokens'))
222 return HTTPFound(h.route_path('my_account_auth_tokens'))
223
223
224 @LoginRequired()
224 @LoginRequired()
225 @NotAnonymous()
225 @NotAnonymous()
226 @view_config(
226 @view_config(
227 route_name='my_account_emails', request_method='GET',
227 route_name='my_account_emails', request_method='GET',
228 renderer='rhodecode:templates/admin/my_account/my_account.mako')
228 renderer='rhodecode:templates/admin/my_account/my_account.mako')
229 def my_account_emails(self):
229 def my_account_emails(self):
230 _ = self.request.translate
230 _ = self.request.translate
231
231
232 c = self.load_default_context()
232 c = self.load_default_context()
233 c.active = 'emails'
233 c.active = 'emails'
234
234
235 c.user_email_map = UserEmailMap.query()\
235 c.user_email_map = UserEmailMap.query()\
236 .filter(UserEmailMap.user == c.user).all()
236 .filter(UserEmailMap.user == c.user).all()
237
237
238 schema = user_schema.AddEmailSchema().bind(
238 schema = user_schema.AddEmailSchema().bind(
239 username=c.user.username, user_emails=c.user.emails)
239 username=c.user.username, user_emails=c.user.emails)
240
240
241 form = forms.RcForm(schema,
241 form = forms.RcForm(schema,
242 action=h.route_path('my_account_emails_add'),
242 action=h.route_path('my_account_emails_add'),
243 buttons=(forms.buttons.save, forms.buttons.reset))
243 buttons=(forms.buttons.save, forms.buttons.reset))
244
244
245 c.form = form
245 c.form = form
246 return self._get_template_context(c)
246 return self._get_template_context(c)
247
247
248 @LoginRequired()
248 @LoginRequired()
249 @NotAnonymous()
249 @NotAnonymous()
250 @CSRFRequired()
250 @CSRFRequired()
251 @view_config(
251 @view_config(
252 route_name='my_account_emails_add', request_method='POST',
252 route_name='my_account_emails_add', request_method='POST',
253 renderer='rhodecode:templates/admin/my_account/my_account.mako')
253 renderer='rhodecode:templates/admin/my_account/my_account.mako')
254 def my_account_emails_add(self):
254 def my_account_emails_add(self):
255 _ = self.request.translate
255 _ = self.request.translate
256 c = self.load_default_context()
256 c = self.load_default_context()
257 c.active = 'emails'
257 c.active = 'emails'
258
258
259 schema = user_schema.AddEmailSchema().bind(
259 schema = user_schema.AddEmailSchema().bind(
260 username=c.user.username, user_emails=c.user.emails)
260 username=c.user.username, user_emails=c.user.emails)
261
261
262 form = forms.RcForm(
262 form = forms.RcForm(
263 schema, action=h.route_path('my_account_emails_add'),
263 schema, action=h.route_path('my_account_emails_add'),
264 buttons=(forms.buttons.save, forms.buttons.reset))
264 buttons=(forms.buttons.save, forms.buttons.reset))
265
265
266 controls = self.request.POST.items()
266 controls = self.request.POST.items()
267 try:
267 try:
268 valid_data = form.validate(controls)
268 valid_data = form.validate(controls)
269 UserModel().add_extra_email(c.user.user_id, valid_data['email'])
269 UserModel().add_extra_email(c.user.user_id, valid_data['email'])
270 audit_logger.store_web(
270 audit_logger.store_web(
271 'user.edit.email.add', action_data={
271 'user.edit.email.add', action_data={
272 'data': {'email': valid_data['email'], 'user': 'self'}},
272 'data': {'email': valid_data['email'], 'user': 'self'}},
273 user=self._rhodecode_user,)
273 user=self._rhodecode_user,)
274 Session().commit()
274 Session().commit()
275 except formencode.Invalid as error:
275 except formencode.Invalid as error:
276 h.flash(h.escape(error.error_dict['email']), category='error')
276 h.flash(h.escape(error.error_dict['email']), category='error')
277 except forms.ValidationFailure as e:
277 except forms.ValidationFailure as e:
278 c.user_email_map = UserEmailMap.query() \
278 c.user_email_map = UserEmailMap.query() \
279 .filter(UserEmailMap.user == c.user).all()
279 .filter(UserEmailMap.user == c.user).all()
280 c.form = e
280 c.form = e
281 return self._get_template_context(c)
281 return self._get_template_context(c)
282 except Exception:
282 except Exception:
283 log.exception("Exception adding email")
283 log.exception("Exception adding email")
284 h.flash(_('Error occurred during adding email'),
284 h.flash(_('Error occurred during adding email'),
285 category='error')
285 category='error')
286 else:
286 else:
287 h.flash(_("Successfully added email"), category='success')
287 h.flash(_("Successfully added email"), category='success')
288
288
289 raise HTTPFound(self.request.route_path('my_account_emails'))
289 raise HTTPFound(self.request.route_path('my_account_emails'))
290
290
291 @LoginRequired()
291 @LoginRequired()
292 @NotAnonymous()
292 @NotAnonymous()
293 @CSRFRequired()
293 @CSRFRequired()
294 @view_config(
294 @view_config(
295 route_name='my_account_emails_delete', request_method='POST')
295 route_name='my_account_emails_delete', request_method='POST')
296 def my_account_emails_delete(self):
296 def my_account_emails_delete(self):
297 _ = self.request.translate
297 _ = self.request.translate
298 c = self.load_default_context()
298 c = self.load_default_context()
299
299
300 del_email_id = self.request.POST.get('del_email_id')
300 del_email_id = self.request.POST.get('del_email_id')
301 if del_email_id:
301 if del_email_id:
302 email = UserEmailMap.get_or_404(del_email_id).email
302 email = UserEmailMap.get_or_404(del_email_id).email
303 UserModel().delete_extra_email(c.user.user_id, del_email_id)
303 UserModel().delete_extra_email(c.user.user_id, del_email_id)
304 audit_logger.store_web(
304 audit_logger.store_web(
305 'user.edit.email.delete', action_data={
305 'user.edit.email.delete', action_data={
306 'data': {'email': email, 'user': 'self'}},
306 'data': {'email': email, 'user': 'self'}},
307 user=self._rhodecode_user,)
307 user=self._rhodecode_user,)
308 Session().commit()
308 Session().commit()
309 h.flash(_("Email successfully deleted"),
309 h.flash(_("Email successfully deleted"),
310 category='success')
310 category='success')
311 return HTTPFound(h.route_path('my_account_emails'))
311 return HTTPFound(h.route_path('my_account_emails'))
312
312
313 @LoginRequired()
313 @LoginRequired()
314 @NotAnonymous()
314 @NotAnonymous()
315 @CSRFRequired()
315 @CSRFRequired()
316 @view_config(
316 @view_config(
317 route_name='my_account_notifications_test_channelstream',
317 route_name='my_account_notifications_test_channelstream',
318 request_method='POST', renderer='json_ext')
318 request_method='POST', renderer='json_ext')
319 def my_account_notifications_test_channelstream(self):
319 def my_account_notifications_test_channelstream(self):
320 message = 'Test message sent via Channelstream by user: {}, on {}'.format(
320 message = 'Test message sent via Channelstream by user: {}, on {}'.format(
321 self._rhodecode_user.username, datetime.datetime.now())
321 self._rhodecode_user.username, datetime.datetime.now())
322 payload = {
322 payload = {
323 # 'channel': 'broadcast',
323 # 'channel': 'broadcast',
324 'type': 'message',
324 'type': 'message',
325 'timestamp': datetime.datetime.utcnow(),
325 'timestamp': datetime.datetime.utcnow(),
326 'user': 'system',
326 'user': 'system',
327 'pm_users': [self._rhodecode_user.username],
327 'pm_users': [self._rhodecode_user.username],
328 'message': {
328 'message': {
329 'message': message,
329 'message': message,
330 'level': 'info',
330 'level': 'info',
331 'topic': '/notifications'
331 'topic': '/notifications'
332 }
332 }
333 }
333 }
334
334
335 registry = self.request.registry
335 registry = self.request.registry
336 rhodecode_plugins = getattr(registry, 'rhodecode_plugins', {})
336 rhodecode_plugins = getattr(registry, 'rhodecode_plugins', {})
337 channelstream_config = rhodecode_plugins.get('channelstream', {})
337 channelstream_config = rhodecode_plugins.get('channelstream', {})
338
338
339 try:
339 try:
340 channelstream_request(channelstream_config, [payload], '/message')
340 channelstream_request(channelstream_config, [payload], '/message')
341 except ChannelstreamException as e:
341 except ChannelstreamException as e:
342 log.exception('Failed to send channelstream data')
342 log.exception('Failed to send channelstream data')
343 return {"response": 'ERROR: {}'.format(e.__class__.__name__)}
343 return {"response": 'ERROR: {}'.format(e.__class__.__name__)}
344 return {"response": 'Channelstream data sent. '
344 return {"response": 'Channelstream data sent. '
345 'You should see a new live message now.'}
345 'You should see a new live message now.'}
346
346
347 def _load_my_repos_data(self, watched=False):
347 def _load_my_repos_data(self, watched=False):
348 if watched:
348 if watched:
349 admin = False
349 admin = False
350 follows_repos = Session().query(UserFollowing)\
350 follows_repos = Session().query(UserFollowing)\
351 .filter(UserFollowing.user_id == self._rhodecode_user.user_id)\
351 .filter(UserFollowing.user_id == self._rhodecode_user.user_id)\
352 .options(joinedload(UserFollowing.follows_repository))\
352 .options(joinedload(UserFollowing.follows_repository))\
353 .all()
353 .all()
354 repo_list = [x.follows_repository for x in follows_repos]
354 repo_list = [x.follows_repository for x in follows_repos]
355 else:
355 else:
356 admin = True
356 admin = True
357 repo_list = Repository.get_all_repos(
357 repo_list = Repository.get_all_repos(
358 user_id=self._rhodecode_user.user_id)
358 user_id=self._rhodecode_user.user_id)
359 repo_list = RepoList(repo_list, perm_set=[
359 repo_list = RepoList(repo_list, perm_set=[
360 'repository.read', 'repository.write', 'repository.admin'])
360 'repository.read', 'repository.write', 'repository.admin'])
361
361
362 repos_data = RepoModel().get_repos_as_dict(
362 repos_data = RepoModel().get_repos_as_dict(
363 repo_list=repo_list, admin=admin)
363 repo_list=repo_list, admin=admin)
364 # json used to render the grid
364 # json used to render the grid
365 return json.dumps(repos_data)
365 return json.dumps(repos_data)
366
366
367 @LoginRequired()
367 @LoginRequired()
368 @NotAnonymous()
368 @NotAnonymous()
369 @view_config(
369 @view_config(
370 route_name='my_account_repos', request_method='GET',
370 route_name='my_account_repos', request_method='GET',
371 renderer='rhodecode:templates/admin/my_account/my_account.mako')
371 renderer='rhodecode:templates/admin/my_account/my_account.mako')
372 def my_account_repos(self):
372 def my_account_repos(self):
373 c = self.load_default_context()
373 c = self.load_default_context()
374 c.active = 'repos'
374 c.active = 'repos'
375
375
376 # json used to render the grid
376 # json used to render the grid
377 c.data = self._load_my_repos_data()
377 c.data = self._load_my_repos_data()
378 return self._get_template_context(c)
378 return self._get_template_context(c)
379
379
380 @LoginRequired()
380 @LoginRequired()
381 @NotAnonymous()
381 @NotAnonymous()
382 @view_config(
382 @view_config(
383 route_name='my_account_watched', request_method='GET',
383 route_name='my_account_watched', request_method='GET',
384 renderer='rhodecode:templates/admin/my_account/my_account.mako')
384 renderer='rhodecode:templates/admin/my_account/my_account.mako')
385 def my_account_watched(self):
385 def my_account_watched(self):
386 c = self.load_default_context()
386 c = self.load_default_context()
387 c.active = 'watched'
387 c.active = 'watched'
388
388
389 # json used to render the grid
389 # json used to render the grid
390 c.data = self._load_my_repos_data(watched=True)
390 c.data = self._load_my_repos_data(watched=True)
391 return self._get_template_context(c)
391 return self._get_template_context(c)
392
392
393 @LoginRequired()
393 @LoginRequired()
394 @NotAnonymous()
394 @NotAnonymous()
395 @view_config(
395 @view_config(
396 route_name='my_account_bookmarks', request_method='GET',
396 route_name='my_account_bookmarks', request_method='GET',
397 renderer='rhodecode:templates/admin/my_account/my_account.mako')
397 renderer='rhodecode:templates/admin/my_account/my_account.mako')
398 def my_account_bookmarks(self):
398 def my_account_bookmarks(self):
399 c = self.load_default_context()
399 c = self.load_default_context()
400 c.active = 'bookmarks'
400 c.active = 'bookmarks'
401 return self._get_template_context(c)
401 return self._get_template_context(c)
402
402
403 def _process_entry(self, entry, user_id):
403 def _process_entry(self, entry, user_id):
404 position = safe_int(entry.get('position'))
404 position = safe_int(entry.get('position'))
405 if position is None:
405 if position is None:
406 return
406 return
407
407
408 # check if this is an existing entry
408 # check if this is an existing entry
409 is_new = False
409 is_new = False
410 db_entry = UserBookmark().get_by_position_for_user(position, user_id)
410 db_entry = UserBookmark().get_by_position_for_user(position, user_id)
411
411
412 if db_entry and str2bool(entry.get('remove')):
412 if db_entry and str2bool(entry.get('remove')):
413 log.debug('Marked bookmark %s for deletion', db_entry)
413 log.debug('Marked bookmark %s for deletion', db_entry)
414 Session().delete(db_entry)
414 Session().delete(db_entry)
415 return
415 return
416
416
417 if not db_entry:
417 if not db_entry:
418 # new
418 # new
419 db_entry = UserBookmark()
419 db_entry = UserBookmark()
420 is_new = True
420 is_new = True
421
421
422 should_save = False
422 should_save = False
423 default_redirect_url = ''
423 default_redirect_url = ''
424
424
425 # save repo
425 # save repo
426 if entry.get('bookmark_repo'):
426 if entry.get('bookmark_repo'):
427 repo = Repository.get(entry['bookmark_repo'])
427 repo = Repository.get(entry['bookmark_repo'])
428 perm_check = HasRepoPermissionAny(
428 perm_check = HasRepoPermissionAny(
429 'repository.read', 'repository.write', 'repository.admin')
429 'repository.read', 'repository.write', 'repository.admin')
430 if repo and perm_check(repo_name=repo.repo_name):
430 if repo and perm_check(repo_name=repo.repo_name):
431 db_entry.repository = repo
431 db_entry.repository = repo
432 should_save = True
432 should_save = True
433 default_redirect_url = '${repo_url}'
433 default_redirect_url = '${repo_url}'
434 # save repo group
434 # save repo group
435 elif entry.get('bookmark_repo_group'):
435 elif entry.get('bookmark_repo_group'):
436 repo_group = RepoGroup.get(entry['bookmark_repo_group'])
436 repo_group = RepoGroup.get(entry['bookmark_repo_group'])
437 perm_check = HasRepoGroupPermissionAny(
437 perm_check = HasRepoGroupPermissionAny(
438 'group.read', 'group.write', 'group.admin')
438 'group.read', 'group.write', 'group.admin')
439
439
440 if repo_group and perm_check(group_name=repo_group.group_name):
440 if repo_group and perm_check(group_name=repo_group.group_name):
441 db_entry.repository_group = repo_group
441 db_entry.repository_group = repo_group
442 should_save = True
442 should_save = True
443 default_redirect_url = '${repo_group_url}'
443 default_redirect_url = '${repo_group_url}'
444 # save generic info
444 # save generic info
445 elif entry.get('title') and entry.get('redirect_url'):
445 elif entry.get('title') and entry.get('redirect_url'):
446 should_save = True
446 should_save = True
447
447
448 if should_save:
448 if should_save:
449 log.debug('Saving bookmark %s, new:%s', db_entry, is_new)
449 log.debug('Saving bookmark %s, new:%s', db_entry, is_new)
450 # mark user and position
450 # mark user and position
451 db_entry.user_id = user_id
451 db_entry.user_id = user_id
452 db_entry.position = position
452 db_entry.position = position
453 db_entry.title = entry.get('title')
453 db_entry.title = entry.get('title')
454 db_entry.redirect_url = entry.get('redirect_url') or default_redirect_url
454 db_entry.redirect_url = entry.get('redirect_url') or default_redirect_url
455
455
456 Session().add(db_entry)
456 Session().add(db_entry)
457
457
458 @LoginRequired()
458 @LoginRequired()
459 @NotAnonymous()
459 @NotAnonymous()
460 @CSRFRequired()
460 @CSRFRequired()
461 @view_config(
461 @view_config(
462 route_name='my_account_bookmarks_update', request_method='POST')
462 route_name='my_account_bookmarks_update', request_method='POST')
463 def my_account_bookmarks_update(self):
463 def my_account_bookmarks_update(self):
464 _ = self.request.translate
464 _ = self.request.translate
465 c = self.load_default_context()
465 c = self.load_default_context()
466 c.active = 'bookmarks'
466 c.active = 'bookmarks'
467
467
468 controls = peppercorn.parse(self.request.POST.items())
468 controls = peppercorn.parse(self.request.POST.items())
469 user_id = c.user.user_id
469 user_id = c.user.user_id
470
470
471 try:
471 try:
472 for entry in controls.get('bookmarks', []):
472 for entry in controls.get('bookmarks', []):
473 self._process_entry(entry, user_id)
473 self._process_entry(entry, user_id)
474
474
475 Session().commit()
475 Session().commit()
476 h.flash(_("Update Bookmarks"), category='success')
476 h.flash(_("Update Bookmarks"), category='success')
477 except IntegrityError:
477 except IntegrityError:
478 h.flash(_("Failed to update bookmarks. "
478 h.flash(_("Failed to update bookmarks. "
479 "Make sure an unique position is used"), category='error')
479 "Make sure an unique position is used"), category='error')
480
480
481 return HTTPFound(h.route_path('my_account_bookmarks'))
481 return HTTPFound(h.route_path('my_account_bookmarks'))
482
482
483 @LoginRequired()
483 @LoginRequired()
484 @NotAnonymous()
484 @NotAnonymous()
485 @view_config(
485 @view_config(
486 route_name='my_account_goto_bookmark', request_method='GET',
486 route_name='my_account_goto_bookmark', request_method='GET',
487 renderer='rhodecode:templates/admin/my_account/my_account.mako')
487 renderer='rhodecode:templates/admin/my_account/my_account.mako')
488 def my_account_goto_bookmark(self):
488 def my_account_goto_bookmark(self):
489
489
490 bookmark_id = self.request.matchdict['bookmark_id']
490 bookmark_id = self.request.matchdict['bookmark_id']
491 user_bookmark = UserBookmark().query()\
491 user_bookmark = UserBookmark().query()\
492 .filter(UserBookmark.user_id == self.request.user.user_id) \
492 .filter(UserBookmark.user_id == self.request.user.user_id) \
493 .filter(UserBookmark.position == bookmark_id).scalar()
493 .filter(UserBookmark.position == bookmark_id).scalar()
494
494
495 redirect_url = h.route_path('my_account_bookmarks')
495 redirect_url = h.route_path('my_account_bookmarks')
496 if not user_bookmark:
496 if not user_bookmark:
497 raise HTTPFound(redirect_url)
497 raise HTTPFound(redirect_url)
498
498
499 # repository set
499 if user_bookmark.repository:
500 if user_bookmark.repository:
500 repo_name = user_bookmark.repository.repo_name
501 repo_name = user_bookmark.repository.repo_name
501 base_redirect_url = h.route_path(
502 base_redirect_url = h.route_path(
502 'repo_summary', repo_name=repo_name)
503 'repo_summary', repo_name=repo_name)
503 if user_bookmark.redirect_url and \
504 if user_bookmark.redirect_url and \
504 '${repo_url}' in user_bookmark.redirect_url:
505 '${repo_url}' in user_bookmark.redirect_url:
505 redirect_url = string.Template(user_bookmark.redirect_url)\
506 redirect_url = string.Template(user_bookmark.redirect_url)\
506 .safe_substitute({'repo_url': base_redirect_url})
507 .safe_substitute({'repo_url': base_redirect_url})
507 else:
508 else:
508 redirect_url = base_redirect_url
509 redirect_url = base_redirect_url
509
510 # repository group set
510 elif user_bookmark.repository_group:
511 elif user_bookmark.repository_group:
511 repo_group_name = user_bookmark.repository_group.group_name
512 repo_group_name = user_bookmark.repository_group.group_name
512 base_redirect_url = h.route_path(
513 base_redirect_url = h.route_path(
513 'repo_group_home', repo_group_name=repo_group_name)
514 'repo_group_home', repo_group_name=repo_group_name)
514 if user_bookmark.redirect_url and \
515 if user_bookmark.redirect_url and \
515 '${repo_group_url}' in user_bookmark.redirect_url:
516 '${repo_group_url}' in user_bookmark.redirect_url:
516 redirect_url = string.Template(user_bookmark.redirect_url)\
517 redirect_url = string.Template(user_bookmark.redirect_url)\
517 .safe_substitute({'repo_group_url': base_redirect_url})
518 .safe_substitute({'repo_group_url': base_redirect_url})
518 else:
519 else:
519 redirect_url = base_redirect_url
520 redirect_url = base_redirect_url
520
521 # custom URL set
521 elif user_bookmark.redirect_url:
522 elif user_bookmark.redirect_url:
522 redirect_url = user_bookmark.redirect_url
523 server_url = h.route_url('home').rstrip('/')
524 redirect_url = string.Template(user_bookmark.redirect_url) \
525 .safe_substitute({'server_url': server_url})
523
526
524 log.debug('Redirecting bookmark %s to %s', user_bookmark, redirect_url)
527 log.debug('Redirecting bookmark %s to %s', user_bookmark, redirect_url)
525 raise HTTPFound(redirect_url)
528 raise HTTPFound(redirect_url)
526
529
527 @LoginRequired()
530 @LoginRequired()
528 @NotAnonymous()
531 @NotAnonymous()
529 @view_config(
532 @view_config(
530 route_name='my_account_perms', request_method='GET',
533 route_name='my_account_perms', request_method='GET',
531 renderer='rhodecode:templates/admin/my_account/my_account.mako')
534 renderer='rhodecode:templates/admin/my_account/my_account.mako')
532 def my_account_perms(self):
535 def my_account_perms(self):
533 c = self.load_default_context()
536 c = self.load_default_context()
534 c.active = 'perms'
537 c.active = 'perms'
535
538
536 c.perm_user = c.auth_user
539 c.perm_user = c.auth_user
537 return self._get_template_context(c)
540 return self._get_template_context(c)
538
541
539 @LoginRequired()
542 @LoginRequired()
540 @NotAnonymous()
543 @NotAnonymous()
541 @view_config(
544 @view_config(
542 route_name='my_account_notifications', request_method='GET',
545 route_name='my_account_notifications', request_method='GET',
543 renderer='rhodecode:templates/admin/my_account/my_account.mako')
546 renderer='rhodecode:templates/admin/my_account/my_account.mako')
544 def my_notifications(self):
547 def my_notifications(self):
545 c = self.load_default_context()
548 c = self.load_default_context()
546 c.active = 'notifications'
549 c.active = 'notifications'
547
550
548 return self._get_template_context(c)
551 return self._get_template_context(c)
549
552
550 @LoginRequired()
553 @LoginRequired()
551 @NotAnonymous()
554 @NotAnonymous()
552 @CSRFRequired()
555 @CSRFRequired()
553 @view_config(
556 @view_config(
554 route_name='my_account_notifications_toggle_visibility',
557 route_name='my_account_notifications_toggle_visibility',
555 request_method='POST', renderer='json_ext')
558 request_method='POST', renderer='json_ext')
556 def my_notifications_toggle_visibility(self):
559 def my_notifications_toggle_visibility(self):
557 user = self._rhodecode_db_user
560 user = self._rhodecode_db_user
558 new_status = not user.user_data.get('notification_status', True)
561 new_status = not user.user_data.get('notification_status', True)
559 user.update_userdata(notification_status=new_status)
562 user.update_userdata(notification_status=new_status)
560 Session().commit()
563 Session().commit()
561 return user.user_data['notification_status']
564 return user.user_data['notification_status']
562
565
563 @LoginRequired()
566 @LoginRequired()
564 @NotAnonymous()
567 @NotAnonymous()
565 @view_config(
568 @view_config(
566 route_name='my_account_edit',
569 route_name='my_account_edit',
567 request_method='GET',
570 request_method='GET',
568 renderer='rhodecode:templates/admin/my_account/my_account.mako')
571 renderer='rhodecode:templates/admin/my_account/my_account.mako')
569 def my_account_edit(self):
572 def my_account_edit(self):
570 c = self.load_default_context()
573 c = self.load_default_context()
571 c.active = 'profile_edit'
574 c.active = 'profile_edit'
572 c.extern_type = c.user.extern_type
575 c.extern_type = c.user.extern_type
573 c.extern_name = c.user.extern_name
576 c.extern_name = c.user.extern_name
574
577
575 schema = user_schema.UserProfileSchema().bind(
578 schema = user_schema.UserProfileSchema().bind(
576 username=c.user.username, user_emails=c.user.emails)
579 username=c.user.username, user_emails=c.user.emails)
577 appstruct = {
580 appstruct = {
578 'username': c.user.username,
581 'username': c.user.username,
579 'email': c.user.email,
582 'email': c.user.email,
580 'firstname': c.user.firstname,
583 'firstname': c.user.firstname,
581 'lastname': c.user.lastname,
584 'lastname': c.user.lastname,
582 }
585 }
583 c.form = forms.RcForm(
586 c.form = forms.RcForm(
584 schema, appstruct=appstruct,
587 schema, appstruct=appstruct,
585 action=h.route_path('my_account_update'),
588 action=h.route_path('my_account_update'),
586 buttons=(forms.buttons.save, forms.buttons.reset))
589 buttons=(forms.buttons.save, forms.buttons.reset))
587
590
588 return self._get_template_context(c)
591 return self._get_template_context(c)
589
592
590 @LoginRequired()
593 @LoginRequired()
591 @NotAnonymous()
594 @NotAnonymous()
592 @CSRFRequired()
595 @CSRFRequired()
593 @view_config(
596 @view_config(
594 route_name='my_account_update',
597 route_name='my_account_update',
595 request_method='POST',
598 request_method='POST',
596 renderer='rhodecode:templates/admin/my_account/my_account.mako')
599 renderer='rhodecode:templates/admin/my_account/my_account.mako')
597 def my_account_update(self):
600 def my_account_update(self):
598 _ = self.request.translate
601 _ = self.request.translate
599 c = self.load_default_context()
602 c = self.load_default_context()
600 c.active = 'profile_edit'
603 c.active = 'profile_edit'
601 c.perm_user = c.auth_user
604 c.perm_user = c.auth_user
602 c.extern_type = c.user.extern_type
605 c.extern_type = c.user.extern_type
603 c.extern_name = c.user.extern_name
606 c.extern_name = c.user.extern_name
604
607
605 schema = user_schema.UserProfileSchema().bind(
608 schema = user_schema.UserProfileSchema().bind(
606 username=c.user.username, user_emails=c.user.emails)
609 username=c.user.username, user_emails=c.user.emails)
607 form = forms.RcForm(
610 form = forms.RcForm(
608 schema, buttons=(forms.buttons.save, forms.buttons.reset))
611 schema, buttons=(forms.buttons.save, forms.buttons.reset))
609
612
610 controls = self.request.POST.items()
613 controls = self.request.POST.items()
611 try:
614 try:
612 valid_data = form.validate(controls)
615 valid_data = form.validate(controls)
613 skip_attrs = ['admin', 'active', 'extern_type', 'extern_name',
616 skip_attrs = ['admin', 'active', 'extern_type', 'extern_name',
614 'new_password', 'password_confirmation']
617 'new_password', 'password_confirmation']
615 if c.extern_type != "rhodecode":
618 if c.extern_type != "rhodecode":
616 # forbid updating username for external accounts
619 # forbid updating username for external accounts
617 skip_attrs.append('username')
620 skip_attrs.append('username')
618 old_email = c.user.email
621 old_email = c.user.email
619 UserModel().update_user(
622 UserModel().update_user(
620 self._rhodecode_user.user_id, skip_attrs=skip_attrs,
623 self._rhodecode_user.user_id, skip_attrs=skip_attrs,
621 **valid_data)
624 **valid_data)
622 if old_email != valid_data['email']:
625 if old_email != valid_data['email']:
623 old = UserEmailMap.query() \
626 old = UserEmailMap.query() \
624 .filter(UserEmailMap.user == c.user).filter(UserEmailMap.email == valid_data['email']).first()
627 .filter(UserEmailMap.user == c.user).filter(UserEmailMap.email == valid_data['email']).first()
625 old.email = old_email
628 old.email = old_email
626 h.flash(_('Your account was updated successfully'), category='success')
629 h.flash(_('Your account was updated successfully'), category='success')
627 Session().commit()
630 Session().commit()
628 except forms.ValidationFailure as e:
631 except forms.ValidationFailure as e:
629 c.form = e
632 c.form = e
630 return self._get_template_context(c)
633 return self._get_template_context(c)
631 except Exception:
634 except Exception:
632 log.exception("Exception updating user")
635 log.exception("Exception updating user")
633 h.flash(_('Error occurred during update of user'),
636 h.flash(_('Error occurred during update of user'),
634 category='error')
637 category='error')
635 raise HTTPFound(h.route_path('my_account_profile'))
638 raise HTTPFound(h.route_path('my_account_profile'))
636
639
637 def _get_pull_requests_list(self, statuses):
640 def _get_pull_requests_list(self, statuses):
638 draw, start, limit = self._extract_chunk(self.request)
641 draw, start, limit = self._extract_chunk(self.request)
639 search_q, order_by, order_dir = self._extract_ordering(self.request)
642 search_q, order_by, order_dir = self._extract_ordering(self.request)
640 _render = self.request.get_partial_renderer(
643 _render = self.request.get_partial_renderer(
641 'rhodecode:templates/data_table/_dt_elements.mako')
644 'rhodecode:templates/data_table/_dt_elements.mako')
642
645
643 pull_requests = PullRequestModel().get_im_participating_in(
646 pull_requests = PullRequestModel().get_im_participating_in(
644 user_id=self._rhodecode_user.user_id,
647 user_id=self._rhodecode_user.user_id,
645 statuses=statuses,
648 statuses=statuses,
646 offset=start, length=limit, order_by=order_by,
649 offset=start, length=limit, order_by=order_by,
647 order_dir=order_dir)
650 order_dir=order_dir)
648
651
649 pull_requests_total_count = PullRequestModel().count_im_participating_in(
652 pull_requests_total_count = PullRequestModel().count_im_participating_in(
650 user_id=self._rhodecode_user.user_id, statuses=statuses)
653 user_id=self._rhodecode_user.user_id, statuses=statuses)
651
654
652 data = []
655 data = []
653 comments_model = CommentsModel()
656 comments_model = CommentsModel()
654 for pr in pull_requests:
657 for pr in pull_requests:
655 repo_id = pr.target_repo_id
658 repo_id = pr.target_repo_id
656 comments = comments_model.get_all_comments(
659 comments = comments_model.get_all_comments(
657 repo_id, pull_request=pr)
660 repo_id, pull_request=pr)
658 owned = pr.user_id == self._rhodecode_user.user_id
661 owned = pr.user_id == self._rhodecode_user.user_id
659
662
660 data.append({
663 data.append({
661 'target_repo': _render('pullrequest_target_repo',
664 'target_repo': _render('pullrequest_target_repo',
662 pr.target_repo.repo_name),
665 pr.target_repo.repo_name),
663 'name': _render('pullrequest_name',
666 'name': _render('pullrequest_name',
664 pr.pull_request_id, pr.target_repo.repo_name,
667 pr.pull_request_id, pr.target_repo.repo_name,
665 short=True),
668 short=True),
666 'name_raw': pr.pull_request_id,
669 'name_raw': pr.pull_request_id,
667 'status': _render('pullrequest_status',
670 'status': _render('pullrequest_status',
668 pr.calculated_review_status()),
671 pr.calculated_review_status()),
669 'title': _render(
672 'title': _render(
670 'pullrequest_title', pr.title, pr.description),
673 'pullrequest_title', pr.title, pr.description),
671 'description': h.escape(pr.description),
674 'description': h.escape(pr.description),
672 'updated_on': _render('pullrequest_updated_on',
675 'updated_on': _render('pullrequest_updated_on',
673 h.datetime_to_time(pr.updated_on)),
676 h.datetime_to_time(pr.updated_on)),
674 'updated_on_raw': h.datetime_to_time(pr.updated_on),
677 'updated_on_raw': h.datetime_to_time(pr.updated_on),
675 'created_on': _render('pullrequest_updated_on',
678 'created_on': _render('pullrequest_updated_on',
676 h.datetime_to_time(pr.created_on)),
679 h.datetime_to_time(pr.created_on)),
677 'created_on_raw': h.datetime_to_time(pr.created_on),
680 'created_on_raw': h.datetime_to_time(pr.created_on),
678 'author': _render('pullrequest_author',
681 'author': _render('pullrequest_author',
679 pr.author.full_contact, ),
682 pr.author.full_contact, ),
680 'author_raw': pr.author.full_name,
683 'author_raw': pr.author.full_name,
681 'comments': _render('pullrequest_comments', len(comments)),
684 'comments': _render('pullrequest_comments', len(comments)),
682 'comments_raw': len(comments),
685 'comments_raw': len(comments),
683 'closed': pr.is_closed(),
686 'closed': pr.is_closed(),
684 'owned': owned
687 'owned': owned
685 })
688 })
686
689
687 # json used to render the grid
690 # json used to render the grid
688 data = ({
691 data = ({
689 'draw': draw,
692 'draw': draw,
690 'data': data,
693 'data': data,
691 'recordsTotal': pull_requests_total_count,
694 'recordsTotal': pull_requests_total_count,
692 'recordsFiltered': pull_requests_total_count,
695 'recordsFiltered': pull_requests_total_count,
693 })
696 })
694 return data
697 return data
695
698
696 @LoginRequired()
699 @LoginRequired()
697 @NotAnonymous()
700 @NotAnonymous()
698 @view_config(
701 @view_config(
699 route_name='my_account_pullrequests',
702 route_name='my_account_pullrequests',
700 request_method='GET',
703 request_method='GET',
701 renderer='rhodecode:templates/admin/my_account/my_account.mako')
704 renderer='rhodecode:templates/admin/my_account/my_account.mako')
702 def my_account_pullrequests(self):
705 def my_account_pullrequests(self):
703 c = self.load_default_context()
706 c = self.load_default_context()
704 c.active = 'pullrequests'
707 c.active = 'pullrequests'
705 req_get = self.request.GET
708 req_get = self.request.GET
706
709
707 c.closed = str2bool(req_get.get('pr_show_closed'))
710 c.closed = str2bool(req_get.get('pr_show_closed'))
708
711
709 return self._get_template_context(c)
712 return self._get_template_context(c)
710
713
711 @LoginRequired()
714 @LoginRequired()
712 @NotAnonymous()
715 @NotAnonymous()
713 @view_config(
716 @view_config(
714 route_name='my_account_pullrequests_data',
717 route_name='my_account_pullrequests_data',
715 request_method='GET', renderer='json_ext')
718 request_method='GET', renderer='json_ext')
716 def my_account_pullrequests_data(self):
719 def my_account_pullrequests_data(self):
717 self.load_default_context()
720 self.load_default_context()
718 req_get = self.request.GET
721 req_get = self.request.GET
719 closed = str2bool(req_get.get('closed'))
722 closed = str2bool(req_get.get('closed'))
720
723
721 statuses = [PullRequest.STATUS_NEW, PullRequest.STATUS_OPEN]
724 statuses = [PullRequest.STATUS_NEW, PullRequest.STATUS_OPEN]
722 if closed:
725 if closed:
723 statuses += [PullRequest.STATUS_CLOSED]
726 statuses += [PullRequest.STATUS_CLOSED]
724
727
725 data = self._get_pull_requests_list(statuses=statuses)
728 data = self._get_pull_requests_list(statuses=statuses)
726 return data
729 return data
727
730
728 @LoginRequired()
731 @LoginRequired()
729 @NotAnonymous()
732 @NotAnonymous()
730 @view_config(
733 @view_config(
731 route_name='my_account_user_group_membership',
734 route_name='my_account_user_group_membership',
732 request_method='GET',
735 request_method='GET',
733 renderer='rhodecode:templates/admin/my_account/my_account.mako')
736 renderer='rhodecode:templates/admin/my_account/my_account.mako')
734 def my_account_user_group_membership(self):
737 def my_account_user_group_membership(self):
735 c = self.load_default_context()
738 c = self.load_default_context()
736 c.active = 'user_group_membership'
739 c.active = 'user_group_membership'
737 groups = [UserGroupModel.get_user_groups_as_dict(group.users_group)
740 groups = [UserGroupModel.get_user_groups_as_dict(group.users_group)
738 for group in self._rhodecode_db_user.group_member]
741 for group in self._rhodecode_db_user.group_member]
739 c.user_groups = json.dumps(groups)
742 c.user_groups = json.dumps(groups)
740 return self._get_template_context(c)
743 return self._get_template_context(c)
@@ -1,222 +1,225 b''
1 <%namespace name="dt" file="/data_table/_dt_elements.mako"/>
1 <%namespace name="dt" file="/data_table/_dt_elements.mako"/>
2
2
3 <%def name="form_item(count, position=None, title=None, redirect_url=None, repo=None, repo_group=None)">
3 <%def name="form_item(count, position=None, title=None, redirect_url=None, repo=None, repo_group=None)">
4 <tr>
4 <tr>
5 <td class="td-align-top" >
5 <td class="td-align-top" >
6 <div class="label">
6 <div class="label">
7 <label for="position">${_('Position')}:</label>
7 <label for="position">${_('Position')}:</label>
8 </div>
8 </div>
9 <div class="input">
9 <div class="input">
10 <input type="text" name="position" value="${position or count}" style="width: 40px"/>
10 <input type="text" name="position" value="${position or count}" style="width: 40px"/>
11 </div>
11 </div>
12 </td>
12 </td>
13
13
14 <td>
14 <td>
15 <div class="label">
15 <div class="label">
16 <label for="title">${_('Bookmark title (max 30 characters, optional)')}:</label>
16 <label for="title">${_('Bookmark title (max 30 characters, optional)')}:</label>
17 </div>
17 </div>
18 <div class="input">
18 <div class="input">
19 <input type="text" name="title" value="${title}" style="width: 300px" maxlength="30"/>
19 <input type="text" name="title" value="${title}" style="width: 300px" maxlength="30"/>
20
20
21 <div class="field pull-right">
21 <div class="field pull-right">
22 <div>
22 <div>
23 <label class="btn-link btn-danger">${_('Clear')}:</label>
23 <label class="btn-link btn-danger">${_('Clear')}:</label>
24 ${h.checkbox('remove', value=True)}
24 ${h.checkbox('remove', value=True)}
25 </div>
25 </div>
26 </div>
26 </div>
27 </div>
27 </div>
28 <p class="help-block help-block-inline" >
29 ${_('Server URL is available as ${server_url} variable. E.g. Redirect url: ${server_url}/_admin/exception_tracker')}
30 </p>
28
31
29 <div class="label">
32 <div class="label">
30 <label for="redirect_url">${_('Redirect URL')}:</label>
33 <label for="redirect_url">${_('Redirect URL')}:</label>
31 </div>
34 </div>
32 <div class="input">
35 <div class="input">
33 <input type="text" name="redirect_url" value="${redirect_url}" style="width: 600px"/>
36 <input type="text" name="redirect_url" value="${redirect_url}" style="width: 600px"/>
34 </div>
37 </div>
35
38
36
39
37 <div class="select">
40 <div class="select">
38 % if repo:
41 % if repo:
39 <div class="label">
42 <div class="label">
40 <label for="redirect_url">${_('Repository template')}:</label>
43 <label for="redirect_url">${_('Repository template')}:</label>
41 </div>
44 </div>
42 ${dt.repo_name(name=repo.repo_name, rtype=repo.repo_type,rstate=None,private=None,archived=False,fork_of=False)}
45 ${dt.repo_name(name=repo.repo_name, rtype=repo.repo_type,rstate=None,private=None,archived=False,fork_of=False)}
43 ${h.hidden('bookmark_repo', repo.repo_id)}
46 ${h.hidden('bookmark_repo', repo.repo_id)}
44 % elif repo_group:
47 % elif repo_group:
45 <div class="label">
48 <div class="label">
46 <label for="redirect_url">${_('Repository group template')}:</label>
49 <label for="redirect_url">${_('Repository group template')}:</label>
47 </div>
50 </div>
48 ${dt.repo_group_name(repo_group.group_name)}
51 ${dt.repo_group_name(repo_group.group_name)}
49 ${h.hidden('bookmark_repo_group', repo_group.group_id)}
52 ${h.hidden('bookmark_repo_group', repo_group.group_id)}
50 % else:
53 % else:
51 <div class="label">
54 <div class="label">
52 <label for="redirect_url">${_('Template Repository or Repository group')}:</label>
55 <label for="redirect_url">${_('Template Repository or Repository group')}:</label>
53 </div>
56 </div>
54 ${h.hidden('bookmark_repo', class_='bookmark_repo')}
57 ${h.hidden('bookmark_repo', class_='bookmark_repo')}
55 <span style="padding-right:15px">OR</span>
58 <span style="padding-right:15px">OR</span>
56 ${h.hidden('bookmark_repo_group', class_='bookmark_repo_group')}
59 ${h.hidden('bookmark_repo_group', class_='bookmark_repo_group')}
57 % endif
60 % endif
58 </div>
61 </div>
59
62
60 <p class="help-block help-block-inline" >
63 <p class="help-block help-block-inline" >
61 % if repo:
64 % if repo:
62 ${_('Available as ${repo_url} e.g. Redirect url: ${repo_url}/changelog')}
65 ${_('Available as ${repo_url} e.g. Redirect url: ${repo_url}/changelog')}
63 % elif repo_group:
66 % elif repo_group:
64 ${_('Available as ${repo_group_url} e.g. Redirect url: ${repo_group_url}')}
67 ${_('Available as ${repo_group_url} e.g. Redirect url: ${repo_group_url}')}
65 % else:
68 % else:
66 ${_('Available as full url variables in redirect url. i.e: ${repo_url}, ${repo_group_url}.')}
69 ${_('Available as full url variables in redirect url. i.e: ${repo_url}, ${repo_group_url}.')}
67 % endif
70 % endif
68 </p>
71 </p>
69 </td>
72 </td>
70
73
71 </tr>
74 </tr>
72 </%def>
75 </%def>
73
76
74 <div class="panel panel-default">
77 <div class="panel panel-default">
75 <div class="panel-heading">
78 <div class="panel-heading">
76 <h3 class="panel-title">${_('Your Bookmarks')}</h3>
79 <h3 class="panel-title">${_('Your Bookmarks')}</h3>
77 </div>
80 </div>
78
81
79 <div class="panel-body">
82 <div class="panel-body">
80 <p>
83 <p>
81 ${_('Store upto 10 bookmark links to favorite repositories, external issue tracker or CI server. ')}
84 ${_('Store upto 10 bookmark links to favorite repositories, external issue tracker or CI server. ')}
82 <br/>
85 <br/>
83 ${_('Bookmarks are accessible from your username dropdown or by keyboard shortcut `g 0-9`')}
86 ${_('Bookmarks are accessible from your username dropdown or by keyboard shortcut `g 0-9`')}
84 </p>
87 </p>
85
88
86 ${h.secure_form(h.route_path('my_account_bookmarks_update'), request=request)}
89 ${h.secure_form(h.route_path('my_account_bookmarks_update'), request=request)}
87 <div class="form-vertical">
90 <div class="form-vertical">
88 <table class="rctable">
91 <table class="rctable">
89 ## generate always 10 entries
92 ## generate always 10 entries
90 <input type="hidden" name="__start__" value="bookmarks:sequence"/>
93 <input type="hidden" name="__start__" value="bookmarks:sequence"/>
91 % for cnt, item in enumerate((c.bookmark_items + [None for i in range(10)])[:10]):
94 % for cnt, item in enumerate((c.bookmark_items + [None for i in range(10)])[:10]):
92 <input type="hidden" name="__start__" value="bookmark:mapping"/>
95 <input type="hidden" name="__start__" value="bookmark:mapping"/>
93 % if item is None:
96 % if item is None:
94 ## empty placehodlder
97 ## empty placehodlder
95 ${form_item(cnt)}
98 ${form_item(cnt)}
96 % else:
99 % else:
97 ## actual entry
100 ## actual entry
98 ${form_item(cnt, position=item.position, title=item.title, redirect_url=item.redirect_url, repo=item.repository, repo_group=item.repository_group)}
101 ${form_item(cnt, position=item.position, title=item.title, redirect_url=item.redirect_url, repo=item.repository, repo_group=item.repository_group)}
99 % endif
102 % endif
100 <input type="hidden" name="__end__" value="bookmark:mapping"/>
103 <input type="hidden" name="__end__" value="bookmark:mapping"/>
101 % endfor
104 % endfor
102 <input type="hidden" name="__end__" value="bookmarks:sequence"/>
105 <input type="hidden" name="__end__" value="bookmarks:sequence"/>
103 </table>
106 </table>
104 <div class="buttons">
107 <div class="buttons">
105 ${h.submit('save',_('Save'),class_="btn")}
108 ${h.submit('save',_('Save'),class_="btn")}
106 </div>
109 </div>
107 </div>
110 </div>
108 ${h.end_form()}
111 ${h.end_form()}
109 </div>
112 </div>
110 </div>
113 </div>
111
114
112 <script>
115 <script>
113 $(document).ready(function(){
116 $(document).ready(function(){
114
117
115
118
116 var repoFilter = function (data) {
119 var repoFilter = function (data) {
117 var results = [];
120 var results = [];
118
121
119 if (!data.results[0]) {
122 if (!data.results[0]) {
120 return data
123 return data
121 }
124 }
122
125
123 $.each(data.results[0].children, function () {
126 $.each(data.results[0].children, function () {
124 // replace name to ID for submision
127 // replace name to ID for submision
125 this.id = this.repo_id;
128 this.id = this.repo_id;
126 results.push(this);
129 results.push(this);
127 });
130 });
128
131
129 data.results[0].children = results;
132 data.results[0].children = results;
130 return data;
133 return data;
131 };
134 };
132
135
133
136
134 $(".bookmark_repo").select2({
137 $(".bookmark_repo").select2({
135 cachedDataSource: {},
138 cachedDataSource: {},
136 minimumInputLength: 2,
139 minimumInputLength: 2,
137 placeholder: "${_('repository')}",
140 placeholder: "${_('repository')}",
138 dropdownAutoWidth: true,
141 dropdownAutoWidth: true,
139 containerCssClass: "drop-menu",
142 containerCssClass: "drop-menu",
140 dropdownCssClass: "drop-menu-dropdown",
143 dropdownCssClass: "drop-menu-dropdown",
141 formatResult: formatRepoResult,
144 formatResult: formatRepoResult,
142 query: $.debounce(250, function (query) {
145 query: $.debounce(250, function (query) {
143 self = this;
146 self = this;
144 var cacheKey = query.term;
147 var cacheKey = query.term;
145 var cachedData = self.cachedDataSource[cacheKey];
148 var cachedData = self.cachedDataSource[cacheKey];
146
149
147 if (cachedData) {
150 if (cachedData) {
148 query.callback({results: cachedData.results});
151 query.callback({results: cachedData.results});
149 } else {
152 } else {
150 $.ajax({
153 $.ajax({
151 url: pyroutes.url('repo_list_data'),
154 url: pyroutes.url('repo_list_data'),
152 data: {'query': query.term},
155 data: {'query': query.term},
153 dataType: 'json',
156 dataType: 'json',
154 type: 'GET',
157 type: 'GET',
155 success: function (data) {
158 success: function (data) {
156 data = repoFilter(data);
159 data = repoFilter(data);
157 self.cachedDataSource[cacheKey] = data;
160 self.cachedDataSource[cacheKey] = data;
158 query.callback({results: data.results});
161 query.callback({results: data.results});
159 },
162 },
160 error: function (data, textStatus, errorThrown) {
163 error: function (data, textStatus, errorThrown) {
161 alert("Error while fetching entries.\nError code {0} ({1}).".format(data.status, data.statusText));
164 alert("Error while fetching entries.\nError code {0} ({1}).".format(data.status, data.statusText));
162 }
165 }
163 })
166 })
164 }
167 }
165 }),
168 }),
166 });
169 });
167
170
168 var repoGroupFilter = function (data) {
171 var repoGroupFilter = function (data) {
169 var results = [];
172 var results = [];
170
173
171 if (!data.results[0]) {
174 if (!data.results[0]) {
172 return data
175 return data
173 }
176 }
174
177
175 $.each(data.results[0].children, function () {
178 $.each(data.results[0].children, function () {
176 // replace name to ID for submision
179 // replace name to ID for submision
177 this.id = this.repo_group_id;
180 this.id = this.repo_group_id;
178 results.push(this);
181 results.push(this);
179 });
182 });
180
183
181 data.results[0].children = results;
184 data.results[0].children = results;
182 return data;
185 return data;
183 };
186 };
184
187
185 $(".bookmark_repo_group").select2({
188 $(".bookmark_repo_group").select2({
186 cachedDataSource: {},
189 cachedDataSource: {},
187 minimumInputLength: 2,
190 minimumInputLength: 2,
188 placeholder: "${_('repository group')}",
191 placeholder: "${_('repository group')}",
189 dropdownAutoWidth: true,
192 dropdownAutoWidth: true,
190 containerCssClass: "drop-menu",
193 containerCssClass: "drop-menu",
191 dropdownCssClass: "drop-menu-dropdown",
194 dropdownCssClass: "drop-menu-dropdown",
192 formatResult: formatRepoGroupResult,
195 formatResult: formatRepoGroupResult,
193 query: $.debounce(250, function (query) {
196 query: $.debounce(250, function (query) {
194 self = this;
197 self = this;
195 var cacheKey = query.term;
198 var cacheKey = query.term;
196 var cachedData = self.cachedDataSource[cacheKey];
199 var cachedData = self.cachedDataSource[cacheKey];
197
200
198 if (cachedData) {
201 if (cachedData) {
199 query.callback({results: cachedData.results});
202 query.callback({results: cachedData.results});
200 } else {
203 } else {
201 $.ajax({
204 $.ajax({
202 url: pyroutes.url('repo_group_list_data'),
205 url: pyroutes.url('repo_group_list_data'),
203 data: {'query': query.term},
206 data: {'query': query.term},
204 dataType: 'json',
207 dataType: 'json',
205 type: 'GET',
208 type: 'GET',
206 success: function (data) {
209 success: function (data) {
207 data = repoGroupFilter(data);
210 data = repoGroupFilter(data);
208 self.cachedDataSource[cacheKey] = data;
211 self.cachedDataSource[cacheKey] = data;
209 query.callback({results: data.results});
212 query.callback({results: data.results});
210 },
213 },
211 error: function (data, textStatus, errorThrown) {
214 error: function (data, textStatus, errorThrown) {
212 alert("Error while fetching entries.\nError code {0} ({1}).".format(data.status, data.statusText));
215 alert("Error while fetching entries.\nError code {0} ({1}).".format(data.status, data.statusText));
213 }
216 }
214 })
217 })
215 }
218 }
216 })
219 })
217 });
220 });
218
221
219
222
220 });
223 });
221
224
222 </script>
225 </script>
General Comments 0
You need to be logged in to leave comments. Login now