@@ -462,22 +462,23 @@ def make_map(config):
rmap.connect('repo_settings_home', '/{repo_name:.*}/settings',
controller='settings', action='index',
conditions=dict(function=check_repo))
rmap.connect('repo_fork_create_home', '/{repo_name:.*}/fork',
controller='settings', action='fork_create',
controller='forks', action='fork_create',
conditions=dict(function=check_repo, method=["POST"]))
rmap.connect('repo_fork_home', '/{repo_name:.*}/fork',
controller='settings', action='fork',
controller='forks', action='fork',
rmap.connect('repo_forks_home', '/{repo_name:.*}/forks',
controller='forks', action='forks',
rmap.connect('repo_followers_home', '/{repo_name:.*}/followers',
controller='followers', action='followers',
return rmap
@@ -26,27 +26,28 @@
import logging
import traceback
import formencode
from formencode import htmlfill
from paste.httpexceptions import HTTPInternalServerError
from pylons import request, response, session, tmpl_context as c, url
from pylons.controllers.util import abort, redirect
from pylons import request, session, tmpl_context as c, url
from pylons.controllers.util import redirect
from pylons.i18n.translation import _
from sqlalchemy.exc import IntegrityError
from rhodecode.lib import helpers as h
from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator, \
HasPermissionAnyDecorator
from rhodecode.lib.base import BaseController, render
from rhodecode.lib.utils import invalidate_cache, action_logger, repo_name_slug
from rhodecode.lib.helpers import get_token
from rhodecode.model.meta import Session
from rhodecode.model.db import User, Repository, UserFollowing, RepoGroup
from rhodecode.model.forms import RepoForm
from rhodecode.model.scm import ScmModel
from rhodecode.model.repo import RepoModel
log = logging.getLogger(__name__)
class ReposController(BaseController):
"""
@@ -62,13 +63,13 @@ class ReposController(BaseController):
c.admin_username = session.get('admin_username')
super(ReposController, self).__before__()
def __load_defaults(self):
c.repo_groups = RepoGroup.groups_choices()
c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups)
repo_model = RepoModel()
c.users_array = repo_model.get_users_js()
c.users_groups_array = repo_model.get_users_groups_js()
def __load_data(self, repo_name=None):
@@ -124,35 +125,35 @@ class ReposController(BaseController):
@HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
def create(self):
POST /repos: Create a new item"""
# url('repos')
self.__load_defaults()
form_result = {}
try:
form_result = RepoForm(repo_groups=c.repo_groups_choices)()\
.to_python(dict(request.POST))
repo_model.create(form_result, self.rhodecode_user)
RepoModel().create(form_result, self.rhodecode_user)
if form_result['clone_uri']:
h.flash(_('created repository %s from %s') \
% (form_result['repo_name'], form_result['clone_uri']),
category='success')
else:
h.flash(_('created repository %s') % form_result['repo_name'],
if request.POST.get('user_created'):
#created by regular non admin user
# created by regular non admin user
action_logger(self.rhodecode_user, 'user_created_repo',
form_result['repo_name_full'], '', self.sa)
action_logger(self.rhodecode_user, 'admin_created_repo',
Session().commit()
except formencode.Invalid, errors:
c.new_repo = errors.value['repo_name']
r = render('admin/repos/repo_add_create_repository.html')
@@ -204,13 +205,13 @@ class ReposController(BaseController):
invalidate_cache('get_repo_cached_%s' % repo_name)
h.flash(_('Repository %s updated successfully' % repo_name),
changed_name = repo.repo_name
action_logger(self.rhodecode_user, 'admin_updated_repo',
changed_name, '', self.sa)
defaults = self.__load_data(repo_name)
defaults.update(errors.value)
return htmlfill.render(
render('admin/repos/repo_edit.html'),
defaults=defaults,
@@ -248,13 +249,13 @@ class ReposController(BaseController):
action_logger(self.rhodecode_user, 'admin_deleted_repo',
repo_name, '', self.sa)
repo_model.delete(repo)
h.flash(_('deleted repository %s') % repo_name, category='success')
except IntegrityError, e:
if e.message.find('repositories_fork_id_fkey'):
log.error(traceback.format_exc())
h.flash(_('Cannot delete %s it still contains attached '
'forks') % repo_name,
category='warning')
@@ -20,37 +20,155 @@
# 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 General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from pylons import tmpl_context as c, request
from pylons import tmpl_context as c, request, url
import rhodecode.lib.helpers as h
from rhodecode.lib.helpers import Page
from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator, \
NotAnonymous
from rhodecode.lib.base import BaseRepoController, render
from rhodecode.model.db import Repository, User, UserFollowing
from rhodecode.model.db import Repository, RepoGroup, UserFollowing, User
from rhodecode.model.forms import RepoForkForm
class ForksController(BaseRepoController):
@LoginRequired()
@HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
'repository.admin')
def __before__(self):
super(ForksController, self).__before__()
Load defaults settings for edit, and update
:param repo_name:
c.repo_info = db_repo = Repository.get_by_repo_name(repo_name)
repo = db_repo.scm_instance
if c.repo_info is None:
h.flash(_('%s repository is not mapped to db perhaps'
' it was created or renamed from the filesystem'
' please run the application again'
' in order to rescan repositories') % repo_name,
category='error')
return redirect(url('repos'))
c.default_user_id = User.get_by_username('default').user_id
c.in_public_journal = UserFollowing.query()\
.filter(UserFollowing.user_id == c.default_user_id)\
.filter(UserFollowing.follows_repository == c.repo_info).scalar()
if c.repo_info.stats:
last_rev = c.repo_info.stats.stat_on_revision
last_rev = 0
c.stats_revision = last_rev
c.repo_last_rev = repo.count() - 1 if repo.revisions else 0
if last_rev == 0 or c.repo_last_rev == 0:
c.stats_percentage = 0
c.stats_percentage = '%.2f' % ((float((last_rev)) /
c.repo_last_rev) * 100)
defaults = RepoModel()._get_defaults(repo_name)
# add prefix to fork
defaults['repo_name'] = 'fork-' + defaults['repo_name']
return defaults
def forks(self, repo_name):
p = int(request.params.get('page', 1))
repo_id = c.rhodecode_db_repo.repo_id
d = Repository.get_repo_forks(repo_id)
c.forks_pager = Page(d, page=p, items_per_page=20)
c.forks_data = render('/forks/forks_data.html')
if request.environ.get('HTTP_X_PARTIAL_XHR'):
return c.forks_data
return render('/forks/forks.html')
@NotAnonymous()
def fork(self, repo_name):
c.repo_info = Repository.get_by_repo_name(repo_name)
if not c.repo_info:
' it was created or renamed from the file system'
return redirect(url('home'))
render('forks/fork.html'),
encoding="UTF-8",
force_defaults=False
)
def fork_create(self, repo_name):
_form = RepoForkForm(old_data={'repo_type': c.repo_info.repo_type},
repo_groups=c.repo_groups_choices,)()
form_result = _form.to_python(dict(request.POST))
# add org_path of repo so we can do a clone from it later
form_result['org_path'] = c.repo_info.repo_name
# create fork is done sometimes async on celery, db transaction
# management is handled there.
RepoModel().create_fork(form_result, self.rhodecode_user)
h.flash(_('forked %s repository as %s') \
% (repo_name, form_result['repo_name']),
defaults=errors.value,
errors=errors.error_dict or {},
prefix_error=False,
encoding="UTF-8")
except Exception:
h.flash(_('An error occurred during repository forking %s') %
repo_name, category='error')
@@ -20,27 +20,28 @@
from itertools import groupby
from sqlalchemy import or_
from sqlalchemy.orm import joinedload, make_transient
from sqlalchemy.orm import joinedload
from webhelpers.paginate import Page
from webhelpers.feedgenerator import Atom1Feed, Rss201rev2Feed
from paste.httpexceptions import HTTPBadRequest
from pylons import request, tmpl_context as c, response, url
from rhodecode.lib.auth import LoginRequired, NotAnonymous
from rhodecode.model.db import UserLog, UserFollowing
class JournalController(BaseController):
@@ -121,21 +122,23 @@ class JournalController(BaseController):
user_id = request.POST.get('follows_user_id')
if user_id:
self.scm_model.toggle_following_user(user_id,
self.rhodecode_user.user_id)
return 'ok'
except:
raise HTTPBadRequest()
repo_id = request.POST.get('follows_repo_id')
if repo_id:
self.scm_model.toggle_following_repo(repo_id,
log.debug('token mismatch %s vs %s', cur_token, token)
@@ -32,38 +32,38 @@ from formencode import htmlfill
from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAllDecorator, \
HasRepoPermissionAnyDecorator, NotAnonymous
from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAllDecorator
from rhodecode.lib.utils import invalidate_cache, action_logger
from rhodecode.model.forms import RepoSettingsForm, RepoForkForm
from rhodecode.model.forms import RepoSettingsForm
from rhodecode.model.db import RepoGroup
class SettingsController(BaseRepoController):
super(SettingsController, self).__before__()
@HasRepoPermissionAllDecorator('repository.admin')
def index(self, repo_name):
c.repo_info = repo = repo_model.get_by_repo_name(repo_name)
if not repo:
@@ -86,28 +86,29 @@ class SettingsController(BaseRepoControl
def update(self, repo_name):
changed_name = repo_name
_form = RepoSettingsForm(edit=True,
old_data={'repo_name': repo_name},
repo_groups=c.repo_groups_choices)()
repo_model.update(repo_name, form_result)
changed_name = form_result['repo_name_full']
action_logger(self.rhodecode_user, 'user_updated_repo',
c.repo_info = repo_model.get_by_repo_name(repo_name)
errors.value.update({'user': c.repo_info.user.username})
render('settings/repo_settings.html'),
@@ -145,64 +146,13 @@ class SettingsController(BaseRepoControl
action_logger(self.rhodecode_user, 'user_deleted_repo',
h.flash(_('An error occurred during deletion of %s') % repo_name,
return render('settings/repo_fork.html')
_form = RepoForkForm(old_data={'repo_type': c.repo_info.repo_type})()
form_result.update({'repo_name': repo_name})
repo_model.create_fork(form_result, self.rhodecode_user)
% (repo_name, form_result['fork_name']),
action_logger(self.rhodecode_user,
'user_forked_repo:%s' % form_result['fork_name'],
c.new_repo = errors.value['fork_name']
r = render('settings/repo_fork.html')
r,
@@ -34,35 +34,34 @@ from time import mktime
from operator import itemgetter
from string import lower
from pylons import config, url
from vcs import get_backend
from rhodecode.lib import LANGUAGES_EXTENSIONS_MAP, safe_str
from rhodecode.lib.celerylib import run_task, locked_task, str2bool, \
__get_lockkey, LockHeld, DaemonLock
from rhodecode.lib.helpers import person
from rhodecode.lib.rcmail.smtp_mailer import SmtpMailer
from rhodecode.lib.utils import add_cache
from rhodecode.lib.utils import add_cache, action_logger
from rhodecode.lib.compat import json, OrderedDict
from rhodecode.model import init_model
from rhodecode.model import meta
from rhodecode.model.db import RhodeCodeUi, Statistics, Repository, User
from vcs.backends import get_repo
from rhodecode.model.db import Statistics, Repository, User
from sqlalchemy import engine_from_config
add_cache(config)
__all__ = ['whoosh_index', 'get_commits_stats',
'reset_user_password', 'send_email']
CELERY_ON = str2bool(config['app_conf'].get('use_celery'))
def get_session():
if CELERY_ON:
engine = engine_from_config(config, 'sqlalchemy.db1.')
@@ -78,23 +77,19 @@ def get_logger(cls):
return log
def get_repos_path():
sa = get_session()
q = sa.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key == '/').one()
return q.ui_value
@task(ignore_result=True)
@locked_task
def whoosh_index(repo_location, full_index):
from rhodecode.lib.indexers.daemon import WhooshIndexingDaemon
#log = whoosh_index.get_logger()
index_location = config['index_dir']
WhooshIndexingDaemon(index_location=index_location,
repo_location=repo_location, sa=get_session())\
.run(full_index=full_index)
@@ -108,19 +103,18 @@ def get_commits_stats(repo_name, ts_min_
log.info('running task with lockkey %s', lockkey)
lock = l = DaemonLock(file_=jn(lockkey_path, lockkey))
#for js data compatibilty cleans the key for person from '
# for js data compatibilty cleans the key for person from '
akc = lambda k: person(k).replace('"', "")
co_day_auth_aggr = {}
commits_by_day_aggregate = {}
repos_path = get_repos_path()
repo = get_repo(safe_str(os.path.join(repos_path, repo_name)))
repo = Repository.get_by_repo_name(repo_name).scm_instance
repo_size = len(repo.revisions)
#return if repo have no revisions
if repo_size < 1:
lock.release()
return True
@@ -136,15 +130,15 @@ def get_commits_stats(repo_name, ts_min_
.filter(Statistics.repository == dbrepo).scalar()
if cur_stats is not None:
last_rev = cur_stats.stat_on_revision
if last_rev == repo.get_changeset().revision and repo_size > 1:
#pass silently without any work if we're not on first revision or
#current state of parsing revision(from db marker) is the
#last revision
# pass silently without any work if we're not on first revision or
# current state of parsing revision(from db marker) is the
# last revision
if cur_stats:
commits_by_day_aggregate = OrderedDict(json.loads(
cur_stats.commit_activity_combined))
@@ -252,16 +246,17 @@ def get_commits_stats(repo_name, ts_min_
except LockHeld:
log.info('LockHeld')
return 'Task with key %s already running' % lockkey
def send_password_link(user_email):
from rhodecode.model.notification import EmailNotificationModel
log = get_logger(send_password_link)
user = User.get_by_email(user_email)
if user:
log.debug('password reset user found %s' % user)
link = url('reset_password_confirmation', key=user.api_key,
qualified=True)
@@ -280,15 +275,15 @@ def send_password_link(user_email):
return False
def reset_user_password(user_email):
log = get_logger(reset_user_password)
from rhodecode.lib import auth
new_passwd = auth.PasswordGenerator().gen_password(8,
@@ -358,33 +353,45 @@ def send_email(recipients, subject, body
def create_repo_fork(form_data, cur_user):
Creates a fork of repository using interval VCS methods
:param form_data:
:param cur_user:
log = get_logger(create_repo_fork)
Session = get_session()
base_path = Repository.base_path()
RepoModel(Session).create(form_data, cur_user, just_db=True, fork=True)
alias = form_data['repo_type']
org_repo_name = form_data['org_path']
source_repo_path = os.path.join(base_path, org_repo_name)
destination_fork_path = os.path.join(base_path, form_data['repo_name_full'])
repo_model = RepoModel(get_session())
repo_model.create(form_data, cur_user, just_db=True, fork=True)
repo_name = form_data['repo_name']
repo_path = os.path.join(repos_path, repo_name)
repo_fork_path = os.path.join(repos_path, form_data['fork_name'])
log.info('creating repo fork %s as %s', repo_name, repo_path)
log.info('creating fork of %s as %s', source_repo_path,
destination_fork_path)
backend = get_backend(alias)
backend(str(repo_fork_path), create=True, src_url=str(repo_path))
backend(safe_str(destination_fork_path), create=True,
src_url=safe_str(source_repo_path))
action_logger(cur_user, 'user_forked_repo:%s' % org_repo_name,
org_repo_name, '', Session)
# finally commit at latest possible stage
Session.commit()
def __get_codes_stats(repo_name):
tip = repo.get_changeset()
code_stats = {}
def aggregate(cs):
for f in cs[2]:
ext = lower(f.extension)
@@ -30,13 +30,14 @@ from mercurial.node import nullrev
from rhodecode.lib.utils import action_logger
def repo_size(ui, repo, hooktype=None, **kwargs):
"""Presents size of repository after push
Presents size of repository after push
:param ui:
:param repo:
:param hooktype:
@@ -62,30 +63,33 @@ def repo_size(ui, repo, hooktype=None, *
size_total_f = h.format_byte_size(size_root + size_hg)
sys.stdout.write('Repository size .hg:%s repo:%s total:%s\n' \
% (size_hg_f, size_root_f, size_total_f))
def log_pull_action(ui, repo, **kwargs):
"""Logs user last pull action
Logs user last pull action
extra_params = dict(repo.ui.configitems('rhodecode_extras'))
username = extra_params['username']
repository = extra_params['repository']
action = 'pull'
action_logger(username, action, repository, extra_params['ip'])
action_logger(username, action, repository, extra_params['ip'],
commit=True)
return 0
def log_push_action(ui, repo, **kwargs):
"""Maps user last push action to new changeset id, from mercurial
Maps user last push action to new changeset id, from mercurial
@@ -107,9 +111,10 @@ def log_push_action(ui, repo, **kwargs):
stop, start = get_revs(repo, [node + ':'])
revs = (str(repo[r]) for r in xrange(start, stop + 1))
action = action % ','.join(revs)
@@ -90,13 +90,13 @@ def repo_name_slug(value):
def get_repo_slug(request):
return request.environ['pylons.routes_dict'].get('repo_name')
def action_logger(user, action, repo, ipaddr='', sa=None):
def action_logger(user, action, repo, ipaddr='', sa=None, commit=False):
Action logger for various actions made by users
:param user: user that made this action, can be a unique username string or
object containing user_id attribute
:param action: action to log, should be on of predefined unique actions for
@@ -135,18 +135,19 @@ def action_logger(user, action, repo, ip
user_log.repository_id = repo_obj.repo_id
user_log.repository_name = repo_name
user_log.action_date = datetime.datetime.now()
user_log.user_ip = ipaddr
sa.add(user_log)
sa.commit()
log.info('Adding user %s, action %s on %s', user_obj, action, repo)
if commit:
sa.rollback()
raise
def get_repos(path, recursive=False):
Scans given path for repos and return (name,(type,path)) tuple
@@ -182,36 +182,36 @@ class ValidPassword(formencode.validator
return value
class ValidPasswordsMatch(formencode.validators.FancyValidator):
def validate_python(self, value, state):
pass_val = value.get('password') or value.get('new_password')
if pass_val != value['password_confirmation']:
e_dict = {'password_confirmation':
_('Passwords do not match')}
raise formencode.Invalid('', value, state, error_dict=e_dict)
class ValidAuth(formencode.validators.FancyValidator):
messages = {
'invalid_password':_('invalid password'),
'invalid_login':_('invalid user name'),
'disabled_account':_('Your account is disabled')
}
# error mapping
e_dict = {'username':messages['invalid_login'],
'password':messages['invalid_password']}
e_dict_disable = {'username':messages['disabled_account']}
password = value['password']
username = value['username']
user = User.get_by_username(username)
if authenticate(username, password):
if user and user.active is False:
log.warning('user %s is disabled', username)
raise formencode.Invalid(self.message('disabled_account',
@@ -251,13 +251,13 @@ def ValidRepoName(edit, old_data):
gr = RepoGroup.get(value.get('repo_group'))
group_path = gr.full_path
# value needs to be aware of group name in order to check
# db key This is an actual just the name to store in the
# database
repo_name_full = group_path + RepoGroup.url_sep() + repo_name
group_path = ''
repo_name_full = repo_name
value['repo_name_full'] = repo_name_full
@@ -286,30 +286,14 @@ def ValidRepoName(edit, old_data):
error_dict=e_dict)
return _ValidRepoName
def ValidForkName():
class _ValidForkName(formencode.validators.FancyValidator):
def to_python(self, value, state):
repo_name = value.get('fork_name')
slug = repo_name_slug(repo_name)
if slug in [ADMIN_PREFIX, '']:
e_dict = {'repo_name': _('This repository name is disallowed')}
if RepoModel().get_by_repo_name(repo_name):
e_dict = {'fork_name':_('This repository '
'already exists')}
raise formencode.Invalid('', value, state,
return _ValidForkName
def ValidForkName(*args, **kwargs):
return ValidRepoName(*args, **kwargs)
def SlugifyName():
class _SlugifyName(formencode.validators.FancyValidator):
@@ -510,13 +494,13 @@ def UserForm(edit=False, old_data={}):
new_password = All(UnicodeString(strip=True, min=6, not_empty=False))
password_confirmation = All(UnicodeString(strip=True, min=6, not_empty=False))
admin = StringBoolean(if_missing=False)
password = All(UnicodeString(strip=True, min=6, not_empty=True))
active = StringBoolean(if_missing=False)
name = UnicodeString(strip=True, min=1, not_empty=True)
lastname = UnicodeString(strip=True, min=1, not_empty=True)
email = All(Email(not_empty=True), UniqSystemEmail(old_data))
chained_validators = [ValidPasswordsMatch, ValidPassword]
@@ -602,23 +586,26 @@ def RepoForm(edit=False, old_data={}, su
#this is repo owner
user = All(UnicodeString(not_empty=True), ValidRepoUser)
chained_validators = [ValidRepoName(edit, old_data), ValidPerms]
return _RepoForm
def RepoForkForm(edit=False, old_data={}, supported_backends=BACKENDS.keys()):
def RepoForkForm(edit=False, old_data={}, supported_backends=BACKENDS.keys(),
repo_groups=[]):
class _RepoForkForm(formencode.Schema):
allow_extra_fields = True
filter_extra_fields = False
fork_name = All(UnicodeString(strip=True, min=1, not_empty=True),
repo_name = All(UnicodeString(strip=True, min=1, not_empty=True),
SlugifyName())
repo_group = OneOf(repo_groups, hideList=True)
repo_type = All(ValidForkType(old_data), OneOf(supported_backends))
description = UnicodeString(strip=True, min=1, not_empty=True)
private = StringBoolean(if_missing=False)
chained_validators = [ValidForkName()]
copy_permissions = StringBoolean(if_missing=False)
fork_parent_id = UnicodeString()
chained_validators = [ValidForkName(edit, old_data)]
return _RepoForkForm
def RepoSettingsForm(edit=False, old_data={}, supported_backends=BACKENDS.keys(),
class _RepoForm(formencode.Schema):
@@ -627,13 +614,13 @@ def RepoSettingsForm(edit=False, old_dat
chained_validators = [ValidRepoName(edit, old_data), ValidPerms,
ValidSettings]
def ApplicationSettingsForm():
class _ApplicationSettingsForm(formencode.Schema):
@@ -32,14 +32,12 @@ import datetime
from pylons import config
from rhodecode.model import BaseModel
from rhodecode.model.db import Notification, User, UserNotification
from rhodecode.lib.celerylib import run_task
from rhodecode.lib.celerylib.tasks import send_email
class NotificationModel(BaseModel):
@@ -71,12 +69,13 @@ class NotificationModel(BaseModel):
notification
:param subject:
:param body:
:param recipients: list of int, str or User objects
:param type_: type of notification
from rhodecode.lib.celerylib import tasks, run_task
if not getattr(recipients, '__iter__', False):
raise Exception('recipients must be a list of iterable')
created_by_obj = self.__get_user(created_by)
@@ -97,13 +96,13 @@ class NotificationModel(BaseModel):
email_subject = NotificationModel().make_description(notif, False)
type_ = EmailNotificationModel.TYPE_CHANGESET_COMMENT
email_body = body
email_body_html = EmailNotificationModel()\
.get_email_tmpl(type_, **{'subject':subject,
'body':h.rst(body)})
run_task(send_email, rec.email, email_subject, email_body,
run_task(tasks.send_email, rec.email, email_subject, email_body,
email_body_html)
return notif
def delete(self, user, notification):
# we don't want to remove actual notification just the assignment
@@ -209,42 +209,39 @@ class RepoModel(BaseModel):
self.sa.rollback()
def create(self, form_data, cur_user, just_db=False, fork=False):
if fork:
repo_name = form_data['fork_name']
org_name = form_data['repo_name']
org_full_name = org_name
fork_parent_id = form_data['fork_parent_id']
org_name = repo_name = form_data['repo_name']
repo_name_full = form_data['repo_name_full']
# repo name is just a name of repository
# while repo_name_full is a full qualified name that is combined
# with name and path of group
new_repo = Repository()
new_repo.enable_statistics = False
for k, v in form_data.items():
if k == 'repo_name':
v = repo_name
v = repo_name_full
if k == 'repo_group':
k = 'group_id'
if k == 'description':
v = v or repo_name
setattr(new_repo, k, v)
parent_repo = self.sa.query(Repository)\
.filter(Repository.repo_name == org_full_name).one()
parent_repo = Repository.get(fork_parent_id)
new_repo.fork = parent_repo
new_repo.user_id = cur_user.user_id
self.sa.add(new_repo)
#create default permission
@@ -268,25 +265,27 @@ class RepoModel(BaseModel):
if not just_db:
self.__create_repo(repo_name, form_data['repo_type'],
form_data['repo_group'],
form_data['clone_uri'])
self.sa.commit()
#now automatically start following this repository as owner
# now automatically start following this repository as owner
ScmModel(self.sa).toggle_following_repo(new_repo.repo_id,
cur_user.user_id)
return new_repo
def create_fork(self, form_data, cur_user):
Simple wrapper into executing celery task for fork creation
run_task(tasks.create_repo_fork, form_data, cur_user)
def delete(self, repo):
self.sa.delete(repo)
@@ -322,12 +321,17 @@ class RepoModel(BaseModel):
def delete_stats(self, repo_name):
removes stats for given repo
obj = self.sa.query(Statistics)\
.filter(Statistics.repository == \
self.get_by_repo_name(repo_name)).one()
self.sa.delete(obj)
@@ -205,63 +205,55 @@ class ScmModel(BaseModel):
f = self.sa.query(UserFollowing)\
.filter(UserFollowing.follows_repo_id == follow_repo_id)\
.filter(UserFollowing.user_id == user_id).scalar()
if f is not None:
self.sa.delete(f)
action_logger(UserTemp(user_id),
'stopped_following_repo',
RepoTemp(follow_repo_id))
return
f = UserFollowing()
f.user_id = user_id
f.follows_repo_id = follow_repo_id
self.sa.add(f)
'started_following_repo',
def toggle_following_user(self, follow_user_id, user_id):
.filter(UserFollowing.follows_user_id == follow_user_id)\
f.follows_user_id = follow_user_id
def is_following_repo(self, repo_name, user_id, cache=False):
r = self.sa.query(Repository)\
.filter(Repository.repo_name == repo_name).scalar()
@@ -314,14 +306,14 @@ class ScmModel(BaseModel):
repo.pull(clone_uri)
self.mark_for_invalidation(repo_name)
def commit_change(self, repo, repo_name, cs, user, author, message, content,
f_path):
def commit_change(self, repo, repo_name, cs, user, author, message,
content, f_path):
if repo.alias == 'hg':
from vcs.backends.hg import MercurialInMemoryChangeset as IMC
elif repo.alias == 'git':
from vcs.backends.git import GitInMemoryChangeset as IMC
@@ -1787,20 +1787,24 @@ div.form div.fields div.field div.button
.trending_language_tbl,.trending_language_tbl td {
border: 0 !important;
margin: 0 !important;
padding: 0 !important;
.trending_language_tbl,.trending_language_tbl tr {
border-spacing: 1px;
.trending_language {
background-color: #003367;
color: #FFF;
display: block;
min-width: 20px;
text-decoration: none;
height: 12px;
margin-bottom: 4px;
margin-bottom: 0px;
margin-left: 5px;
white-space: pre;
padding: 3px;
h3.files_location {
file renamed from rhodecode/templates/settings/repo_fork.html to rhodecode/templates/forks/fork.html
@@ -24,20 +24,29 @@
</div>
${h.form(url('repo_fork_create_home',repo_name=c.repo_info.repo_name))}
<div class="form">
<!-- fields -->
<div class="fields">
<div class="field">
<div class="label">
<label for="repo_name">${_('Fork name')}:</label>
<div class="input">
${h.text('fork_name',class_="small")}
${h.hidden('repo_type',c.repo_info.repo_type)}
${h.text('repo_name',class_="small")}
${h.hidden('fork_parent_id',c.repo_info.repo_id)}
<label for="repo_group">${_('Repository group')}:</label>
${h.select('repo_group','',c.repo_groups,class_="medium")}
<div class="label label-textarea">
<label for="description">${_('Description')}:</label>
<div class="textarea text-area editor">
${h.textarea('description',cols=23,rows=5)}
@@ -47,13 +56,21 @@
<div class="label label-checkbox">
<label for="private">${_('Private')}:</label>
<div class="checkboxes">
${h.checkbox('private',value="True")}
<label for="private">${_('Copy permissions')}:</label>
${h.checkbox('copy_permissions',value="True")}
<div class="buttons">
${h.submit('',_('fork this repository'),class_="ui-button")}
${h.end_form()}
Status change: