##// END OF EJS Templates
Added tag v4.10.0 for changeset 4ba4d74981ce
Added tag v4.10.0 for changeset 4ba4d74981ce

File last commit:

r2175:ea878558 default
r2219:7f41de40 stable
Show More
__init__.py
583 lines | 18.8 KiB | text/x-python | PythonLexer
user-profile: migrated to pyramid views.
r1502 # -*- coding: utf-8 -*-
# Copyright (C) 2016-2017 RhodeCode GmbH
#
# 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/
pyramid: added checks for password change for authenticated users.
r1539 import time
user-profile: migrated to pyramid views.
r1502 import logging
data-grid-app: added universal function for extracting ordering....
r2040 import operator
core: make pylons global be used in obsolete parts of the code....
r1895
my-account: switched my-password view to pyramid.
r1537 from pyramid.httpexceptions import HTTPFound
user-profile: migrated to pyramid views.
r1502
pyramid: added checks for password change for authenticated users.
r1539 from rhodecode.lib import helpers as h
tags/branches/bookmarks: moved views into pyramid.
r1746 from rhodecode.lib.utils2 import StrictAttributeDict, safe_int, datetime_to_time
repo-view: handle empty repo or missing requirements in base view for repositories.
r1714 from rhodecode.lib.vcs.exceptions import RepositoryRequirementError
core: added supports of repo based views via pyramid.
r1554 from rhodecode.model import repo
home: moved home and repo group views into pyramid....
r1774 from rhodecode.model import repo_group
user-groups: rewrote the app to pyramid...
r2068 from rhodecode.model import user_group
users: ported controllers from pylons into pyramid views.
r2114 from rhodecode.model import user
pyramid: added checks for password change for authenticated users.
r1539 from rhodecode.model.db import User
core: added supports of repo based views via pyramid.
r1554 from rhodecode.model.scm import ScmModel
user-profile: migrated to pyramid views.
r1502
log = logging.getLogger(__name__)
my-account-auth-tokens: moved into pyramid apps....
r1505 ADMIN_PREFIX = '/_admin'
STATIC_FILE_PREFIX = '/_static'
pyramid: moved add_route_requirements into pyramid base app....
r1928 URL_NAME_REQUIREMENTS = {
# group name can have a slash in them, but they must not end with a slash
'group_name': r'.*?[^/]',
'repo_group_name': r'.*?[^/]',
# repo names can have a slash in them, but they must not end with a slash
'repo_name': r'.*?[^/]',
# file path eats up everything at the end
'f_path': r'.*',
# reference types
'source_ref_type': '(branch|book|tag|rev|\%\(source_ref_type\)s)',
'target_ref_type': '(branch|book|tag|rev|\%\(target_ref_type\)s)',
}
my-account-auth-tokens: moved into pyramid apps....
r1505
home: moved home and repo group views into pyramid....
r1774 def add_route_with_slash(config,name, pattern, **kw):
config.add_route(name, pattern, **kw)
if not pattern.endswith('/'):
config.add_route(name + '_slash', pattern + '/', **kw)
pyramid: moved add_route_requirements into pyramid base app....
r1928 def add_route_requirements(route_path, requirements=URL_NAME_REQUIREMENTS):
"""
Adds regex requirements to pyramid routes using a mapping dict
e.g::
add_route_requirements('{repo_name}/settings')
"""
for key, regex in requirements.items():
route_path = route_path.replace('{%s}' % key, '{%s:%s}' % (key, regex))
return route_path
tags/branches/bookmarks: moved views into pyramid.
r1746 def get_format_ref_id(repo):
"""Returns a `repo` specific reference formatter function"""
if h.is_svn(repo):
return _format_ref_id_svn
else:
return _format_ref_id
def _format_ref_id(name, raw_id):
"""Default formatting of a given reference `name`"""
return name
def _format_ref_id_svn(name, raw_id):
"""Special way of formatting a reference for Subversion including path"""
return '%s@%s' % (name, raw_id)
user-profile: migrated to pyramid views.
r1502 class TemplateArgs(StrictAttributeDict):
pass
class BaseAppView(object):
def __init__(self, context, request):
self.request = request
self.context = context
self.session = request.session
pyramid: base view improvements
r1534 self._rhodecode_user = request.user # auth user
my-account: switched my-password view to pyramid.
r1537 self._rhodecode_db_user = self._rhodecode_user.get_instance()
pyramid: added checks for password change for authenticated users.
r1539 self._maybe_needs_password_change(
request.matched_route.name, self._rhodecode_db_user)
def _maybe_needs_password_change(self, view_name, user_obj):
log.debug('Checking if user %s needs password change on view %s',
user_obj, view_name)
skip_user_views = [
'logout', 'login',
'my_account_password', 'my_account_password_update'
]
if not user_obj:
return
if user_obj.username == User.DEFAULT_USER:
return
now = time.time()
should_change = user_obj.user_data.get('force_password_change')
change_after = safe_int(should_change) or 0
if should_change and now > change_after:
log.debug('User %s requires password change', user_obj)
h.flash('You are required to change your password', 'warning',
ignore_duplicate=True)
if view_name not in skip_user_views:
raise HTTPFound(
self.request.route_path('my_account_password'))
user-profile: migrated to pyramid views.
r1502
repositories: handle in a nicer way a filesystem damaged repositories....
r1984 def _log_creation_exception(self, e, repo_name):
_ = self.request.translate
reason = None
if len(e.args) == 2:
reason = e.args[1]
if reason == 'INVALID_CERTIFICATE':
log.exception(
'Exception creating a repository: invalid certificate')
msg = (_('Error creating repository %s: invalid certificate')
% repo_name)
else:
log.exception("Exception creating a repository")
msg = (_('Error creating repository %s')
% repo_name)
return msg
repo-summary: re-implemented summary view as pyramid....
r1785 def _get_local_tmpl_context(self, include_app_defaults=False):
pyramid-views: always register auth_user for via the base view....
r1533 c = TemplateArgs()
c.auth_user = self.request.user
pyramid: changes for pyramid migration.
r1908 # TODO(marcink): migrate the usage of c.rhodecode_user to c.auth_user
c.rhodecode_user = self.request.user
repo-summary: re-implemented summary view as pyramid....
r1785 if include_app_defaults:
# NOTE(marcink): after full pyramid migration include_app_defaults
# should be turned on by default
from rhodecode.lib.base import attach_context_attributes
attach_context_attributes(c, self.request, self.request.user.user_id)
pyramid: changes for pyramid migration.
r1908
pyramid-views: always register auth_user for via the base view....
r1533 return c
user-profile: migrated to pyramid views.
r1502
my-account-auth-tokens: moved into pyramid apps....
r1505 def _register_global_c(self, tmpl_args):
"""
Registers attributes to pylons global `c`
"""
pyramid: changes for pyramid migration.
r1908
my-account-auth-tokens: moved into pyramid apps....
r1505 # TODO(marcink): remove once pyramid migration is finished
core: make pylons global be used in obsolete parts of the code....
r1895 from pylons import tmpl_context as c
pyramid: allows easier turning off the pylons components.
r1912 try:
for k, v in tmpl_args.items():
setattr(c, k, v)
except TypeError:
log.exception('Failed to register pylons C')
pass
my-account-auth-tokens: moved into pyramid apps....
r1505
user-profile: migrated to pyramid views.
r1502 def _get_template_context(self, tmpl_args):
my-account-auth-tokens: moved into pyramid apps....
r1505 self._register_global_c(tmpl_args)
user-profile: migrated to pyramid views.
r1502
login: switch to re-use the baseApp pyramid view.
r1536 local_tmpl_args = {
user-profile: migrated to pyramid views.
r1502 'defaults': {},
'errors': {},
pyramid: expose a temporary TemplateContext under `_c`
r1924 # register a fake 'c' to be used in templates instead of global
# pylons c, after migration to pyramid we should rename it to 'c'
# make sure we replace usage of _c in templates too
'_c': tmpl_args
user-profile: migrated to pyramid views.
r1502 }
login: switch to re-use the baseApp pyramid view.
r1536 local_tmpl_args.update(tmpl_args)
return local_tmpl_args
user-profile: migrated to pyramid views.
r1502
pyramid: base view improvements
r1534 def load_default_context(self):
"""
example:
def load_default_context(self):
c = self._get_local_tmpl_context()
c.custom_var = 'foobar'
self._register_global_c(c)
return c
"""
raise NotImplementedError('Needs implementation in view class')
core: added supports of repo based views via pyramid.
r1554
class RepoAppView(BaseAppView):
def __init__(self, context, request):
super(RepoAppView, self).__init__(context, request)
self.db_repo = request.db_repo
self.db_repo_name = self.db_repo.repo_name
self.db_repo_pull_requests = ScmModel().get_pull_requests(self.db_repo)
repo-view: handle empty repo or missing requirements in base view for repositories.
r1714 def _handle_missing_requirements(self, error):
log.error(
'Requirements are missing for repository %s: %s',
self.db_repo_name, error.message)
repo-summary: re-implemented summary view as pyramid....
r1785 def _get_local_tmpl_context(self, include_app_defaults=False):
repositories: handle in a nicer way a filesystem damaged repositories....
r1984 _ = self.request.translate
repo-summary: re-implemented summary view as pyramid....
r1785 c = super(RepoAppView, self)._get_local_tmpl_context(
include_app_defaults=include_app_defaults)
core: added supports of repo based views via pyramid.
r1554 # register common vars for this type of view
c.rhodecode_db_repo = self.db_repo
c.repo_name = self.db_repo_name
c.repository_pull_requests = self.db_repo_pull_requests
repo-view: handle empty repo or missing requirements in base view for repositories.
r1714
c.repository_requirements_missing = False
try:
self.rhodecode_vcs_repo = self.db_repo.scm_instance()
except RepositoryRequirementError as e:
c.repository_requirements_missing = True
self._handle_missing_requirements(e)
repositories: handle in a nicer way a filesystem damaged repositories....
r1984 self.rhodecode_vcs_repo = None
if (not c.repository_requirements_missing
and self.rhodecode_vcs_repo is None):
# unable to fetch this repo as vcs instance, report back to user
h.flash(_(
"The repository `%(repo_name)s` cannot be loaded in filesystem. "
"Please check if it exist, or is not damaged.") %
{'repo_name': c.repo_name},
category='error', ignore_duplicate=True)
raise HTTPFound(h.route_path('home'))
repo-view: handle empty repo or missing requirements in base view for repositories.
r1714
core: added supports of repo based views via pyramid.
r1554 return c
routing: use a common method to extract the f_path for repo views....
r1929 def _get_f_path(self, matchdict, default=None):
f_path = matchdict.get('f_path')
if f_path:
# fix for multiple initial slashes that causes errors for GIT
return f_path.lstrip('/')
return default
core: added supports of repo based views via pyramid.
r1554
core: no longer rely on webob exception inside get_or_404 function....
r1956
routing: optionally use explicit db object on route....
r1989 class RepoGroupAppView(BaseAppView):
def __init__(self, context, request):
super(RepoGroupAppView, self).__init__(context, request)
self.db_repo_group = request.db_repo_group
self.db_repo_group_name = self.db_repo_group.group_name
repo-groups: moved to pyramid
r2175 def _revoke_perms_on_yourself(self, form_result):
_updates = filter(lambda u: self._rhodecode_user.user_id == int(u[0]),
form_result['perm_updates'])
_additions = filter(lambda u: self._rhodecode_user.user_id == int(u[0]),
form_result['perm_additions'])
_deletions = filter(lambda u: self._rhodecode_user.user_id == int(u[0]),
form_result['perm_deletions'])
admin_perm = 'group.admin'
if _updates and _updates[0][1] != admin_perm or \
_additions and _additions[0][1] != admin_perm or \
_deletions and _deletions[0][1] != admin_perm:
return True
return False
routing: optionally use explicit db object on route....
r1989
user-groups: rewrote the app to pyramid...
r2068 class UserGroupAppView(BaseAppView):
def __init__(self, context, request):
super(UserGroupAppView, self).__init__(context, request)
self.db_user_group = request.db_user_group
self.db_user_group_name = self.db_user_group.users_group_name
users: ported controllers from pylons into pyramid views.
r2114 class UserAppView(BaseAppView):
def __init__(self, context, request):
super(UserAppView, self).__init__(context, request)
self.db_user = request.db_user
self.db_user_id = self.db_user.user_id
_ = self.request.translate
if not request.db_user_supports_default:
if self.db_user.username == User.DEFAULT_USER:
h.flash(_("Editing user `{}` is disabled.".format(
User.DEFAULT_USER)), category='warning')
raise HTTPFound(h.route_path('users'))
data-grid: create a common re-usable app with datagrid rendering helpers
r1646 class DataGridAppView(object):
"""
Common class to have re-usable grid rendering components
"""
base-grid-view: allow specifing custom mapping for sort columns.
r1649 def _extract_ordering(self, request, column_map=None):
column_map = column_map or {}
data-grid: create a common re-usable app with datagrid rendering helpers
r1646 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')
# translate datatable to DB columns
base-grid-view: allow specifing custom mapping for sort columns.
r1649 order_by = column_map.get(order_by) or order_by
data-grid: create a common re-usable app with datagrid rendering helpers
r1646
search_q = request.GET.get('search[value]')
return search_q, order_by, order_dir
def _extract_chunk(self, request):
start = safe_int(request.GET.get('start'), 0)
length = safe_int(request.GET.get('length'), 25)
draw = safe_int(request.GET.get('draw'))
return draw, start, length
data-grid-app: added universal function for extracting ordering....
r2040 def _get_order_col(self, order_by, model):
if isinstance(order_by, basestring):
try:
return operator.attrgetter(order_by)(model)
except AttributeError:
return None
else:
return order_by
data-grid: create a common re-usable app with datagrid rendering helpers
r1646
tags/branches/bookmarks: moved views into pyramid.
r1746 class BaseReferencesView(RepoAppView):
"""
Base for reference view for branches, tags and bookmarks.
"""
def load_default_context(self):
c = self._get_local_tmpl_context()
self._register_global_c(c)
return c
def load_refs_context(self, ref_items, partials_template):
core: use new style pyramid partial renderer where possible.
r1897 _render = self.request.get_partial_renderer(partials_template)
tags/branches/bookmarks: moved views into pyramid.
r1746 pre_load = ["author", "date", "message"]
is_svn = h.is_svn(self.rhodecode_vcs_repo)
refs: refactored references code to remove any pylons elements....
r1898 is_hg = h.is_hg(self.rhodecode_vcs_repo)
tags/branches/bookmarks: moved views into pyramid.
r1746 format_ref_id = get_format_ref_id(self.rhodecode_vcs_repo)
refs: refactored references code to remove any pylons elements....
r1898 closed_refs = {}
if is_hg:
closed_refs = self.rhodecode_vcs_repo.branches_closed
data = []
tags/branches/bookmarks: moved views into pyramid.
r1746 for ref_name, commit_id in ref_items:
commit = self.rhodecode_vcs_repo.get_commit(
commit_id=commit_id, pre_load=pre_load)
refs: refactored references code to remove any pylons elements....
r1898 closed = ref_name in closed_refs
tags/branches/bookmarks: moved views into pyramid.
r1746
# TODO: johbo: Unify generation of reference links
use_commit_id = '/' in ref_name or is_svn
files: ported repository files controllers to pyramid views.
r1927
if use_commit_id:
files_url = h.route_path(
'repo_files',
repo_name=self.db_repo_name,
f_path=ref_name if is_svn else '',
commit_id=commit_id)
else:
files_url = h.route_path(
'repo_files',
repo_name=self.db_repo_name,
f_path=ref_name if is_svn else '',
commit_id=ref_name,
_query=dict(at=ref_name))
tags/branches/bookmarks: moved views into pyramid.
r1746
refs: refactored references code to remove any pylons elements....
r1898 data.append({
"name": _render('name', ref_name, files_url, closed),
tags/branches/bookmarks: moved views into pyramid.
r1746 "name_raw": ref_name,
"date": _render('date', commit.date),
"date_raw": datetime_to_time(commit.date),
"author": _render('author', commit.author),
"commit": _render(
'commit', commit.message, commit.raw_id, commit.idx),
"commit_raw": commit.idx,
"compare": _render(
'compare', format_ref_id(ref_name, commit.raw_id)),
})
refs: refactored references code to remove any pylons elements....
r1898
return data
tags/branches/bookmarks: moved views into pyramid.
r1746
core: added supports of repo based views via pyramid.
r1554 class RepoRoutePredicate(object):
def __init__(self, val, config):
self.val = val
def text(self):
return 'repo_route = %s' % self.val
phash = text
def __call__(self, info, request):
predicates: skip route matching for vcs type calls.
r1778
if hasattr(request, 'vcs_call'):
# skip vcs calls
return
core: added supports of repo based views via pyramid.
r1554 repo_name = info['match']['repo_name']
repo_model = repo.RepoModel()
by_name_match = repo_model.get_by_repo_name(repo_name, cache=True)
home: moved home and repo group views into pyramid....
r1774
repositories: ported repo_creating checks to pyramid....
r1985 def redirect_if_creating(db_repo):
if db_repo.repo_state in [repo.Repository.STATE_PENDING]:
raise HTTPFound(
request.route_path('repo_creating',
repo_name=db_repo.repo_name))
core: added supports of repo based views via pyramid.
r1554 if by_name_match:
# register this as request object we can re-use later
request.db_repo = by_name_match
repositories: ported repo_creating checks to pyramid....
r1985 redirect_if_creating(by_name_match)
core: added supports of repo based views via pyramid.
r1554 return True
by_id_match = repo_model.get_repo_by_id(repo_name)
if by_id_match:
request.db_repo = by_id_match
repositories: ported repo_creating checks to pyramid....
r1985 redirect_if_creating(by_id_match)
core: added supports of repo based views via pyramid.
r1554 return True
return False
pull-requests: moved the listing of pull requests for repo into pyramid....
r1766 class RepoTypeRoutePredicate(object):
def __init__(self, val, config):
self.val = val or ['hg', 'git', 'svn']
def text(self):
return 'repo_accepted_type = %s' % self.val
phash = text
def __call__(self, info, request):
predicates: skip route matching for vcs type calls.
r1778 if hasattr(request, 'vcs_call'):
# skip vcs calls
return
pull-requests: moved the listing of pull requests for repo into pyramid....
r1766
rhodecode_db_repo = request.db_repo
log.debug(
'%s checking repo type for %s in %s',
self.__class__.__name__, rhodecode_db_repo.repo_type, self.val)
if rhodecode_db_repo.repo_type in self.val:
return True
else:
log.warning('Current view is not supported for repo type:%s',
rhodecode_db_repo.repo_type)
pull-request: extended default reviewers functionality....
r1769 #
# h.flash(h.literal(
# _('Action not supported for %s.' % rhodecode_repo.alias)),
# category='warning')
# return redirect(
repo-summary: re-implemented summary view as pyramid....
r1785 # route_path('repo_summary', repo_name=cls.rhodecode_db_repo.repo_name))
pull-request: extended default reviewers functionality....
r1769
pull-requests: moved the listing of pull requests for repo into pyramid....
r1766 return False
home: moved home and repo group views into pyramid....
r1774 class RepoGroupRoutePredicate(object):
def __init__(self, val, config):
self.val = val
def text(self):
return 'repo_group_route = %s' % self.val
phash = text
def __call__(self, info, request):
predicates: skip route matching for vcs type calls.
r1778 if hasattr(request, 'vcs_call'):
# skip vcs calls
return
home: moved home and repo group views into pyramid....
r1774 repo_group_name = info['match']['repo_group_name']
repo_group_model = repo_group.RepoGroupModel()
by_name_match = repo_group_model.get_by_group_name(
repo_group_name, cache=True)
if by_name_match:
# register this as request object we can re-use later
request.db_repo_group = by_name_match
return True
return False
pull-requests: moved the listing of pull requests for repo into pyramid....
r1766
user-groups: rewrote the app to pyramid...
r2068 class UserGroupRoutePredicate(object):
def __init__(self, val, config):
self.val = val
def text(self):
return 'user_group_route = %s' % self.val
phash = text
def __call__(self, info, request):
if hasattr(request, 'vcs_call'):
# skip vcs calls
return
user_group_id = info['match']['user_group_id']
user_group_model = user_group.UserGroup()
users: ported controllers from pylons into pyramid views.
r2114 by_id_match = user_group_model.get(
user-groups: rewrote the app to pyramid...
r2068 user_group_id, cache=True)
users: ported controllers from pylons into pyramid views.
r2114 if by_id_match:
user-groups: rewrote the app to pyramid...
r2068 # register this as request object we can re-use later
users: ported controllers from pylons into pyramid views.
r2114 request.db_user_group = by_id_match
user-groups: rewrote the app to pyramid...
r2068 return True
return False
users: ported controllers from pylons into pyramid views.
r2114 class UserRoutePredicateBase(object):
supports_default = None
def __init__(self, val, config):
self.val = val
def text(self):
raise NotImplementedError()
def __call__(self, info, request):
if hasattr(request, 'vcs_call'):
# skip vcs calls
return
user_id = info['match']['user_id']
user_model = user.User()
by_id_match = user_model.get(
user_id, cache=True)
if by_id_match:
# register this as request object we can re-use later
request.db_user = by_id_match
request.db_user_supports_default = self.supports_default
return True
return False
class UserRoutePredicate(UserRoutePredicateBase):
supports_default = False
def text(self):
return 'user_route = %s' % self.val
phash = text
class UserRouteWithDefaultPredicate(UserRoutePredicateBase):
supports_default = True
def text(self):
return 'user_with_default_route = %s' % self.val
phash = text
core: added supports of repo based views via pyramid.
r1554 def includeme(config):
config.add_route_predicate(
'repo_route', RepoRoutePredicate)
pull-requests: moved the listing of pull requests for repo into pyramid....
r1766 config.add_route_predicate(
home: moved home and repo group views into pyramid....
r1774 'repo_accepted_types', RepoTypeRoutePredicate)
config.add_route_predicate(
'repo_group_route', RepoGroupRoutePredicate)
user-groups: rewrote the app to pyramid...
r2068 config.add_route_predicate(
'user_group_route', UserGroupRoutePredicate)
users: ported controllers from pylons into pyramid views.
r2114 config.add_route_predicate(
'user_route_with_default', UserRouteWithDefaultPredicate)
config.add_route_predicate(
'user_route', UserRoutePredicate)