@@ -21,264 +21,265 @@ repos, branches, bookmarks or tips
This file was forked by the Kallithea project in July 2014.
Original author and date, and relevant copyright and licensing information is below:
:created_on: May 6, 2012
:author: marcink
:copyright: (c) 2013 RhodeCode GmbH, and others.
:license: GPLv3, see LICENSE.md for more details.
"""
import logging
import re
from webob.exc import HTTPBadRequest
from pylons import request, tmpl_context as c, url
from pylons.controllers.util import redirect
from pylons.i18n.translation import _
from kallithea.lib.vcs.utils.hgcompat import unionrepo
from kallithea.lib import helpers as h
from kallithea.lib.base import BaseRepoController, render
from kallithea.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
from kallithea.lib import diffs
from kallithea.model.db import Repository
from kallithea.lib.diffs import LimitedDiffContainer
from kallithea.controllers.changeset import _ignorews_url,\
_context_url, get_line_ctx, get_ignore_ws
from kallithea.lib.graphmod import graph_data
from kallithea.lib.compat import json
log = logging.getLogger(__name__)
class CompareController(BaseRepoController):
def __before__(self):
super(CompareController, self).__before__()
@staticmethod
def _get_changesets(alias, org_repo, org_rev, other_repo, other_rev):
Returns lists of changesets that can be merged from org_repo@org_rev
to other_repo@other_rev
... and the other way
... and the ancestor that would be used for merge
:param org_repo: repo object, that is most likely the orginal repo we forked from
:param org_rev: the revision we want our compare to be made
:param other_repo: repo object, mostl likely the fork of org_repo. It hass
all changesets that we need to obtain
:param other_rev: revision we want out compare to be made on other_repo
ancestor = None
if org_rev == other_rev:
org_changesets = []
other_changesets = []
ancestor = org_rev
elif alias == 'hg':
#case two independent repos
if org_repo != other_repo:
hgrepo = unionrepo.unionrepository(other_repo.baseui,
other_repo.path,
org_repo.path)
# all ancestors of other_rev will be in other_repo and
# rev numbers from hgrepo can be used in other_repo - org_rev ancestors cannot
#no remote compare do it on the same repository
else:
hgrepo = other_repo._repo
ancestors = hgrepo.revs("ancestor(id(%s), id(%s))", org_rev, other_rev)
if ancestors:
# pick arbitrary ancestor - but there is usually only one
ancestor = hgrepo[ancestors[0]].hex()
other_revs = hgrepo.revs("ancestors(id(%s)) and not ancestors(id(%s)) and not id(%s)",
other_rev, org_rev, org_rev)
other_changesets = [other_repo.get_changeset(rev) for rev in other_revs]
org_revs = hgrepo.revs("ancestors(id(%s)) and not ancestors(id(%s)) and not id(%s)",
org_rev, other_rev, other_rev)
org_changesets = [org_repo.get_changeset(hgrepo[rev].hex()) for rev in org_revs]
elif alias == 'git':
from dulwich.repo import Repo
from dulwich.client import SubprocessGitClient
gitrepo = Repo(org_repo.path)
SubprocessGitClient(thin_packs=False).fetch(other_repo.path, gitrepo)
gitrepo_remote = Repo(other_repo.path)
SubprocessGitClient(thin_packs=False).fetch(org_repo.path, gitrepo_remote)
revs = []
for x in gitrepo_remote.get_walker(include=[other_rev],
exclude=[org_rev]):
revs.append(x.commit.id)
other_changesets = [other_repo.get_changeset(rev) for rev in reversed(revs)]
if other_changesets:
ancestor = other_changesets[0].parents[0].raw_id
# no changesets from other repo, ancestor is the other_rev
ancestor = other_rev
so, se = org_repo.run_git_command(
'log --reverse --pretty="format: %%H" -s %s..%s'
% (org_rev, other_rev)
)
other_changesets = [org_repo.get_changeset(cs)
for cs in re.findall(r'[0-9a-fA-F]{40}', so)]
'merge-base %s %s' % (org_rev, other_rev)
ancestor = re.findall(r'[0-9a-fA-F]{40}', so)[0]
raise Exception('Bad alias only git and hg is allowed')
return other_changesets, org_changesets, ancestor
@LoginRequired()
@HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
'repository.admin')
def index(self, repo_name):
c.compare_home = True
org_repo = c.db_repo.repo_name
other_repo = request.GET.get('other_repo', org_repo)
c.a_repo = Repository.get_by_repo_name(org_repo)
c.cs_repo = Repository.get_by_repo_name(other_repo)
c.a_ref_name = c.cs_ref_name = _('Select changeset')
return render('compare/compare_diff.html')
def compare(self, repo_name, org_ref_type, org_ref_name, other_ref_type, other_ref_name):
# If merge is True:
# Show what org would get if merged with other:
# List changesets that are ancestors of other but not of org.
# New changesets in org is thus ignored.
# Diff will be from common ancestor, and merges of org to other will thus be ignored.
# If merge is False:
# Make a raw diff from org to other, no matter if related or not.
# Changesets in one and not in the other will be ignored
merge = bool(request.GET.get('merge'))
# fulldiff disables cut_off_limit
c.fulldiff = request.GET.get('fulldiff')
# partial uses compare_cs.html template directly
partial = request.environ.get('HTTP_X_PARTIAL_XHR')
# as_form puts hidden input field with changeset revisions
c.as_form = partial and request.GET.get('as_form')
# swap url for compare_diff page - never partial and never as_form
c.swap_url = h.url('compare_url',
repo_name=other_repo,
org_ref_type=other_ref_type, org_ref_name=other_ref_name,
other_repo=org_repo,
other_ref_type=org_ref_type, other_ref_name=org_ref_name,
merge=merge or '')
# set callbacks for generating markup for icons
c.ignorews_url = _ignorews_url
c.context_url = _context_url
ignore_whitespace = request.GET.get('ignorews') == '1'
line_context = request.GET.get('context', 3)
org_repo = Repository.get_by_repo_name(org_repo)
other_repo = Repository.get_by_repo_name(other_repo)
if org_repo is None:
msg = 'Could not find org repo %s' % org_repo
log.error(msg)
h.flash(msg, category='error')
return redirect(url('compare_home', repo_name=c.repo_name))
if other_repo is None:
msg = 'Could not find other repo %s' % other_repo
if org_repo.scm_instance.alias != other_repo.scm_instance.alias:
msg = 'compare of two different kind of remote repos not available'
c.a_rev = self._get_ref_rev(org_repo, org_ref_type, org_ref_name)
c.a_rev = self._get_ref_rev(org_repo, org_ref_type, org_ref_name,
returnempty=True)
c.cs_rev = self._get_ref_rev(other_repo, other_ref_type, other_ref_name)
c.compare_home = False
c.a_repo = org_repo
c.a_ref_name = org_ref_name
c.a_ref_type = org_ref_type
c.cs_repo = other_repo
c.cs_ref_name = other_ref_name
c.cs_ref_type = other_ref_type
c.cs_ranges, c.cs_ranges_org, c.ancestor = self._get_changesets(
org_repo.scm_instance.alias, org_repo.scm_instance, c.a_rev,
other_repo.scm_instance, c.cs_rev)
raw_ids = [x.raw_id for x in c.cs_ranges]
c.cs_comments = other_repo.get_comments(raw_ids)
c.statuses = other_repo.statuses(raw_ids)
revs = [ctx.revision for ctx in reversed(c.cs_ranges)]
c.jsdata = json.dumps(graph_data(c.cs_repo.scm_instance, revs))
if partial:
return render('compare/compare_cs.html')
if merge and c.ancestor:
# case we want a simple diff without incoming changesets,
# previewing what will be merged.
# Make the diff on the other repo (which is known to have other_rev)
log.debug('Using ancestor %s as rev1 instead of %s'
% (c.ancestor, c.a_rev))
rev1 = c.ancestor
org_repo = other_repo
else: # comparing tips, not necessarily linearly related
if merge:
log.error('Unable to find ancestor revision')
# TODO: we could do this by using hg unionrepo
log.error('cannot compare across repos %s and %s', org_repo, other_repo)
h.flash(_('Cannot compare repositories without using common ancestor'), category='error')
raise HTTPBadRequest
rev1 = c.a_rev
diff_limit = self.cut_off_limit if not c.fulldiff else None
log.debug('running diff between %s and %s in %s'
% (rev1, c.cs_rev, org_repo.scm_instance.path))
txtdiff = org_repo.scm_instance.get_diff(rev1=rev1, rev2=c.cs_rev,
ignore_whitespace=ignore_whitespace,
context=line_context)
diff_processor = diffs.DiffProcessor(txtdiff or '', format='gitdiff',
diff_limit=diff_limit)
_parsed = diff_processor.prepare()
c.limited_diff = False
if isinstance(_parsed, LimitedDiffContainer):
c.limited_diff = True
c.files = []
c.changes = {}
c.lines_added = 0
c.lines_deleted = 0
for f in _parsed:
st = f['stats']
if not st['binary']:
c.lines_added += st['added']
c.lines_deleted += st['deleted']
fid = h.FID('', f['filename'])
c.files.append([fid, f['operation'], f['filename'], f['stats']])
htmldiff = diff_processor.as_html(enable_comments=False, parsed_lines=[f])
c.changes[fid] = [f['operation'], f['filename'], htmldiff]
@@ -391,370 +391,373 @@ class PullrequestsController(BaseRepoCon
cs_ranges, _cs_ranges_not, ancestor_rev = CompareController._get_changesets(org_repo.scm_instance.alias,
other_repo.scm_instance, new_other_rev, # org and other "swapped"
org_repo.scm_instance, new_org_rev)
old_revisions = set(old_pull_request.revisions)
revisions = [cs.raw_id for cs in cs_ranges]
new_revisions = [r for r in revisions if r not in old_revisions]
lost = old_revisions.difference(revisions)
infos = ['','', 'This is an update of %s "%s".' %
(h.canonical_url('pullrequest_show', repo_name=old_pull_request.other_repo.repo_name,
pull_request_id=pull_request_id),
old_pull_request.title)]
if lost:
infos.append(_('Missing changesets since the previous version:'))
for r in old_pull_request.revisions:
if r in lost:
desc = org_repo.get_changeset(r).message.split('\n')[0]
infos.append(' %s "%s"' % (h.short_id(r), desc))
if new_revisions:
infos.append(_('New changesets on %s %s since the previous version:') % (org_ref_type, org_ref_name))
for r in reversed(revisions):
if r in new_revisions:
infos.append(' %s %s' % (h.short_id(r), h.shorter(desc, 80)))
if ancestor_rev == other_rev:
infos.append(_("Ancestor didn't change - show diff since previous version:"))
infos.append(h.canonical_url('compare_url',
repo_name=org_repo.repo_name, # other_repo is always same as repo_name
org_ref_type='rev', org_ref_name=h.short_id(org_rev), # use old org_rev as base
other_ref_type='rev', other_ref_name=h.short_id(new_org_rev),
)) # note: linear diff, merge or not doesn't matter
infos.append(_('This pull request is based on another %s revision and there is no simple diff.') % other_ref_name)
infos.append(_('No changes found on %s %s since previous version.') % (org_ref_type, org_ref_name))
# TODO: fail?
# hack: ancestor_rev is not an other_ref but we want to show the
# requested destination and have the exact ancestor
new_other_ref = '%s:%s:%s' % (other_ref_type, other_ref_name, ancestor_rev)
new_org_ref = '%s:%s:%s' % (org_ref_type, org_ref_name, new_org_rev)
reviewers = [r.user_id for r in old_pull_request.reviewers]
try:
old_title, old_v = re.match(r'(.*)\(v(\d+)\)\s*$', old_pull_request.title).groups()
v = int(old_v) + 1
except (AttributeError, ValueError):
old_title = old_pull_request.title
v = 2
title = '%s (v%s)' % (old_title.strip(), v)
description = (old_pull_request.description.rstrip() +
'\n'.join(infos))
pull_request = PullRequestModel().create(
self.authuser.user_id,
old_pull_request.org_repo.repo_name, new_org_ref,
old_pull_request.other_repo.repo_name, new_other_ref,
revisions, reviewers, title, description
except Exception:
h.flash(_('Error occurred while creating pull request'),
category='error')
log.error(traceback.format_exc())
return redirect(old_pull_request.url())
ChangesetCommentsModel().create(
text=_('Closed, replaced by %s .') % h.canonical_url('pullrequest_show',
repo_name=old_pull_request.other_repo.repo_name,
pull_request_id=pull_request.pull_request_id),
repo=old_pull_request.other_repo.repo_id,
user=c.authuser.user_id,
pull_request=pull_request_id,
closing_pr=True)
PullRequestModel().close_pull_request(pull_request_id)
Session().commit()
h.flash(_('Pull request update created'),
category='success')
return redirect(pull_request.url())
# pullrequest_post for PR editing
@NotAnonymous()
def post(self, repo_name, pull_request_id):
repo = RepoModel()._get_repo(repo_name)
pull_request = PullRequest.get_or_404(pull_request_id)
old_description = pull_request.description
_form = PullRequestPostForm()().to_python(request.POST)
pull_request.title = _form['pullrequest_title']
pull_request.description = _form['pullrequest_desc'].strip() or _('No description')
PullRequestModel().mention_from_description(pull_request, old_description)
h.flash(_('Pull request updated'), category='success')
# pullrequest_update for updating reviewer list
@jsonify
def update(self, repo_name, pull_request_id):
if pull_request.is_closed():
raise HTTPForbidden()
#only owner or admin can update it
owner = pull_request.author.user_id == c.authuser.user_id
repo_admin = h.HasRepoPermissionAny('repository.admin')(c.repo_name)
if h.HasPermissionAny('hg.admin') or repo_admin or owner:
reviewers_ids = map(int, filter(lambda v: v not in [None, ''],
request.POST.get('reviewers_ids', '').split(',')))
PullRequestModel().update_reviewers(pull_request_id, reviewers_ids)
return True
def delete(self, repo_name, pull_request_id):
#only owner can delete it !
if pull_request.author.user_id == c.authuser.user_id:
PullRequestModel().delete(pull_request)
h.flash(_('Successfully deleted pull request'),
return redirect(url('my_pullrequests'))
def show(self, repo_name, pull_request_id, extra=None):
repo_model = RepoModel()
c.users_array = repo_model.get_users_js()
c.user_groups_array = repo_model.get_user_groups_js()
c.pull_request = PullRequest.get_or_404(pull_request_id)
c.allowed_to_change_status = self._get_is_allowed_change_status(c.pull_request)
cc_model = ChangesetCommentsModel()
cs_model = ChangesetStatusModel()
# pull_requests repo_name we opened it against
# ie. other_repo must match
if repo_name != c.pull_request.other_repo.repo_name:
raise HTTPNotFound
# load compare data into template context
c.cs_repo = c.pull_request.org_repo
(c.cs_ref_type,
c.cs_ref_name,
c.cs_rev) = c.pull_request.org_ref.split(':')
c.a_repo = c.pull_request.other_repo
(c.a_ref_type,
c.a_ref_name,
c.a_rev) = c.pull_request.other_ref.split(':') # other_rev is ancestor
org_scm_instance = c.cs_repo.scm_instance # property with expensive cache invalidation check!!!
c.cs_repo = c.cs_repo
c.cs_ranges = [org_scm_instance.get_changeset(x) for x in c.pull_request.revisions]
c.cs_ranges_org = None # not stored and not important and moving target - could be calculated ...
c.jsdata = json.dumps(graph_data(org_scm_instance, revs))
c.available = []
c.cs_branch_name = c.cs_ref_name
other_scm_instance = c.a_repo.scm_instance
c.update_msg = ""
c.update_msg_other = ""
if org_scm_instance.alias == 'hg' and c.a_ref_name != 'ancestor':
if c.cs_ref_type != 'branch':
c.cs_branch_name = org_scm_instance.get_changeset(c.cs_ref_name).branch # use ref_type ?
other_branch_name = c.a_ref_name
if c.a_ref_type != 'branch':
other_branch_name = other_scm_instance.get_changeset(c.a_ref_name).branch # use ref_type ?
except EmptyRepositoryError:
other_branch_name = 'null' # not a branch name ... but close enough
# candidates: descendants of old head that are on the right branch
# and not are the old head itself ...
# and nothing at all if old head is a descendent of target ref name
if other_scm_instance._repo.revs('present(%s)::&%s', c.cs_ranges[-1].raw_id, other_branch_name):
c.update_msg = _('This pull request has already been merged to %s.') % other_branch_name
else: # look for children of PR head on source branch in org repo
arevs = org_scm_instance._repo.revs('%s:: & branch(%s) - %s',
revs[0], c.cs_branch_name, revs[0])
if arevs:
if c.pull_request.is_closed():
c.update_msg = _('This pull request has been closed and can not be updated with descendent changes on %s:') % c.cs_branch_name
c.update_msg = _('This pull request can be updated with descendent changes on %s:') % c.cs_branch_name
c.available = [org_scm_instance.get_changeset(x) for x in arevs]
c.update_msg = _('No changesets found for updating this pull request.')
revs = org_scm_instance._repo.revs('head() & not (%s::) & branch(%s) & !closed()', revs[0], c.cs_branch_name)
if revs:
c.update_msg_other = _('Note: Branch %s also contains unrelated changes, such as %s.') % (c.cs_branch_name,
h.short_id(org_scm_instance.get_changeset((max(revs))).raw_id))
c.update_msg_other = _('Branch %s does not contain other changes.') % c.cs_branch_name
elif org_scm_instance.alias == 'git':
c.update_msg = _("Git pull requests don't support updates yet.")
c.cs_comments = c.cs_repo.get_comments(raw_ids)
c.statuses = c.cs_repo.statuses(raw_ids)
# we swap org/other ref since we run a simple diff on one repo
% (c.a_rev, c.cs_rev, org_scm_instance.path))
txtdiff = org_scm_instance.get_diff(rev1=safe_str(c.a_rev), rev2=safe_str(c.cs_rev),
htmldiff = diff_processor.as_html(enable_comments=True,
parsed_lines=[f])
# inline comments
c.inline_cnt = 0
c.inline_comments = cc_model.get_inline_comments(
c.db_repo.repo_id,
pull_request=pull_request_id)
# count inline comments
for __, lines in c.inline_comments:
for comments in lines.values():
c.inline_cnt += len(comments)
# comments
c.comments = cc_model.get_comments(c.db_repo.repo_id,
# (badly named) pull-request status calculation based on reviewer votes
(c.pull_request_reviewers,
c.pull_request_pending_reviewers,
c.current_voting_result,
) = cs_model.calculate_pull_request_result(c.pull_request)
c.changeset_statuses = ChangesetStatus.STATUSES
c.as_form = False
c.ancestor = None # there is one - but right here we don't know which
return render('/pullrequests/pullrequest_show.html')
def comment(self, repo_name, pull_request_id):
status = 0
close_pr = False
allowed_to_change_status = self._get_is_allowed_change_status(pull_request)
if allowed_to_change_status:
status = request.POST.get('changeset_status')
close_pr = request.POST.get('save_close')
text = request.POST.get('text', '').strip() or _('No comments.')
if close_pr:
text = _('Closing.') + '\n' + text
comm = ChangesetCommentsModel().create(
text=text,
repo=c.db_repo.repo_id,
f_path=request.POST.get('f_path'),
line_no=request.POST.get('line'),
status_change=(ChangesetStatus.get_status_lbl(status)
if status and allowed_to_change_status else None),
closing_pr=close_pr
action_logger(self.authuser,
'user_commented_pull_request:%s' % pull_request_id,
c.db_repo, self.ip_addr, self.sa)
# get status if set !
if status:
ChangesetStatusModel().set_status(
status,
c.authuser.user_id,
comm,
pull_request=pull_request_id
'user_closed_pull_request:%s' % pull_request_id,
if not request.environ.get('HTTP_X_PARTIAL_XHR'):
data = {
'target_id': h.safeid(h.safe_unicode(request.POST.get('f_path'))),
}
if comm:
c.co = comm
data.update(comm.get_dict())
data.update({'rendered_text':
render('changeset/changeset_comment_block.html')})
return data
def delete_comment(self, repo_name, comment_id):
co = ChangesetComment.get(comment_id)
if co.pull_request.is_closed():
#don't allow deleting comments on closed pull request
owner = co.author.user_id == c.authuser.user_id
ChangesetCommentsModel().delete(comment=co)
@@ -247,211 +247,213 @@ class BaseVCSController(object):
# locking on those
obsolete_call = environ['QUERY_STRING'] in ['cmd=listkeys',]
locked_by = repo.locked
if repo and repo.enable_locking and not obsolete_call:
if action == 'push':
#check if it's already locked !, if it is compare users
user_id, _date = repo.locked
if user.user_id == user_id:
log.debug('Got push from user %s, now unlocking' % (user))
# unlock if we have push from user who locked
make_lock = False
# we're not the same user who locked, ban with 423 !
locked = True
if action == 'pull':
if repo.locked[0] and repo.locked[1]:
log.debug('Setting lock on repo %s by %s' % (repo, user))
make_lock = True
log.debug('Repository %s do not have locking enabled' % (repo))
log.debug('FINAL locking values make_lock:%s,locked:%s,locked_by:%s'
% (make_lock, locked, locked_by))
return make_lock, locked, locked_by
def __call__(self, environ, start_response):
start = time.time()
return self._handle_request(environ, start_response)
finally:
log = logging.getLogger('kallithea.' + self.__class__.__name__)
log.debug('Request time: %.3fs' % (time.time() - start))
meta.Session.remove()
class BaseController(WSGIController):
__before__ is called before controller methods and after __call__
c.kallithea_version = __version__
rc_config = Setting.get_app_settings()
# Visual options
c.visual = AttributeDict({})
## DB stored
c.visual.show_public_icon = str2bool(rc_config.get('show_public_icon'))
c.visual.show_private_icon = str2bool(rc_config.get('show_private_icon'))
c.visual.stylify_metatags = str2bool(rc_config.get('stylify_metatags'))
c.visual.dashboard_items = safe_int(rc_config.get('dashboard_items', 100))
c.visual.admin_grid_items = safe_int(rc_config.get('admin_grid_items', 100))
c.visual.repository_fields = str2bool(rc_config.get('repository_fields'))
c.visual.show_version = str2bool(rc_config.get('show_version'))
c.visual.use_gravatar = str2bool(rc_config.get('use_gravatar'))
c.visual.gravatar_url = rc_config.get('gravatar_url')
c.ga_code = rc_config.get('ga_code')
# TODO: replace undocumented backwards compatibility hack with db upgrade and rename ga_code
if c.ga_code and '<' not in c.ga_code:
c.ga_code = '''<script type="text/javascript">
var _gaq = _gaq || [];
_gaq.push(['_setAccount', '%s']);
_gaq.push(['_trackPageview']);
(function() {
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
})();
</script>''' % c.ga_code
c.site_name = rc_config.get('title')
c.clone_uri_tmpl = rc_config.get('clone_uri_tmpl')
## INI stored
c.visual.allow_repo_location_change = str2bool(config.get('allow_repo_location_change', True))
c.visual.allow_custom_hooks_settings = str2bool(config.get('allow_custom_hooks_settings', True))
c.instance_id = config.get('instance_id')
c.issues_url = config.get('bugtracker', url('issues_url'))
# END CONFIG VARS
c.repo_name = get_repo_slug(request) # can be empty
c.backends = BACKENDS.keys()
c.unread_notifications = NotificationModel()\
.get_unread_cnt_for_user(c.authuser.user_id)
self.cut_off_limit = safe_int(config.get('cut_off_limit'))
c.my_pr_count = PullRequestModel().get_pullrequest_cnt_for_user(c.authuser.user_id)
self.sa = meta.Session
self.scm_model = ScmModel(self.sa)
"""Invoke the Controller"""
# WSGIController.__call__ dispatches to the Controller method
# the request is routed to. This routing information is
# available in environ['pylons.routes_dict']
self.ip_addr = _get_ip_addr(environ)
# make sure that we update permissions each time we call controller
api_key = request.GET.get('api_key')
if api_key:
# when using API_KEY we are sure user exists.
auth_user = AuthUser(api_key=api_key, ip_addr=self.ip_addr)
authenticated = False
cookie_store = CookieStoreWrapper(session.get('authuser'))
auth_user = AuthUser(user_id=cookie_store.get('user_id', None),
ip_addr=self.ip_addr)
except UserCreationError, e:
h.flash(e, 'error')
# container auth or other auth functions that create users on
# the fly can throw this exception signaling that there's issue
# with user creation, explanation should be provided in
# Exception itself
auth_user = AuthUser(ip_addr=self.ip_addr)
authenticated = cookie_store.get('is_authenticated')
if not auth_user.is_authenticated and auth_user.user_id is not None:
# user is not authenticated and not empty
auth_user.set_authenticated(authenticated)
request.user = auth_user
#set globals for auth user
self.authuser = c.authuser = auth_user
log.info('IP: %s User: %s accessed %s' % (
self.ip_addr, auth_user, safe_unicode(_get_access_path(environ)))
return WSGIController.__call__(self, environ, start_response)
class BaseRepoController(BaseController):
Base class for controllers responsible for loading all needed data for
repository loaded items are
c.db_repo_scm_instance: instance of scm repository
c.db_repo: instance of db
c.repository_followers: number of followers
c.repository_forks: number of forks
c.repository_following: weather the current user is following the current repo
super(BaseRepoController, self).__before__()
if c.repo_name: # extracted from routes
_dbr = Repository.get_by_repo_name(c.repo_name)
if not _dbr:
return
log.debug('Found repository in database %s with state `%s`'
% (safe_unicode(_dbr), safe_unicode(_dbr.repo_state)))
route = getattr(request.environ.get('routes.route'), 'name', '')
# allow to delete repos that are somehow damages in filesystem
if route in ['delete_repo']:
if _dbr.repo_state in [Repository.STATE_PENDING]:
if route in ['repo_creating_home']:
check_url = url('repo_creating_home', repo_name=c.repo_name)
return redirect(check_url)
dbr = c.db_repo = _dbr
c.db_repo_scm_instance = c.db_repo.scm_instance
if c.db_repo_scm_instance is None:
log.error('%s this repository is present in database but it '
'cannot be created as an scm instance', c.repo_name)
h.flash(h.literal(_('Repository not found in the filesystem')),
raise paste.httpexceptions.HTTPNotFound()
# some globals counter for menu
c.repository_followers = self.scm_model.get_followers(dbr)
c.repository_forks = self.scm_model.get_forks(dbr)
c.repository_pull_requests = self.scm_model.get_pull_requests(dbr)
c.repository_following = self.scm_model.is_following_repo(
c.repo_name, self.authuser.user_id)
def _get_ref_rev(repo, ref_type, ref_name):
def _get_ref_rev(repo, ref_type, ref_name, returnempty=False):
Safe way to get changeset. If error occurs show error.
return repo.scm_instance.get_ref_revision(ref_type, ref_name)
except EmptyRepositoryError as e:
if returnempty:
return repo.scm_instance.EMPTY_CHANGESET
h.flash(h.literal(_('There are no changesets yet')),
raise webob.exc.HTTPNotFound()
except ChangesetDoesNotExistError as e:
h.flash(h.literal(_('Changeset not found')),
except RepositoryError as e:
h.flash(safe_str(e), category='error')
raise webob.exc.HTTPBadRequest()
Status change: