##// END OF EJS Templates
notifications: replace toggle button with actual toggle element - fixes #4171
ergo -
r692:cea437a2 default
parent child Browse files
Show More
@@ -1,371 +1,371 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2013-2016 RhodeCode GmbH
3 # Copyright (C) 2013-2016 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 pylons import request, tmpl_context as c, url, session
30 from pylons import request, tmpl_context as c, url, session
31 from pylons.controllers.util import redirect
31 from pylons.controllers.util import redirect
32 from pylons.i18n.translation import _
32 from pylons.i18n.translation import _
33 from sqlalchemy.orm import joinedload
33 from sqlalchemy.orm import joinedload
34
34
35 from rhodecode import forms
35 from rhodecode import forms
36 from rhodecode.lib import helpers as h
36 from rhodecode.lib import helpers as h
37 from rhodecode.lib import auth
37 from rhodecode.lib import auth
38 from rhodecode.lib.auth import (
38 from rhodecode.lib.auth import (
39 LoginRequired, NotAnonymous, AuthUser, generate_auth_token)
39 LoginRequired, NotAnonymous, AuthUser, generate_auth_token)
40 from rhodecode.lib.base import BaseController, render
40 from rhodecode.lib.base import BaseController, render
41 from rhodecode.lib.utils2 import safe_int, md5
41 from rhodecode.lib.utils2 import safe_int, md5
42 from rhodecode.lib.ext_json import json
42 from rhodecode.lib.ext_json import json
43
43
44 from rhodecode.model.validation_schema.schemas import user_schema
44 from rhodecode.model.validation_schema.schemas import user_schema
45 from rhodecode.model.db import (
45 from rhodecode.model.db import (
46 Repository, PullRequest, PullRequestReviewers, UserEmailMap, User,
46 Repository, PullRequest, PullRequestReviewers, UserEmailMap, User,
47 UserFollowing)
47 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.auth_token import AuthTokenModel
52 from rhodecode.model.auth_token import AuthTokenModel
53 from rhodecode.model.meta import Session
53 from rhodecode.model.meta import Session
54
54
55 log = logging.getLogger(__name__)
55 log = logging.getLogger(__name__)
56
56
57
57
58 class MyAccountController(BaseController):
58 class MyAccountController(BaseController):
59 """REST Controller styled on the Atom Publishing Protocol"""
59 """REST Controller styled on the Atom Publishing Protocol"""
60 # To properly map this controller, ensure your config/routing.py
60 # To properly map this controller, ensure your config/routing.py
61 # file has a resource setup:
61 # file has a resource setup:
62 # map.resource('setting', 'settings', controller='admin/settings',
62 # map.resource('setting', 'settings', controller='admin/settings',
63 # path_prefix='/admin', name_prefix='admin_')
63 # path_prefix='/admin', name_prefix='admin_')
64
64
65 @LoginRequired()
65 @LoginRequired()
66 @NotAnonymous()
66 @NotAnonymous()
67 def __before__(self):
67 def __before__(self):
68 super(MyAccountController, self).__before__()
68 super(MyAccountController, self).__before__()
69
69
70 def __load_data(self):
70 def __load_data(self):
71 c.user = User.get(c.rhodecode_user.user_id)
71 c.user = User.get(c.rhodecode_user.user_id)
72 if c.user.username == User.DEFAULT_USER:
72 if c.user.username == User.DEFAULT_USER:
73 h.flash(_("You can't edit this user since it's"
73 h.flash(_("You can't edit this user since it's"
74 " crucial for entire application"), category='warning')
74 " crucial for entire application"), category='warning')
75 return redirect(url('users'))
75 return redirect(url('users'))
76
76
77 def _load_my_repos_data(self, watched=False):
77 def _load_my_repos_data(self, watched=False):
78 if watched:
78 if watched:
79 admin = False
79 admin = False
80 follows_repos = Session().query(UserFollowing)\
80 follows_repos = Session().query(UserFollowing)\
81 .filter(UserFollowing.user_id == c.rhodecode_user.user_id)\
81 .filter(UserFollowing.user_id == c.rhodecode_user.user_id)\
82 .options(joinedload(UserFollowing.follows_repository))\
82 .options(joinedload(UserFollowing.follows_repository))\
83 .all()
83 .all()
84 repo_list = [x.follows_repository for x in follows_repos]
84 repo_list = [x.follows_repository for x in follows_repos]
85 else:
85 else:
86 admin = True
86 admin = True
87 repo_list = Repository.get_all_repos(
87 repo_list = Repository.get_all_repos(
88 user_id=c.rhodecode_user.user_id)
88 user_id=c.rhodecode_user.user_id)
89 repo_list = RepoList(repo_list, perm_set=[
89 repo_list = RepoList(repo_list, perm_set=[
90 'repository.read', 'repository.write', 'repository.admin'])
90 'repository.read', 'repository.write', 'repository.admin'])
91
91
92 repos_data = RepoModel().get_repos_as_dict(
92 repos_data = RepoModel().get_repos_as_dict(
93 repo_list=repo_list, admin=admin)
93 repo_list=repo_list, admin=admin)
94 # json used to render the grid
94 # json used to render the grid
95 return json.dumps(repos_data)
95 return json.dumps(repos_data)
96
96
97 @auth.CSRFRequired()
97 @auth.CSRFRequired()
98 def my_account_update(self):
98 def my_account_update(self):
99 """
99 """
100 POST /_admin/my_account Updates info of my account
100 POST /_admin/my_account Updates info of my account
101 """
101 """
102 # url('my_account')
102 # url('my_account')
103 c.active = 'profile_edit'
103 c.active = 'profile_edit'
104 self.__load_data()
104 self.__load_data()
105 c.perm_user = AuthUser(user_id=c.rhodecode_user.user_id,
105 c.perm_user = AuthUser(user_id=c.rhodecode_user.user_id,
106 ip_addr=self.ip_addr)
106 ip_addr=self.ip_addr)
107 c.extern_type = c.user.extern_type
107 c.extern_type = c.user.extern_type
108 c.extern_name = c.user.extern_name
108 c.extern_name = c.user.extern_name
109
109
110 defaults = c.user.get_dict()
110 defaults = c.user.get_dict()
111 update = False
111 update = False
112 _form = UserForm(edit=True,
112 _form = UserForm(edit=True,
113 old_data={'user_id': c.rhodecode_user.user_id,
113 old_data={'user_id': c.rhodecode_user.user_id,
114 'email': c.rhodecode_user.email})()
114 'email': c.rhodecode_user.email})()
115 form_result = {}
115 form_result = {}
116 try:
116 try:
117 post_data = dict(request.POST)
117 post_data = dict(request.POST)
118 post_data['new_password'] = ''
118 post_data['new_password'] = ''
119 post_data['password_confirmation'] = ''
119 post_data['password_confirmation'] = ''
120 form_result = _form.to_python(post_data)
120 form_result = _form.to_python(post_data)
121 # skip updating those attrs for my account
121 # skip updating those attrs for my account
122 skip_attrs = ['admin', 'active', 'extern_type', 'extern_name',
122 skip_attrs = ['admin', 'active', 'extern_type', 'extern_name',
123 'new_password', 'password_confirmation']
123 'new_password', 'password_confirmation']
124 # TODO: plugin should define if username can be updated
124 # TODO: plugin should define if username can be updated
125 if c.extern_type != "rhodecode":
125 if c.extern_type != "rhodecode":
126 # forbid updating username for external accounts
126 # forbid updating username for external accounts
127 skip_attrs.append('username')
127 skip_attrs.append('username')
128
128
129 UserModel().update_user(
129 UserModel().update_user(
130 c.rhodecode_user.user_id, skip_attrs=skip_attrs, **form_result)
130 c.rhodecode_user.user_id, skip_attrs=skip_attrs, **form_result)
131 h.flash(_('Your account was updated successfully'),
131 h.flash(_('Your account was updated successfully'),
132 category='success')
132 category='success')
133 Session().commit()
133 Session().commit()
134 update = True
134 update = True
135
135
136 except formencode.Invalid as errors:
136 except formencode.Invalid as errors:
137 return htmlfill.render(
137 return htmlfill.render(
138 render('admin/my_account/my_account.html'),
138 render('admin/my_account/my_account.html'),
139 defaults=errors.value,
139 defaults=errors.value,
140 errors=errors.error_dict or {},
140 errors=errors.error_dict or {},
141 prefix_error=False,
141 prefix_error=False,
142 encoding="UTF-8",
142 encoding="UTF-8",
143 force_defaults=False)
143 force_defaults=False)
144 except Exception:
144 except Exception:
145 log.exception("Exception updating user")
145 log.exception("Exception updating user")
146 h.flash(_('Error occurred during update of user %s')
146 h.flash(_('Error occurred during update of user %s')
147 % form_result.get('username'), category='error')
147 % form_result.get('username'), category='error')
148
148
149 if update:
149 if update:
150 return redirect('my_account')
150 return redirect('my_account')
151
151
152 return htmlfill.render(
152 return htmlfill.render(
153 render('admin/my_account/my_account.html'),
153 render('admin/my_account/my_account.html'),
154 defaults=defaults,
154 defaults=defaults,
155 encoding="UTF-8",
155 encoding="UTF-8",
156 force_defaults=False
156 force_defaults=False
157 )
157 )
158
158
159 def my_account(self):
159 def my_account(self):
160 """
160 """
161 GET /_admin/my_account Displays info about my account
161 GET /_admin/my_account Displays info about my account
162 """
162 """
163 # url('my_account')
163 # url('my_account')
164 c.active = 'profile'
164 c.active = 'profile'
165 self.__load_data()
165 self.__load_data()
166
166
167 defaults = c.user.get_dict()
167 defaults = c.user.get_dict()
168 return htmlfill.render(
168 return htmlfill.render(
169 render('admin/my_account/my_account.html'),
169 render('admin/my_account/my_account.html'),
170 defaults=defaults, encoding="UTF-8", force_defaults=False)
170 defaults=defaults, encoding="UTF-8", force_defaults=False)
171
171
172 def my_account_edit(self):
172 def my_account_edit(self):
173 """
173 """
174 GET /_admin/my_account/edit Displays edit form of my account
174 GET /_admin/my_account/edit Displays edit form of my account
175 """
175 """
176 c.active = 'profile_edit'
176 c.active = 'profile_edit'
177 self.__load_data()
177 self.__load_data()
178 c.perm_user = AuthUser(user_id=c.rhodecode_user.user_id,
178 c.perm_user = AuthUser(user_id=c.rhodecode_user.user_id,
179 ip_addr=self.ip_addr)
179 ip_addr=self.ip_addr)
180 c.extern_type = c.user.extern_type
180 c.extern_type = c.user.extern_type
181 c.extern_name = c.user.extern_name
181 c.extern_name = c.user.extern_name
182
182
183 defaults = c.user.get_dict()
183 defaults = c.user.get_dict()
184 return htmlfill.render(
184 return htmlfill.render(
185 render('admin/my_account/my_account.html'),
185 render('admin/my_account/my_account.html'),
186 defaults=defaults,
186 defaults=defaults,
187 encoding="UTF-8",
187 encoding="UTF-8",
188 force_defaults=False
188 force_defaults=False
189 )
189 )
190
190
191 @auth.CSRFRequired(except_methods=['GET'])
191 @auth.CSRFRequired(except_methods=['GET'])
192 def my_account_password(self):
192 def my_account_password(self):
193 c.active = 'password'
193 c.active = 'password'
194 self.__load_data()
194 self.__load_data()
195
195
196 schema = user_schema.ChangePasswordSchema().bind(
196 schema = user_schema.ChangePasswordSchema().bind(
197 username=c.rhodecode_user.username)
197 username=c.rhodecode_user.username)
198
198
199 form = forms.Form(schema,
199 form = forms.Form(schema,
200 buttons=(forms.buttons.save, forms.buttons.reset))
200 buttons=(forms.buttons.save, forms.buttons.reset))
201
201
202 if request.method == 'POST':
202 if request.method == 'POST':
203 controls = request.POST.items()
203 controls = request.POST.items()
204 try:
204 try:
205 valid_data = form.validate(controls)
205 valid_data = form.validate(controls)
206 UserModel().update_user(c.rhodecode_user.user_id, **valid_data)
206 UserModel().update_user(c.rhodecode_user.user_id, **valid_data)
207 instance = c.rhodecode_user.get_instance()
207 instance = c.rhodecode_user.get_instance()
208 instance.update_userdata(force_password_change=False)
208 instance.update_userdata(force_password_change=False)
209 Session().commit()
209 Session().commit()
210 except forms.ValidationFailure as e:
210 except forms.ValidationFailure as e:
211 request.session.flash(
211 request.session.flash(
212 _('Error occurred during update of user password'),
212 _('Error occurred during update of user password'),
213 queue='error')
213 queue='error')
214 form = e
214 form = e
215 except Exception:
215 except Exception:
216 log.exception("Exception updating password")
216 log.exception("Exception updating password")
217 request.session.flash(
217 request.session.flash(
218 _('Error occurred during update of user password'),
218 _('Error occurred during update of user password'),
219 queue='error')
219 queue='error')
220 else:
220 else:
221 session.setdefault('rhodecode_user', {}).update(
221 session.setdefault('rhodecode_user', {}).update(
222 {'password': md5(instance.password)})
222 {'password': md5(instance.password)})
223 session.save()
223 session.save()
224 request.session.flash(
224 request.session.flash(
225 _("Successfully updated password"), queue='success')
225 _("Successfully updated password"), queue='success')
226 return redirect(url('my_account_password'))
226 return redirect(url('my_account_password'))
227
227
228 c.form = form
228 c.form = form
229 return render('admin/my_account/my_account.html')
229 return render('admin/my_account/my_account.html')
230
230
231 def my_account_repos(self):
231 def my_account_repos(self):
232 c.active = 'repos'
232 c.active = 'repos'
233 self.__load_data()
233 self.__load_data()
234
234
235 # json used to render the grid
235 # json used to render the grid
236 c.data = self._load_my_repos_data()
236 c.data = self._load_my_repos_data()
237 return render('admin/my_account/my_account.html')
237 return render('admin/my_account/my_account.html')
238
238
239 def my_account_watched(self):
239 def my_account_watched(self):
240 c.active = 'watched'
240 c.active = 'watched'
241 self.__load_data()
241 self.__load_data()
242
242
243 # json used to render the grid
243 # json used to render the grid
244 c.data = self._load_my_repos_data(watched=True)
244 c.data = self._load_my_repos_data(watched=True)
245 return render('admin/my_account/my_account.html')
245 return render('admin/my_account/my_account.html')
246
246
247 def my_account_perms(self):
247 def my_account_perms(self):
248 c.active = 'perms'
248 c.active = 'perms'
249 self.__load_data()
249 self.__load_data()
250 c.perm_user = AuthUser(user_id=c.rhodecode_user.user_id,
250 c.perm_user = AuthUser(user_id=c.rhodecode_user.user_id,
251 ip_addr=self.ip_addr)
251 ip_addr=self.ip_addr)
252
252
253 return render('admin/my_account/my_account.html')
253 return render('admin/my_account/my_account.html')
254
254
255 def my_account_emails(self):
255 def my_account_emails(self):
256 c.active = 'emails'
256 c.active = 'emails'
257 self.__load_data()
257 self.__load_data()
258
258
259 c.user_email_map = UserEmailMap.query()\
259 c.user_email_map = UserEmailMap.query()\
260 .filter(UserEmailMap.user == c.user).all()
260 .filter(UserEmailMap.user == c.user).all()
261 return render('admin/my_account/my_account.html')
261 return render('admin/my_account/my_account.html')
262
262
263 @auth.CSRFRequired()
263 @auth.CSRFRequired()
264 def my_account_emails_add(self):
264 def my_account_emails_add(self):
265 email = request.POST.get('new_email')
265 email = request.POST.get('new_email')
266
266
267 try:
267 try:
268 UserModel().add_extra_email(c.rhodecode_user.user_id, email)
268 UserModel().add_extra_email(c.rhodecode_user.user_id, email)
269 Session().commit()
269 Session().commit()
270 h.flash(_("Added new email address `%s` for user account") % email,
270 h.flash(_("Added new email address `%s` for user account") % email,
271 category='success')
271 category='success')
272 except formencode.Invalid as error:
272 except formencode.Invalid as error:
273 msg = error.error_dict['email']
273 msg = error.error_dict['email']
274 h.flash(msg, category='error')
274 h.flash(msg, category='error')
275 except Exception:
275 except Exception:
276 log.exception("Exception in my_account_emails")
276 log.exception("Exception in my_account_emails")
277 h.flash(_('An error occurred during email saving'),
277 h.flash(_('An error occurred during email saving'),
278 category='error')
278 category='error')
279 return redirect(url('my_account_emails'))
279 return redirect(url('my_account_emails'))
280
280
281 @auth.CSRFRequired()
281 @auth.CSRFRequired()
282 def my_account_emails_delete(self):
282 def my_account_emails_delete(self):
283 email_id = request.POST.get('del_email_id')
283 email_id = request.POST.get('del_email_id')
284 user_model = UserModel()
284 user_model = UserModel()
285 user_model.delete_extra_email(c.rhodecode_user.user_id, email_id)
285 user_model.delete_extra_email(c.rhodecode_user.user_id, email_id)
286 Session().commit()
286 Session().commit()
287 h.flash(_("Removed email address from user account"),
287 h.flash(_("Removed email address from user account"),
288 category='success')
288 category='success')
289 return redirect(url('my_account_emails'))
289 return redirect(url('my_account_emails'))
290
290
291 def my_account_pullrequests(self):
291 def my_account_pullrequests(self):
292 c.active = 'pullrequests'
292 c.active = 'pullrequests'
293 self.__load_data()
293 self.__load_data()
294 c.show_closed = request.GET.get('pr_show_closed')
294 c.show_closed = request.GET.get('pr_show_closed')
295
295
296 def _filter(pr):
296 def _filter(pr):
297 s = sorted(pr, key=lambda o: o.created_on, reverse=True)
297 s = sorted(pr, key=lambda o: o.created_on, reverse=True)
298 if not c.show_closed:
298 if not c.show_closed:
299 s = filter(lambda p: p.status != PullRequest.STATUS_CLOSED, s)
299 s = filter(lambda p: p.status != PullRequest.STATUS_CLOSED, s)
300 return s
300 return s
301
301
302 c.my_pull_requests = _filter(
302 c.my_pull_requests = _filter(
303 PullRequest.query().filter(
303 PullRequest.query().filter(
304 PullRequest.user_id == c.rhodecode_user.user_id).all())
304 PullRequest.user_id == c.rhodecode_user.user_id).all())
305 my_prs = [
305 my_prs = [
306 x.pull_request for x in PullRequestReviewers.query().filter(
306 x.pull_request for x in PullRequestReviewers.query().filter(
307 PullRequestReviewers.user_id == c.rhodecode_user.user_id).all()]
307 PullRequestReviewers.user_id == c.rhodecode_user.user_id).all()]
308 c.participate_in_pull_requests = _filter(my_prs)
308 c.participate_in_pull_requests = _filter(my_prs)
309 return render('admin/my_account/my_account.html')
309 return render('admin/my_account/my_account.html')
310
310
311 def my_account_auth_tokens(self):
311 def my_account_auth_tokens(self):
312 c.active = 'auth_tokens'
312 c.active = 'auth_tokens'
313 self.__load_data()
313 self.__load_data()
314 show_expired = True
314 show_expired = True
315 c.lifetime_values = [
315 c.lifetime_values = [
316 (str(-1), _('forever')),
316 (str(-1), _('forever')),
317 (str(5), _('5 minutes')),
317 (str(5), _('5 minutes')),
318 (str(60), _('1 hour')),
318 (str(60), _('1 hour')),
319 (str(60 * 24), _('1 day')),
319 (str(60 * 24), _('1 day')),
320 (str(60 * 24 * 30), _('1 month')),
320 (str(60 * 24 * 30), _('1 month')),
321 ]
321 ]
322 c.lifetime_options = [(c.lifetime_values, _("Lifetime"))]
322 c.lifetime_options = [(c.lifetime_values, _("Lifetime"))]
323 c.role_values = [(x, AuthTokenModel.cls._get_role_name(x))
323 c.role_values = [(x, AuthTokenModel.cls._get_role_name(x))
324 for x in AuthTokenModel.cls.ROLES]
324 for x in AuthTokenModel.cls.ROLES]
325 c.role_options = [(c.role_values, _("Role"))]
325 c.role_options = [(c.role_values, _("Role"))]
326 c.user_auth_tokens = AuthTokenModel().get_auth_tokens(
326 c.user_auth_tokens = AuthTokenModel().get_auth_tokens(
327 c.rhodecode_user.user_id, show_expired=show_expired)
327 c.rhodecode_user.user_id, show_expired=show_expired)
328 return render('admin/my_account/my_account.html')
328 return render('admin/my_account/my_account.html')
329
329
330 @auth.CSRFRequired()
330 @auth.CSRFRequired()
331 def my_account_auth_tokens_add(self):
331 def my_account_auth_tokens_add(self):
332 lifetime = safe_int(request.POST.get('lifetime'), -1)
332 lifetime = safe_int(request.POST.get('lifetime'), -1)
333 description = request.POST.get('description')
333 description = request.POST.get('description')
334 role = request.POST.get('role')
334 role = request.POST.get('role')
335 AuthTokenModel().create(c.rhodecode_user.user_id, description, lifetime,
335 AuthTokenModel().create(c.rhodecode_user.user_id, description, lifetime,
336 role)
336 role)
337 Session().commit()
337 Session().commit()
338 h.flash(_("Auth token successfully created"), category='success')
338 h.flash(_("Auth token successfully created"), category='success')
339 return redirect(url('my_account_auth_tokens'))
339 return redirect(url('my_account_auth_tokens'))
340
340
341 @auth.CSRFRequired()
341 @auth.CSRFRequired()
342 def my_account_auth_tokens_delete(self):
342 def my_account_auth_tokens_delete(self):
343 auth_token = request.POST.get('del_auth_token')
343 auth_token = request.POST.get('del_auth_token')
344 user_id = c.rhodecode_user.user_id
344 user_id = c.rhodecode_user.user_id
345 if request.POST.get('del_auth_token_builtin'):
345 if request.POST.get('del_auth_token_builtin'):
346 user = User.get(user_id)
346 user = User.get(user_id)
347 if user:
347 if user:
348 user.api_key = generate_auth_token(user.username)
348 user.api_key = generate_auth_token(user.username)
349 Session().add(user)
349 Session().add(user)
350 Session().commit()
350 Session().commit()
351 h.flash(_("Auth token successfully reset"), category='success')
351 h.flash(_("Auth token successfully reset"), category='success')
352 elif auth_token:
352 elif auth_token:
353 AuthTokenModel().delete(auth_token, c.rhodecode_user.user_id)
353 AuthTokenModel().delete(auth_token, c.rhodecode_user.user_id)
354 Session().commit()
354 Session().commit()
355 h.flash(_("Auth token successfully deleted"), category='success')
355 h.flash(_("Auth token successfully deleted"), category='success')
356
356
357 return redirect(url('my_account_auth_tokens'))
357 return redirect(url('my_account_auth_tokens'))
358
358
359 def my_notifications(self):
359 def my_notifications(self):
360 c.active = 'notifications'
360 c.active = 'notifications'
361 return render('admin/my_account/my_account.html')
361 return render('admin/my_account/my_account.html')
362
362
363 @auth.CSRFRequired()
363 @auth.CSRFRequired()
364 def my_notifications_toggle_visibility(self):
364 def my_notifications_toggle_visibility(self):
365 user = c.rhodecode_user.get_instance()
365 user = c.rhodecode_user.get_instance()
366 user_data = user.user_data
366 user_data = user.user_data
367 status = user_data.get('notification_status', False)
367 status = user_data.get('notification_status', False)
368 user_data['notification_status'] = not status
368 user_data['notification_status'] = not status
369 user.user_data = user_data
369 user.user_data = user_data
370 Session().commit()
370 Session().commit()
371 return redirect(url('my_account_notifications'))
371 return json.dumps(user_data['notification_status'])
@@ -1,4 +1,6 b''
1 <link rel="import" href="../../../../../bower_components/paper-toggle-button/paper-toggle-button.html">
1 <link rel="import" href="../../../../../bower_components/paper-toggle-button/paper-toggle-button.html">
2 <link rel="import" href="../../../../../bower_components/paper-toast/paper-toast.html">
2 <link rel="import" href="../../../../../bower_components/paper-toast/paper-toast.html">
3 <link rel="import" href="../../../../../bower_components/paper-tooltip/paper-tooltip.html">
3 <link rel="import" href="../../../../../bower_components/paper-tooltip/paper-tooltip.html">
4 <link rel="import" href="../../../../../bower_components/paper-button/paper-button.html">
4 <link rel="import" href="../../../../../bower_components/paper-button/paper-button.html">
5 <link rel="import" href="../../../../../bower_components/paper-spinner/paper-spinner.html">
6 <link rel="import" href="../../../../../bower_components/iron-ajax/iron-ajax.html">
@@ -1,56 +1,96 b''
1 <template is="dom-bind" id="notificationsPage">
2
3 <iron-ajax id="toggleNotifications"
4 method="post"
5 url="${url('my_account_notifications_toggle_visibility')}"
6 content-type="application/json"
7 loading="{{changeNotificationsLoading}}"
8 on-response="handleNotifications"
9 handle-as="json"></iron-ajax>
10
1 <div class="panel panel-default">
11 <div class="panel panel-default">
2 <div class="panel-heading">
12 <div class="panel-heading">
3 <h3 class="panel-title">${_('Your live notification settings')}</h3>
13 <h3 class="panel-title">${_('Your live notification settings')}</h3>
4 </div>
14 </div>
5
6 <div class="panel-body">
15 <div class="panel-body">
7
16
8 <p><strong>IMPORTANT:</strong> This feature requires enabled channelstream websocket server to function correctly.</p>
17 <p><strong>IMPORTANT:</strong> This feature requires enabled channelstream websocket server to function correctly.</p>
9
18
10 <p class="hidden">Status of browser notifications permission: <strong id="browser-notification-status"></strong></p>
19 <p class="hidden">Status of browser notifications permission: <strong id="browser-notification-status"></strong></p>
11
20
12 ${h.secure_form(url('my_account_notifications_toggle_visibility'), method='post', id='notification-status')}
21 <div class="form">
13 <button class="btn btn-default" type="submit">
22 <!-- fields -->
14 ${_('Notifications')} <strong>${_('Enabled') if c.rhodecode_user.get_instance().user_data.get('notification_status') else _('Disabled')}</strong>
23 <div class="fields">
15 </button>
24 <div class="field">
16 ${h.end_form()}
25 <div class="label">
17
26 <label for="new_email">${_('Notifications status')}:</label>
18 <a class="btn btn-info" id="test-notification">Test notification</a>
27 </div>
28 <div class="checkboxes">
29 <div style="display: inline-block">
30 <paper-toggle-button on-change="toggleNotifications" ${'checked' if c.rhodecode_user.get_instance().user_data.get('notification_status') else ''}></paper-toggle-button>
31 <paper-tooltip>Toggle your notifications on/off globally.</paper-tooltip>
32 </div>
33 <template is="dom-if" if="{{changeNotificationsLoading}}">
34 <paper-spinner active style="height: 22px; width: 22px"></paper-spinner>
35 </template>
36 </div>
37 </div>
38 <div class="buttons">
39 <a class="btn btn-info" id="test-notification">Test notification</a>
40 </div>
41 </div>
42 </div>
19
43
20 </div>
44 </div>
21 </div>
45 </div>
22
46
23 <script type="application/javascript">
47 <script type="application/javascript">
48 /** because im not creating a custom element for this page
49 * we need to push the function onto the dom-template
50 * ideally we turn this into notification-settings elements
51 * then it will be cleaner
52 */
53 var ctrlr = $('#notificationsPage')[0];
54 ctrlr.toggleNotifications = function(event){
55 var ajax = $('#toggleNotifications')[0];
56 ajax.headers = {"X-CSRF-Token": CSRF_TOKEN}
57 ajax.body = {notification_status:event.target.active};
58 ajax.generateRequest();
59 };
60 ctrlr.handleNotifications = function(event){
61 $('paper-toggle-button')[0].active = event.detail.response;
62 };
24
63
25 function checkBrowserStatus(){
64 function checkBrowserStatus(){
26 var browserStatus = 'Unknown';
65 var browserStatus = 'Unknown';
27
66
28 if (!("Notification" in window)) {
67 if (!("Notification" in window)) {
29 browserStatus = 'Not supported'
68 browserStatus = 'Not supported'
30 }
69 }
31 else if(Notification.permission === 'denied'){
70 else if(Notification.permission === 'denied'){
32 browserStatus = 'Denied';
71 browserStatus = 'Denied';
33 $('.flash_msg').append('<div class="alert alert-error">Notifications are blocked on browser level - you need to enable them in your browser settings.</div>')
72 $('.flash_msg').append('<div class="alert alert-error">Notifications are blocked on browser level - you need to enable them in your browser settings.</div>')
34 }
73 }
35 else if(Notification.permission === 'granted'){
74 else if(Notification.permission === 'granted'){
36 browserStatus = 'Allowed';
75 browserStatus = 'Allowed';
37 }
76 }
38
77
39 $('#browser-notification-status').text(browserStatus);
78 $('#browser-notification-status').text(browserStatus);
40 };
79 };
41
80
42 checkBrowserStatus();
81 checkBrowserStatus();
43
82
44 $('#test-notification').on('click', function(e){
83 $('#test-notification').on('click', function(e){
45 var levels = ['info', 'error', 'warning', 'success'];
84 var levels = ['info', 'error', 'warning', 'success'];
46 var level = levels[Math.floor(Math.random()*levels.length)];
85 var level = levels[Math.floor(Math.random()*levels.length)];
47 var payload = {
86 var payload = {
48 message: {
87 message: {
49 message: 'This is a test notification.',
88 message: 'This is a test notification.',
50 level: level,
89 level: level,
51 testMessage: true
90 testMessage: true
52 }
91 }
53 };
92 };
54 $.Topic('/notifications').publish(payload);
93 $.Topic('/notifications').publish(payload);
55 })
94 })
56 </script>
95 </script>
96 </template>
General Comments 0
You need to be logged in to leave comments. Login now