##// END OF EJS Templates
chore: bump version
chore: bump version

File last commit:

r5093:525812a8 default
r5417:75a4ae34 v5.0.2 stable
Show More
repo_forks.py
252 lines | 9.5 KiB | text/x-python | PythonLexer
# Copyright (C) 2011-2023 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/
import logging
import datetime
import formencode
import formencode.htmlfill
from pyramid.httpexceptions import HTTPFound
from pyramid.renderers import render
from pyramid.response import Response
from rhodecode import events
from rhodecode.apps._base import RepoAppView, DataGridAppView
from rhodecode.lib.auth import (
LoginRequired, HasRepoPermissionAnyDecorator, NotAnonymous,
HasRepoPermissionAny, HasPermissionAnyDecorator, CSRFRequired)
import rhodecode.lib.helpers as h
from rhodecode.lib.str_utils import safe_str
from rhodecode.lib.celerylib.utils import get_task_id
from rhodecode.model.db import coalesce, or_, Repository, RepoGroup
from rhodecode.model.permission import PermissionModel
from rhodecode.model.repo import RepoModel
from rhodecode.model.forms import RepoForkForm
from rhodecode.model.scm import ScmModel, RepoGroupList
log = logging.getLogger(__name__)
class RepoForksView(RepoAppView, DataGridAppView):
def load_default_context(self):
c = self._get_local_tmpl_context(include_app_defaults=True)
c.rhodecode_repo = self.rhodecode_vcs_repo
acl_groups = RepoGroupList(
RepoGroup.query().all(),
perm_set=['group.write', 'group.admin'])
c.repo_groups = RepoGroup.groups_choices(groups=acl_groups)
c.repo_groups_choices = list(map(lambda k: safe_str(k[0]), c.repo_groups))
c.personal_repo_group = c.rhodecode_user.personal_repo_group
return c
@LoginRequired()
@HasRepoPermissionAnyDecorator(
'repository.read', 'repository.write', 'repository.admin')
def repo_forks_show_all(self):
c = self.load_default_context()
return self._get_template_context(c)
@LoginRequired()
@HasRepoPermissionAnyDecorator(
'repository.read', 'repository.write', 'repository.admin')
def repo_forks_data(self):
_ = self.request.translate
self.load_default_context()
column_map = {
'fork_name': 'repo_name',
'fork_date': 'created_on',
'last_activity': 'updated_on'
}
draw, start, limit = self._extract_chunk(self.request)
search_q, order_by, order_dir = self._extract_ordering(
self.request, column_map=column_map)
acl_check = HasRepoPermissionAny(
'repository.read', 'repository.write', 'repository.admin')
repo_id = self.db_repo.repo_id
allowed_ids = [-1]
for f in Repository.query().filter(Repository.fork_id == repo_id):
if acl_check(f.repo_name, 'get forks check'):
allowed_ids.append(f.repo_id)
forks_data_total_count = Repository.query()\
.filter(Repository.fork_id == repo_id)\
.filter(Repository.repo_id.in_(allowed_ids))\
.count()
# json generate
base_q = Repository.query()\
.filter(Repository.fork_id == repo_id)\
.filter(Repository.repo_id.in_(allowed_ids))\
if search_q:
like_expression = f'%{safe_str(search_q)}%'
base_q = base_q.filter(or_(
Repository.repo_name.ilike(like_expression),
Repository.description.ilike(like_expression),
))
forks_data_total_filtered_count = base_q.count()
sort_col = getattr(Repository, order_by, None)
if sort_col:
if order_dir == 'asc':
# handle null values properly to order by NULL last
if order_by in ['last_activity']:
sort_col = coalesce(sort_col, datetime.date.max)
sort_col = sort_col.asc()
else:
# handle null values properly to order by NULL last
if order_by in ['last_activity']:
sort_col = coalesce(sort_col, datetime.date.min)
sort_col = sort_col.desc()
base_q = base_q.order_by(sort_col)
base_q = base_q.offset(start).limit(limit)
fork_list = base_q.all()
def fork_actions(fork):
url_link = h.route_path(
'repo_compare',
repo_name=fork.repo_name,
source_ref_type=self.db_repo.landing_ref_type,
source_ref=self.db_repo.landing_ref_name,
target_ref_type=self.db_repo.landing_ref_type,
target_ref=self.db_repo.landing_ref_name,
_query=dict(merge=1, target_repo=f.repo_name))
return h.link_to(_('Compare fork'), url_link, class_='btn-link')
def fork_name(fork):
return h.link_to(fork.repo_name,
h.route_path('repo_summary', repo_name=fork.repo_name))
forks_data = []
for fork in fork_list:
forks_data.append({
"username": h.gravatar_with_user(self.request, fork.user.username),
"fork_name": fork_name(fork),
"description": fork.description_safe,
"fork_date": h.age_component(fork.created_on, time_is_local=True),
"last_activity": h.format_date(fork.updated_on),
"action": fork_actions(fork),
})
data = ({
'draw': draw,
'data': forks_data,
'recordsTotal': forks_data_total_count,
'recordsFiltered': forks_data_total_filtered_count,
})
return data
@LoginRequired()
@NotAnonymous()
@HasPermissionAnyDecorator('hg.admin', PermissionModel.FORKING_ENABLED)
@HasRepoPermissionAnyDecorator(
'repository.read', 'repository.write', 'repository.admin')
def repo_fork_new(self):
c = self.load_default_context()
defaults = RepoModel()._get_defaults(self.db_repo_name)
# alter the description to indicate a fork
defaults['description'] = (
'fork of repository: {} \n{}'.format(
defaults['repo_name'], defaults['description']))
# add suffix to fork
defaults['repo_name'] = '%s-fork' % defaults['repo_name']
data = render('rhodecode:templates/forks/fork.mako',
self._get_template_context(c), self.request)
html = formencode.htmlfill.render(
data,
defaults=defaults,
encoding="UTF-8",
force_defaults=False
)
return Response(html)
@LoginRequired()
@NotAnonymous()
@HasPermissionAnyDecorator('hg.admin', PermissionModel.FORKING_ENABLED)
@HasRepoPermissionAnyDecorator(
'repository.read', 'repository.write', 'repository.admin')
@CSRFRequired()
def repo_fork_create(self):
_ = self.request.translate
c = self.load_default_context()
_form = RepoForkForm(self.request.translate,
old_data={'repo_type': self.db_repo.repo_type},
repo_groups=c.repo_groups_choices)()
post_data = dict(self.request.POST)
# forbid injecting other repo by forging a request
post_data['fork_parent_id'] = self.db_repo.repo_id
post_data['landing_rev'] = self.db_repo._landing_revision
form_result = {}
task_id = None
try:
form_result = _form.to_python(post_data)
copy_permissions = form_result.get('copy_permissions')
# create fork is done sometimes async on celery, db transaction
# management is handled there.
task = RepoModel().create_fork(
form_result, c.rhodecode_user.user_id)
task_id = get_task_id(task)
except formencode.Invalid as errors:
c.rhodecode_db_repo = self.db_repo
data = render('rhodecode:templates/forks/fork.mako',
self._get_template_context(c), self.request)
html = formencode.htmlfill.render(
data,
defaults=errors.value,
errors=errors.error_dict or {},
prefix_error=False,
encoding="UTF-8",
force_defaults=False
)
return Response(html)
except Exception:
log.exception(
'Exception while trying to fork the repository %s', self.db_repo_name)
msg = _('An error occurred during repository forking %s') % (self.db_repo_name, )
h.flash(msg, category='error')
raise HTTPFound(h.route_path('home'))
repo_name = form_result.get('repo_name_full', self.db_repo_name)
affected_user_ids = [self._rhodecode_user.user_id]
if copy_permissions:
# permission flush is done in repo creating
pass
PermissionModel().trigger_permission_flush(affected_user_ids)
raise HTTPFound(
h.route_path('repo_creating', repo_name=repo_name,
_query=dict(task_id=task_id)))