diff --git a/rhodecode/config/routing.py b/rhodecode/config/routing.py --- a/rhodecode/config/routing.py +++ b/rhodecode/config/routing.py @@ -465,19 +465,20 @@ def make_map(config): 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', conditions=dict(function=check_repo)) + rmap.connect('repo_forks_home', '/{repo_name:.*}/forks', + controller='forks', action='forks', + conditions=dict(function=check_repo)) + rmap.connect('repo_followers_home', '/{repo_name:.*}/followers', controller='followers', action='followers', conditions=dict(function=check_repo)) - rmap.connect('repo_forks_home', '/{repo_name:.*}/forks', - controller='forks', action='forks', - conditions=dict(function=check_repo)) return rmap diff --git a/rhodecode/controllers/admin/repos.py b/rhodecode/controllers/admin/repos.py --- a/rhodecode/controllers/admin/repos.py +++ b/rhodecode/controllers/admin/repos.py @@ -29,9 +29,10 @@ 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, \ @@ -39,11 +40,11 @@ from rhodecode.lib.auth import LoginRequ 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 -from sqlalchemy.exc import IntegrityError log = logging.getLogger(__name__) @@ -65,7 +66,7 @@ class ReposController(BaseController): 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() @@ -127,13 +128,13 @@ class ReposController(BaseController): """ POST /repos: Create a new item""" # url('repos') - repo_model = RepoModel() + 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']), @@ -143,13 +144,13 @@ class ReposController(BaseController): category='success') 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) else: action_logger(self.rhodecode_user, 'admin_created_repo', form_result['repo_name_full'], '', self.sa) - + Session().commit() except formencode.Invalid, errors: c.new_repo = errors.value['repo_name'] @@ -207,7 +208,7 @@ class ReposController(BaseController): changed_name = repo.repo_name action_logger(self.rhodecode_user, 'admin_updated_repo', changed_name, '', self.sa) - + Session().commit() except formencode.Invalid, errors: defaults = self.__load_data(repo_name) defaults.update(errors.value) @@ -251,7 +252,7 @@ class ReposController(BaseController): repo_model.delete(repo) invalidate_cache('get_repo_cached_%s' % repo_name) h.flash(_('deleted repository %s') % repo_name, category='success') - + Session().commit() except IntegrityError, e: if e.message.find('repositories_fork_id_fkey'): log.error(traceback.format_exc()) diff --git a/rhodecode/controllers/forks.py b/rhodecode/controllers/forks.py --- a/rhodecode/controllers/forks.py +++ b/rhodecode/controllers/forks.py @@ -23,13 +23,23 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . import logging +import formencode +import traceback +from formencode import htmlfill -from pylons import tmpl_context as c, request +from pylons import tmpl_context as c, request, url +from pylons.controllers.util import redirect +from pylons.i18n.translation import _ + +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.repo import RepoModel +from rhodecode.model.forms import RepoForkForm log = logging.getLogger(__name__) @@ -37,11 +47,59 @@ log = logging.getLogger(__name__) class ForksController(BaseRepoController): @LoginRequired() - @HasRepoPermissionAnyDecorator('repository.read', 'repository.write', - 'repository.admin') def __before__(self): super(ForksController, 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) + + def __load_data(self, repo_name=None): + """ + Load defaults settings for edit, and update + + :param repo_name: + """ + self.__load_defaults() + + 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 + else: + 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 + else: + 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 + + @HasRepoPermissionAnyDecorator('repository.read', 'repository.write', + 'repository.admin') def forks(self, repo_name): p = int(request.params.get('page', 1)) repo_id = c.rhodecode_db_repo.repo_id @@ -54,3 +112,63 @@ class ForksController(BaseRepoController return c.forks_data return render('/forks/forks.html') + + @NotAnonymous() + @HasRepoPermissionAnyDecorator('repository.read', 'repository.write', + 'repository.admin') + def fork(self, repo_name): + c.repo_info = Repository.get_by_repo_name(repo_name) + if not c.repo_info: + h.flash(_('%s repository is not mapped to db perhaps' + ' it was created or renamed from the file system' + ' please run the application again' + ' in order to rescan repositories') % repo_name, + category='error') + + return redirect(url('home')) + + defaults = self.__load_data(repo_name) + + return htmlfill.render( + render('forks/fork.html'), + defaults=defaults, + encoding="UTF-8", + force_defaults=False + ) + + + @NotAnonymous() + @HasRepoPermissionAnyDecorator('repository.read', 'repository.write', + 'repository.admin') + def fork_create(self, repo_name): + self.__load_defaults() + c.repo_info = Repository.get_by_repo_name(repo_name) + _form = RepoForkForm(old_data={'repo_type': c.repo_info.repo_type}, + repo_groups=c.repo_groups_choices,)() + form_result = {} + try: + 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']), + category='success') + except formencode.Invalid, errors: + c.new_repo = errors.value['repo_name'] + + return htmlfill.render( + render('forks/fork.html'), + defaults=errors.value, + errors=errors.error_dict or {}, + prefix_error=False, + encoding="UTF-8") + except Exception: + log.error(traceback.format_exc()) + h.flash(_('An error occurred during repository forking %s') % + repo_name, category='error') + + return redirect(url('home')) diff --git a/rhodecode/controllers/journal.py b/rhodecode/controllers/journal.py --- a/rhodecode/controllers/journal.py +++ b/rhodecode/controllers/journal.py @@ -23,21 +23,22 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . import logging +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 itertools import groupby +from webhelpers.feedgenerator import Atom1Feed, Rss201rev2Feed from paste.httpexceptions import HTTPBadRequest from pylons import request, tmpl_context as c, response, url from pylons.i18n.translation import _ -from webhelpers.feedgenerator import Atom1Feed, Rss201rev2Feed import rhodecode.lib.helpers as h from rhodecode.lib.auth import LoginRequired, NotAnonymous from rhodecode.lib.base import BaseController, render from rhodecode.model.db import UserLog, UserFollowing +from rhodecode.model.meta import Session log = logging.getLogger(__name__) @@ -124,6 +125,7 @@ class JournalController(BaseController): try: self.scm_model.toggle_following_user(user_id, self.rhodecode_user.user_id) + Session().commit() return 'ok' except: raise HTTPBadRequest() @@ -133,6 +135,7 @@ class JournalController(BaseController): try: self.scm_model.toggle_following_repo(repo_id, self.rhodecode_user.user_id) + Session().commit() return 'ok' except: raise HTTPBadRequest() diff --git a/rhodecode/controllers/settings.py b/rhodecode/controllers/settings.py --- a/rhodecode/controllers/settings.py +++ b/rhodecode/controllers/settings.py @@ -35,14 +35,14 @@ from pylons.i18n.translation import _ import rhodecode.lib.helpers as h -from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAllDecorator, \ - HasRepoPermissionAnyDecorator, NotAnonymous +from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAllDecorator from rhodecode.lib.base import BaseRepoController, render 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.repo import RepoModel from rhodecode.model.db import RepoGroup +from rhodecode.model.meta import Session log = logging.getLogger(__name__) @@ -52,15 +52,15 @@ class SettingsController(BaseRepoControl @LoginRequired() def __before__(self): super(SettingsController, 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() - + @HasRepoPermissionAllDecorator('repository.admin') def index(self, repo_name): repo_model = RepoModel() @@ -89,15 +89,15 @@ class SettingsController(BaseRepoControl def update(self, repo_name): repo_model = RepoModel() changed_name = repo_name - + self.__load_defaults() - + _form = RepoSettingsForm(edit=True, old_data={'repo_name': repo_name}, repo_groups=c.repo_groups_choices)() try: form_result = _form.to_python(dict(request.POST)) - + repo_model.update(repo_name, form_result) invalidate_cache('get_repo_cached_%s' % repo_name) h.flash(_('Repository %s updated successfully' % repo_name), @@ -105,6 +105,7 @@ class SettingsController(BaseRepoControl changed_name = form_result['repo_name_full'] action_logger(self.rhodecode_user, 'user_updated_repo', changed_name, '', self.sa) + Session().commit() except formencode.Invalid, errors: c.repo_info = repo_model.get_by_repo_name(repo_name) c.users_array = repo_model.get_users_js() @@ -148,61 +149,10 @@ class SettingsController(BaseRepoControl repo_model.delete(repo) invalidate_cache('get_repo_cached_%s' % repo_name) h.flash(_('deleted repository %s') % repo_name, category='success') + Session().commit() except Exception: log.error(traceback.format_exc()) h.flash(_('An error occurred during deletion of %s') % repo_name, category='error') return redirect(url('home')) - - @NotAnonymous() - @HasRepoPermissionAnyDecorator('repository.read', 'repository.write', - 'repository.admin') - def fork(self, repo_name): - repo_model = RepoModel() - c.repo_info = repo = repo_model.get_by_repo_name(repo_name) - if not repo: - h.flash(_('%s repository is not mapped to db perhaps' - ' it was created or renamed from the file system' - ' please run the application again' - ' in order to rescan repositories') % repo_name, - category='error') - - return redirect(url('home')) - - return render('settings/repo_fork.html') - - @NotAnonymous() - @HasRepoPermissionAnyDecorator('repository.read', 'repository.write', - 'repository.admin') - def fork_create(self, repo_name): - repo_model = RepoModel() - c.repo_info = repo_model.get_by_repo_name(repo_name) - _form = RepoForkForm(old_data={'repo_type': c.repo_info.repo_type})() - form_result = {} - try: - form_result = _form.to_python(dict(request.POST)) - form_result.update({'repo_name': repo_name}) - repo_model.create_fork(form_result, self.rhodecode_user) - h.flash(_('forked %s repository as %s') \ - % (repo_name, form_result['fork_name']), - category='success') - action_logger(self.rhodecode_user, - 'user_forked_repo:%s' % form_result['fork_name'], - repo_name, '', self.sa) - except formencode.Invalid, errors: - c.new_repo = errors.value['fork_name'] - r = render('settings/repo_fork.html') - - return htmlfill.render( - r, - defaults=errors.value, - errors=errors.error_dict or {}, - prefix_error=False, - encoding="UTF-8") - except Exception: - log.error(traceback.format_exc()) - h.flash(_('An error occurred during repository forking %s') % - repo_name, category='error') - - return redirect(url('home')) diff --git a/rhodecode/lib/celerylib/tasks.py b/rhodecode/lib/celerylib/tasks.py --- a/rhodecode/lib/celerylib/tasks.py +++ b/rhodecode/lib/celerylib/tasks.py @@ -37,29 +37,28 @@ from string import lower from pylons import config, url from pylons.i18n.translation import _ +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')) @@ -81,17 +80,13 @@ 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() - from rhodecode.lib.indexers.daemon import WhooshIndexingDaemon + index_location = config['index_dir'] WhooshIndexingDaemon(index_location=index_location, repo_location=repo_location, sa=get_session())\ @@ -111,13 +106,12 @@ def get_commits_stats(repo_name, ts_min_ sa = get_session() 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: @@ -139,9 +133,9 @@ def get_commits_stats(repo_name, ts_min_ 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 lock.release() return True @@ -255,10 +249,11 @@ def get_commits_stats(repo_name, ts_min_ @task(ignore_result=True) def send_password_link(user_email): + from rhodecode.model.notification import EmailNotificationModel + log = get_logger(send_password_link) try: - from rhodecode.model.notification import EmailNotificationModel sa = get_session() user = User.get_by_email(user_email) if user: @@ -283,9 +278,9 @@ def send_password_link(user_email): @task(ignore_result=True) def reset_user_password(user_email): - log = get_logger(reset_user_password) + from rhodecode.lib import auth - from rhodecode.lib import auth + log = get_logger(reset_user_password) try: try: @@ -361,27 +356,39 @@ def send_email(recipients, subject, body @task(ignore_result=True) def create_repo_fork(form_data, cur_user): + """ + Creates a fork of repository using interval VCS methods + + :param form_data: + :param cur_user: + """ + from rhodecode.model.repo import RepoModel + log = get_logger(create_repo_fork) - from rhodecode.model.repo import RepoModel - from vcs import get_backend + 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'] - repos_path = get_repos_path() - repo_path = os.path.join(repos_path, repo_name) - repo_fork_path = os.path.join(repos_path, form_data['fork_name']) - alias = form_data['repo_type'] - - 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): - 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 + tip = repo.get_changeset() code_stats = {} diff --git a/rhodecode/lib/hooks.py b/rhodecode/lib/hooks.py --- a/rhodecode/lib/hooks.py +++ b/rhodecode/lib/hooks.py @@ -33,7 +33,8 @@ from rhodecode.lib.utils import action_l def repo_size(ui, repo, hooktype=None, **kwargs): - """Presents size of repository after push + """ + Presents size of repository after push :param ui: :param repo: @@ -65,7 +66,8 @@ def repo_size(ui, repo, hooktype=None, * def log_pull_action(ui, repo, **kwargs): - """Logs user last pull action + """ + Logs user last pull action :param ui: :param repo: @@ -76,13 +78,15 @@ def log_pull_action(ui, repo, **kwargs): 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 :param ui: :param repo: @@ -110,6 +114,7 @@ def log_push_action(ui, repo, **kwargs): action = action % ','.join(revs) - action_logger(username, action, repository, extra_params['ip']) + action_logger(username, action, repository, extra_params['ip'], + commit=True) return 0 diff --git a/rhodecode/lib/utils.py b/rhodecode/lib/utils.py --- a/rhodecode/lib/utils.py +++ b/rhodecode/lib/utils.py @@ -93,7 +93,7 @@ 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 @@ -138,12 +138,13 @@ def action_logger(user, action, repo, ip 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.commit() except: log.error(traceback.format_exc()) - sa.rollback() + raise def get_repos(path, recursive=False): diff --git a/rhodecode/model/forms.py b/rhodecode/model/forms.py --- a/rhodecode/model/forms.py +++ b/rhodecode/model/forms.py @@ -185,7 +185,7 @@ class ValidPassword(formencode.validator 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': @@ -198,7 +198,7 @@ class ValidAuth(formencode.validators.Fa 'invalid_login':_('invalid user name'), 'disabled_account':_('Your account is disabled') } - + # error mapping e_dict = {'username':messages['invalid_login'], 'password':messages['invalid_password']} @@ -208,7 +208,7 @@ class ValidAuth(formencode.validators.Fa password = value['password'] username = value['username'] user = User.get_by_username(username) - + if authenticate(username, password): return value else: @@ -254,7 +254,7 @@ def ValidRepoName(edit, old_data): # db key This is an actual just the name to store in the # database repo_name_full = group_path + RepoGroup.url_sep() + repo_name - + else: group_path = '' repo_name_full = repo_name @@ -289,24 +289,8 @@ def ValidRepoName(edit, old_data): 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')} - raise formencode.Invalid('', value, state, error_dict=e_dict) - - if RepoModel().get_by_repo_name(repo_name): - e_dict = {'fork_name':_('This repository ' - 'already exists')} - raise formencode.Invalid('', value, state, - error_dict=e_dict) - return value - return _ValidForkName +def ValidForkName(*args, **kwargs): + return ValidRepoName(*args, **kwargs) def SlugifyName(): @@ -513,7 +497,7 @@ def UserForm(edit=False, old_data={}): else: password = All(UnicodeString(strip=True, min=6, not_empty=True)) password_confirmation = All(UnicodeString(strip=True, min=6, not_empty=False)) - + active = StringBoolean(if_missing=False) name = UnicodeString(strip=True, min=1, not_empty=True) lastname = UnicodeString(strip=True, min=1, not_empty=True) @@ -605,17 +589,20 @@ def RepoForm(edit=False, old_data={}, su 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) - repo_type = All(ValidForkType(old_data), OneOf(supported_backends)) - - chained_validators = [ValidForkName()] + copy_permissions = StringBoolean(if_missing=False) + fork_parent_id = UnicodeString() + chained_validators = [ValidForkName(edit, old_data)] return _RepoForkForm @@ -630,7 +617,7 @@ def RepoSettingsForm(edit=False, old_dat repo_group = OneOf(repo_groups, hideList=True) private = StringBoolean(if_missing=False) - chained_validators = [ValidRepoName(edit, old_data), ValidPerms, + chained_validators = [ValidRepoName(edit, old_data), ValidPerms, ValidSettings] return _RepoForm diff --git a/rhodecode/model/notification.py b/rhodecode/model/notification.py --- a/rhodecode/model/notification.py +++ b/rhodecode/model/notification.py @@ -35,8 +35,6 @@ from pylons.i18n.translation import _ from rhodecode.lib import helpers as h 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 log = logging.getLogger(__name__) @@ -74,6 +72,7 @@ class NotificationModel(BaseModel): :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') @@ -100,7 +99,7 @@ class NotificationModel(BaseModel): 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 diff --git a/rhodecode/model/repo.py b/rhodecode/model/repo.py --- a/rhodecode/model/repo.py +++ b/rhodecode/model/repo.py @@ -212,36 +212,33 @@ class RepoModel(BaseModel): raise def create(self, form_data, cur_user, just_db=False, fork=False): + from rhodecode.model.scm import ScmModel try: 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'] - else: - 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 + repo_name = form_data['repo_name'] + repo_name_full = form_data['repo_name_full'] new_repo = Repository() new_repo.enable_statistics = False + for k, v in form_data.items(): if k == 'repo_name': - if fork: - v = repo_name - else: - v = repo_name_full + v = repo_name_full if k == 'repo_group': k = 'group_id' - if k == 'description': v = v or repo_name setattr(new_repo, k, v) if fork: - 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 @@ -271,19 +268,21 @@ class RepoModel(BaseModel): form_data['repo_group'], form_data['clone_uri']) - self.sa.commit() - - #now automatically start following this repository as owner - from rhodecode.model.scm import ScmModel + # now automatically start following this repository as owner ScmModel(self.sa).toggle_following_repo(new_repo.repo_id, - cur_user.user_id) + cur_user.user_id) return new_repo except: log.error(traceback.format_exc()) - self.sa.rollback() raise def create_fork(self, form_data, cur_user): + """ + Simple wrapper into executing celery task for fork creation + + :param form_data: + :param cur_user: + """ from rhodecode.lib.celerylib import tasks, run_task run_task(tasks.create_repo_fork, form_data, cur_user) @@ -325,6 +324,11 @@ class RepoModel(BaseModel): raise def delete_stats(self, repo_name): + """ + removes stats for given repo + + :param repo_name: + """ try: obj = self.sa.query(Statistics)\ .filter(Statistics.repository == \ diff --git a/rhodecode/model/scm.py b/rhodecode/model/scm.py --- a/rhodecode/model/scm.py +++ b/rhodecode/model/scm.py @@ -208,17 +208,14 @@ class ScmModel(BaseModel): .filter(UserFollowing.user_id == user_id).scalar() if f is not None: - try: self.sa.delete(f) - self.sa.commit() action_logger(UserTemp(user_id), 'stopped_following_repo', RepoTemp(follow_repo_id)) return except: log.error(traceback.format_exc()) - self.sa.rollback() raise try: @@ -226,13 +223,12 @@ class ScmModel(BaseModel): f.user_id = user_id f.follows_repo_id = follow_repo_id self.sa.add(f) - self.sa.commit() + action_logger(UserTemp(user_id), 'started_following_repo', RepoTemp(follow_repo_id)) except: log.error(traceback.format_exc()) - self.sa.rollback() raise def toggle_following_user(self, follow_user_id, user_id): @@ -243,11 +239,9 @@ class ScmModel(BaseModel): if f is not None: try: self.sa.delete(f) - self.sa.commit() return except: log.error(traceback.format_exc()) - self.sa.rollback() raise try: @@ -255,10 +249,8 @@ class ScmModel(BaseModel): f.user_id = user_id f.follows_user_id = follow_user_id self.sa.add(f) - self.sa.commit() except: log.error(traceback.format_exc()) - self.sa.rollback() raise def is_following_repo(self, repo_name, user_id, cache=False): @@ -317,8 +309,8 @@ class ScmModel(BaseModel): log.error(traceback.format_exc()) raise - 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 diff --git a/rhodecode/public/css/style.css b/rhodecode/public/css/style.css --- a/rhodecode/public/css/style.css +++ b/rhodecode/public/css/style.css @@ -1790,6 +1790,10 @@ div.form div.fields div.field div.button padding: 0 !important; } +.trending_language_tbl,.trending_language_tbl tr { + border-spacing: 1px; +} + .trending_language { background-color: #003367; color: #FFF; @@ -1797,7 +1801,7 @@ div.form div.fields div.field div.button min-width: 20px; text-decoration: none; height: 12px; - margin-bottom: 4px; + margin-bottom: 0px; margin-left: 5px; white-space: pre; padding: 3px; diff --git a/rhodecode/templates/settings/repo_fork.html b/rhodecode/templates/forks/fork.html rename from rhodecode/templates/settings/repo_fork.html rename to rhodecode/templates/forks/fork.html --- a/rhodecode/templates/settings/repo_fork.html +++ b/rhodecode/templates/forks/fork.html @@ -27,14 +27,23 @@
-
- -
-
- ${h.text('fork_name',class_="small")} - ${h.hidden('repo_type',c.repo_info.repo_type)} -
-
+
+ +
+
+ ${h.text('repo_name',class_="small")} + ${h.hidden('repo_type',c.repo_info.repo_type)} + ${h.hidden('fork_parent_id',c.repo_info.repo_id)} +
+
+
+
+ +
+
+ ${h.select('repo_group','',c.repo_groups,class_="medium")} +
+
@@ -50,7 +59,15 @@
${h.checkbox('private',value="True")}
-
+
+
+
+ +
+
+ ${h.checkbox('copy_permissions',value="True")} +
+
${h.submit('',_('fork this repository'),class_="ui-button")}