##// END OF EJS Templates
release: Finish preparation for 4.7.1
release: Finish preparation for 4.7.1

File last commit:

r1540:e835784f default
r1640:7198bdec v4.7.1 stable
Show More
my_account.py
364 lines | 13.8 KiB | text/x-python | PythonLexer
project: added all source files and assets
r1 # -*- coding: utf-8 -*-
license: updated copyright year to 2017
r1271 # Copyright (C) 2013-2017 RhodeCode GmbH
project: added all source files and assets
r1 #
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License, version 3
# (only), as published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# This program is dual-licensed. If you wish to learn more about the
# RhodeCode Enterprise Edition, including its added features, Support services,
# and proprietary license terms, please see https://rhodecode.com/licenses/
"""
my account controller for RhodeCode admin
"""
import logging
channelstream: added testing panel for live-notifications.
r1274 import datetime
project: added all source files and assets
r1
import formencode
from formencode import htmlfill
channelstream: added testing panel for live-notifications.
r1274 from pyramid.threadlocal import get_current_registry
my-account: moved profile page into pyramid.
r1540 from pyramid.httpexceptions import HTTPFound
from pylons import request, tmpl_context as c, url
project: added all source files and assets
r1 from pylons.controllers.util import redirect
from pylons.i18n.translation import _
from sqlalchemy.orm import joinedload
from rhodecode.lib import helpers as h
from rhodecode.lib import auth
from rhodecode.lib.auth import (
my-account-auth-tokens: moved into pyramid apps....
r1505 LoginRequired, NotAnonymous, AuthUser)
project: added all source files and assets
r1 from rhodecode.lib.base import BaseController, render
account: use jsonify decorator
r694 from rhodecode.lib.utils import jsonify
my-account: switched my-password view to pyramid.
r1537 from rhodecode.lib.utils2 import safe_int, str2bool
project: added all source files and assets
r1 from rhodecode.lib.ext_json import json
channelstream: added testing panel for live-notifications.
r1274 from rhodecode.lib.channelstream import channelstream_request, \
ChannelstreamException
dan
account: convert change password form to colander schema and fix bug...
r665
my_account: Remove oauth identities views from my_account controller.
r26 from rhodecode.model.db import (
pull-requests: redo my account pull request page with datagrid. Fixes #4297...
r1084 Repository, PullRequest, UserEmailMap, User, UserFollowing)
dan
account: convert change password form to colander schema and fix bug...
r665 from rhodecode.model.forms import UserForm
project: added all source files and assets
r1 from rhodecode.model.scm import RepoList
from rhodecode.model.user import UserModel
from rhodecode.model.repo import RepoModel
from rhodecode.model.meta import Session
pull-requests: redo my account pull request page with datagrid. Fixes #4297...
r1084 from rhodecode.model.pull_request import PullRequestModel
comments: renamed ChangesetCommentsModel to CommentsModel to reflect what it actually does....
r1323 from rhodecode.model.comment import CommentsModel
project: added all source files and assets
r1
log = logging.getLogger(__name__)
class MyAccountController(BaseController):
"""REST Controller styled on the Atom Publishing Protocol"""
# To properly map this controller, ensure your config/routing.py
# file has a resource setup:
# map.resource('setting', 'settings', controller='admin/settings',
# path_prefix='/admin', name_prefix='admin_')
@LoginRequired()
@NotAnonymous()
def __before__(self):
super(MyAccountController, self).__before__()
def __load_data(self):
c.user = User.get(c.rhodecode_user.user_id)
if c.user.username == User.DEFAULT_USER:
h.flash(_("You can't edit this user since it's"
" crucial for entire application"), category='warning')
admin-users: moved grid browsing to pyramid....
r1520 return redirect(h.route_path('users'))
project: added all source files and assets
r1
auth: disable password change form for accounts that are not managed by RhodeCode....
r1275 c.auth_user = AuthUser(
user_id=c.rhodecode_user.user_id, ip_addr=self.ip_addr)
project: added all source files and assets
r1 def _load_my_repos_data(self, watched=False):
if watched:
admin = False
follows_repos = Session().query(UserFollowing)\
.filter(UserFollowing.user_id == c.rhodecode_user.user_id)\
.options(joinedload(UserFollowing.follows_repository))\
.all()
repo_list = [x.follows_repository for x in follows_repos]
else:
admin = True
repo_list = Repository.get_all_repos(
user_id=c.rhodecode_user.user_id)
repo_list = RepoList(repo_list, perm_set=[
'repository.read', 'repository.write', 'repository.admin'])
repos_data = RepoModel().get_repos_as_dict(
repo_list=repo_list, admin=admin)
# json used to render the grid
return json.dumps(repos_data)
@auth.CSRFRequired()
def my_account_update(self):
"""
POST /_admin/my_account Updates info of my account
"""
# url('my_account')
c.active = 'profile_edit'
self.__load_data()
auth: disable password change form for accounts that are not managed by RhodeCode....
r1275 c.perm_user = c.auth_user
project: added all source files and assets
r1 c.extern_type = c.user.extern_type
c.extern_name = c.user.extern_name
defaults = c.user.get_dict()
update = False
_form = UserForm(edit=True,
old_data={'user_id': c.rhodecode_user.user_id,
'email': c.rhodecode_user.email})()
form_result = {}
try:
post_data = dict(request.POST)
post_data['new_password'] = ''
post_data['password_confirmation'] = ''
form_result = _form.to_python(post_data)
# skip updating those attrs for my account
skip_attrs = ['admin', 'active', 'extern_type', 'extern_name',
'new_password', 'password_confirmation']
# TODO: plugin should define if username can be updated
if c.extern_type != "rhodecode":
# forbid updating username for external accounts
skip_attrs.append('username')
UserModel().update_user(
c.rhodecode_user.user_id, skip_attrs=skip_attrs, **form_result)
h.flash(_('Your account was updated successfully'),
category='success')
Session().commit()
update = True
except formencode.Invalid as errors:
return htmlfill.render(
templating: use .mako as extensions for template files.
r1282 render('admin/my_account/my_account.mako'),
project: added all source files and assets
r1 defaults=errors.value,
errors=errors.error_dict or {},
prefix_error=False,
encoding="UTF-8",
force_defaults=False)
except Exception:
log.exception("Exception updating user")
h.flash(_('Error occurred during update of user %s')
% form_result.get('username'), category='error')
if update:
my-account: moved profile page into pyramid.
r1540 raise HTTPFound(h.route_path('my_account_profile'))
project: added all source files and assets
r1
return htmlfill.render(
templating: use .mako as extensions for template files.
r1282 render('admin/my_account/my_account.mako'),
project: added all source files and assets
r1 defaults=defaults,
encoding="UTF-8",
force_defaults=False
)
def my_account_edit(self):
"""
GET /_admin/my_account/edit Displays edit form of my account
"""
c.active = 'profile_edit'
self.__load_data()
auth: disable password change form for accounts that are not managed by RhodeCode....
r1275 c.perm_user = c.auth_user
project: added all source files and assets
r1 c.extern_type = c.user.extern_type
c.extern_name = c.user.extern_name
defaults = c.user.get_dict()
return htmlfill.render(
templating: use .mako as extensions for template files.
r1282 render('admin/my_account/my_account.mako'),
project: added all source files and assets
r1 defaults=defaults,
encoding="UTF-8",
force_defaults=False
)
def my_account_repos(self):
c.active = 'repos'
self.__load_data()
# json used to render the grid
c.data = self._load_my_repos_data()
templating: use .mako as extensions for template files.
r1282 return render('admin/my_account/my_account.mako')
project: added all source files and assets
r1
def my_account_watched(self):
c.active = 'watched'
self.__load_data()
# json used to render the grid
c.data = self._load_my_repos_data(watched=True)
templating: use .mako as extensions for template files.
r1282 return render('admin/my_account/my_account.mako')
project: added all source files and assets
r1
def my_account_perms(self):
c.active = 'perms'
self.__load_data()
auth: disable password change form for accounts that are not managed by RhodeCode....
r1275 c.perm_user = c.auth_user
project: added all source files and assets
r1
templating: use .mako as extensions for template files.
r1282 return render('admin/my_account/my_account.mako')
project: added all source files and assets
r1
def my_account_emails(self):
c.active = 'emails'
self.__load_data()
c.user_email_map = UserEmailMap.query()\
.filter(UserEmailMap.user == c.user).all()
templating: use .mako as extensions for template files.
r1282 return render('admin/my_account/my_account.mako')
project: added all source files and assets
r1
@auth.CSRFRequired()
def my_account_emails_add(self):
email = request.POST.get('new_email')
try:
UserModel().add_extra_email(c.rhodecode_user.user_id, email)
Session().commit()
h.flash(_("Added new email address `%s` for user account") % email,
category='success')
except formencode.Invalid as error:
msg = error.error_dict['email']
h.flash(msg, category='error')
except Exception:
log.exception("Exception in my_account_emails")
h.flash(_('An error occurred during email saving'),
category='error')
return redirect(url('my_account_emails'))
@auth.CSRFRequired()
def my_account_emails_delete(self):
email_id = request.POST.get('del_email_id')
user_model = UserModel()
user_model.delete_extra_email(c.rhodecode_user.user_id, email_id)
Session().commit()
h.flash(_("Removed email address from user account"),
category='success')
return redirect(url('my_account_emails'))
pull-requests: redo my account pull request page with datagrid. Fixes #4297...
r1084 def _extract_ordering(self, request):
column_index = safe_int(request.GET.get('order[0][column]'))
order_dir = request.GET.get('order[0][dir]', 'desc')
order_by = request.GET.get(
'columns[%s][data][sort]' % column_index, 'name_raw')
return order_by, order_dir
def _get_pull_requests_list(self, statuses):
start = safe_int(request.GET.get('start'), 0)
length = safe_int(request.GET.get('length'), c.visual.dashboard_items)
order_by, order_dir = self._extract_ordering(request)
pull_requests = PullRequestModel().get_im_participating_in(
user_id=c.rhodecode_user.user_id,
statuses=statuses,
offset=start, length=length, order_by=order_by,
order_dir=order_dir)
pull_requests_total_count = PullRequestModel().count_im_participating_in(
user_id=c.rhodecode_user.user_id, statuses=statuses)
from rhodecode.lib.utils import PartialRenderer
templating: use .mako as extensions for template files.
r1282 _render = PartialRenderer('data_table/_dt_elements.mako')
pull-requests: redo my account pull request page with datagrid. Fixes #4297...
r1084 data = []
for pr in pull_requests:
repo_id = pr.target_repo_id
comments: renamed ChangesetCommentsModel to CommentsModel to reflect what it actually does....
r1323 comments = CommentsModel().get_all_comments(
pull-requests: redo my account pull request page with datagrid. Fixes #4297...
r1084 repo_id, pull_request=pr)
owned = pr.user_id == c.rhodecode_user.user_id
status = pr.calculated_review_status()
data.append({
'target_repo': _render('pullrequest_target_repo',
pr.target_repo.repo_name),
'name': _render('pullrequest_name',
pr.pull_request_id, pr.target_repo.repo_name,
short=True),
'name_raw': pr.pull_request_id,
'status': _render('pullrequest_status', status),
'title': _render(
'pullrequest_title', pr.title, pr.description),
'description': h.escape(pr.description),
'updated_on': _render('pullrequest_updated_on',
h.datetime_to_time(pr.updated_on)),
'updated_on_raw': h.datetime_to_time(pr.updated_on),
'created_on': _render('pullrequest_updated_on',
h.datetime_to_time(pr.created_on)),
'created_on_raw': h.datetime_to_time(pr.created_on),
'author': _render('pullrequest_author',
pr.author.full_contact, ),
'author_raw': pr.author.full_name,
'comments': _render('pullrequest_comments', len(comments)),
'comments_raw': len(comments),
'closed': pr.is_closed(),
'owned': owned
})
# json used to render the grid
data = ({
'data': data,
'recordsTotal': pull_requests_total_count,
'recordsFiltered': pull_requests_total_count,
})
return data
project: added all source files and assets
r1 def my_account_pullrequests(self):
c.active = 'pullrequests'
self.__load_data()
pull-requests: redo my account pull request page with datagrid. Fixes #4297...
r1084 c.show_closed = str2bool(request.GET.get('pr_show_closed'))
project: added all source files and assets
r1
pull-requests: redo my account pull request page with datagrid. Fixes #4297...
r1084 statuses = [PullRequest.STATUS_NEW, PullRequest.STATUS_OPEN]
if c.show_closed:
statuses += [PullRequest.STATUS_CLOSED]
data = self._get_pull_requests_list(statuses)
if not request.is_xhr:
c.data_participate = json.dumps(data['data'])
c.records_total_participate = data['recordsTotal']
templating: use .mako as extensions for template files.
r1282 return render('admin/my_account/my_account.mako')
pull-requests: redo my account pull request page with datagrid. Fixes #4297...
r1084 else:
return json.dumps(data)
project: added all source files and assets
r1
notifications: support real-time notifications with websockets via channelstream
r526 def my_notifications(self):
c.active = 'notifications'
templating: use .mako as extensions for template files.
r1282 return render('admin/my_account/my_account.mako')
notifications: support real-time notifications with websockets via channelstream
r526
@auth.CSRFRequired()
account: use jsonify decorator
r694 @jsonify
notifications: support real-time notifications with websockets via channelstream
r526 def my_notifications_toggle_visibility(self):
user = c.rhodecode_user.get_instance()
notifications: store notification status in channelstream
r734 new_status = not user.user_data.get('notification_status', True)
user.update_userdata(notification_status=new_status)
notifications: support real-time notifications with websockets via channelstream
r526 Session().commit()
notifications: store notification status in channelstream
r734 return user.user_data['notification_status']
channelstream: added testing panel for live-notifications.
r1274
@auth.CSRFRequired()
@jsonify
def my_account_notifications_test_channelstream(self):
message = 'Test message sent via Channelstream by user: {}, on {}'.format(
c.rhodecode_user.username, datetime.datetime.now())
payload = {
'type': 'message',
'timestamp': datetime.datetime.utcnow(),
'user': 'system',
#'channel': 'broadcast',
'pm_users': [c.rhodecode_user.username],
'message': {
'message': message,
'level': 'info',
'topic': '/notifications'
}
}
registry = get_current_registry()
rhodecode_plugins = getattr(registry, 'rhodecode_plugins', {})
channelstream_config = rhodecode_plugins.get('channelstream', {})
try:
channelstream_request(channelstream_config, [payload], '/message')
except ChannelstreamException as e:
log.exception('Failed to send channelstream data')
return {"response": 'ERROR: {}'.format(e.__class__.__name__)}
return {"response": 'Channelstream data sent. '
'You should see a new live message now.'}