Mercurial > kallithea
changeset 2825:f7a52d548fd0
merge with beta
author | Marcin Kuzminski <marcin@python-works.com> |
---|---|
date | Fri, 07 Sep 2012 23:20:33 +0200 |
parents | 4c13cedbde93 (current diff) 92822bd2e88a (diff) |
children | 909143a4dde5 |
files | docs/changelog.rst docs/installation.rst docs/setup.rst docs/upgrade.rst rhodecode/__init__.py rhodecode/config/routing.py rhodecode/controllers/admin/settings.py rhodecode/controllers/changeset.py rhodecode/lib/celerylib/tasks.py rhodecode/lib/db_manage.py rhodecode/lib/hooks.py rhodecode/lib/utils.py rhodecode/model/db.py rhodecode/model/forms.py rhodecode/model/permission.py rhodecode/model/repo.py rhodecode/model/user.py rhodecode/templates/admin/settings/settings.html rhodecode/templates/admin/users/user_edit.html rhodecode/templates/summary/summary.html rhodecode/tests/functional/test_summary.py |
diffstat | 56 files changed, 1226 insertions(+), 221 deletions(-) [+] |
line wrap: on
line diff
--- a/docs/changelog.rst Mon Sep 03 22:22:58 2012 +0200 +++ b/docs/changelog.rst Fri Sep 07 23:20:33 2012 +0200 @@ -4,6 +4,45 @@ Changelog ========= + +1.4.1 (**2012-09-07**) +---------------------- + +:status: in-progress +:branch: beta + +news +++++ + +- always put a comment about code-review status change even if user send + empty data +- modified_on column saves repository update and it's going to be used + later for light version of main page ref #500 +- pull request notifications send much nicer emails with details about pull + request +- #551 show breadcrumbs in summary view for repositories inside a group + +fixes ++++++ + +- fixed migrations of permissions that can lead to inconsistency. + Some users sent feedback that after upgrading from older versions issues + with updating default permissions occurred. RhodeCode detects that now and + resets default user permission to initial state if there is a need for that. + Also forces users to set the default value for new forking permission. +- #535 improved apache wsgi example configuration in docs +- fixes #550 mercurial repositories comparision failed when origin repo had + additional not-common changesets +- fixed status of code-review in preview windows of pull request +- git forks were not initialized at bare repos +- fixes #555 fixes issues with comparing non-related repositories +- fixes #557 follower counter always counts up +- fixed issue #560 require push ssl checkbox wasn't shown when option was + enabled +- fixed #559 +- fixed issue #559 fixed bug in routing that mapped repo names with <name>_<num> in name as + if it was a request to url by repository ID + 1.4.0 (**2012-09-03**) ----------------------
--- a/docs/installation.rst Mon Sep 03 22:22:58 2012 +0200 +++ b/docs/installation.rst Fri Sep 07 23:20:33 2012 +0200 @@ -11,8 +11,8 @@ **1.6**. If you're using older client, please upgrade. -Installing RhodeCode from Cheese Shop -------------------------------------- +Installing RhodeCode from PyPI (aka "Cheeseshop") +------------------------------------------------- Rhodecode requires python version 2.5 or higher. @@ -126,4 +126,4 @@ .. _python: http://www.python.org/ .. _mercurial: http://mercurial.selenic.com/ .. _celery: http://celeryproject.org/ -.. _rabbitmq: http://www.rabbitmq.com/ \ No newline at end of file +.. _rabbitmq: http://www.rabbitmq.com/
--- a/docs/setup.rst Mon Sep 03 22:22:58 2012 +0200 +++ b/docs/setup.rst Fri Sep 07 23:20:33 2012 +0200 @@ -667,12 +667,21 @@ Here is a sample excerpt from an Apache Virtual Host configuration file:: - WSGIDaemonProcess pylons user=www-data group=www-data processes=1 \ + WSGIDaemonProcess pylons \ threads=4 \ python-path=/home/web/rhodecode/pyenv/lib/python2.6/site-packages WSGIScriptAlias / /home/web/rhodecode/dispatch.wsgi WSGIPassAuthorization On +.. note:: + when running apache as root please add: `user=www-data group=www-data` + into above configuration + +.. note:: + RhodeCode cannot be runned in multiprocess mode in apache, make sure + you don't specify `processes=num` directive in the config + + Example wsgi dispatch script:: import os
--- a/docs/upgrade.rst Mon Sep 03 22:22:58 2012 +0200 +++ b/docs/upgrade.rst Fri Sep 07 23:20:33 2012 +0200 @@ -4,14 +4,43 @@ Upgrade ======= -Upgrading from Cheese Shop --------------------------- +Upgrading from PyPI (aka "Cheeseshop") +--------------------------------------- .. note:: - Firstly, it is recommended that you **always** perform a database backup - before doing an upgrade. + Firstly, it is recommended that you **always** perform a database and + configuration backup before doing an upgrade. + + (These directions will use '{version}' to note that this is the version of + Rhodecode that these files were used with. If backing up your RhodeCode + instance from version 1.3.6 to 1.4.0, the ``production.ini`` file would be + backed up to ``production.ini.1-3-6``.) + + +If using a sqlite database, stop the Rhodecode process/daemon/service, and +then make a copy of the database file:: + + service rhodecode stop + cp rhodecode.db rhodecode.db.{version} + -The easiest way to upgrade ``rhodecode`` is to run:: +Back up your configuration file:: + + cp production.ini production.ini.{version} + + +Ensure that you are using the Python Virtual Environment that you'd originally +installed Rhodecode in:: + + pip freeze + +will list all packages installed in the current environment. If Rhodecode +isn't listed, change virtual environments to your venv location:: + + source /opt/rhodecode-venv/bin/activate + + +Once you have verified the environment you can upgrade ``Rhodecode`` with:: easy_install -U rhodecode @@ -20,14 +49,13 @@ pip install --upgrade rhodecode -Then make sure you run the following command from the installation directory:: +Then run the following command from the installation directory:: paster make-config RhodeCode production.ini This will display any changes made by the new version of RhodeCode to your -current configuration. It will try to perform an automerge. It's always better -to make a backup of your configuration file before hand and re check the -content after the automerge. +current configuration. It will try to perform an automerge. It's recommended +that you re-check the content after the automerge. .. note:: Please always make sure your .ini files are up to date. Often errors are @@ -41,12 +69,25 @@ The final step is to upgrade the database. To do this simply run:: - paster upgrade-db production.ini + paster upgrade-db production.ini This will upgrade the schema and update some of the defaults in the database, and will always recheck the settings of the application, if there are no new options that need to be set. +You may find it helpful to clear out your log file so that new errors are +readily apparent:: + + echo > rhodecode.log + +Once that is complete, you may now start your upgraded Rhodecode Instance:: + + service rhodecode start + +Or:: + + paster serve /var/www/rhodecode/production.ini + .. note:: If you're using Celery, make sure you restart all instances of it after upgrade. @@ -55,4 +96,4 @@ .. _python: http://www.python.org/ .. _mercurial: http://mercurial.selenic.com/ .. _celery: http://celeryproject.org/ -.. _rabbitmq: http://www.rabbitmq.com/ \ No newline at end of file +.. _rabbitmq: http://www.rabbitmq.com/
--- a/rhodecode/__init__.py Mon Sep 03 22:22:58 2012 +0200 +++ b/rhodecode/__init__.py Fri Sep 07 23:20:33 2012 +0200 @@ -26,7 +26,7 @@ import sys import platform -VERSION = (1, 4, 0) +VERSION = (1, 4, 1) try: from rhodecode.lib import get_current_revision @@ -38,7 +38,7 @@ __version__ = ('.'.join((str(each) for each in VERSION[:3])) + '.'.join(VERSION[3:])) -__dbversion__ = 6 # defines current db version for migrations +__dbversion__ = 7 # defines current db version for migrations __platform__ = platform.system() __license__ = 'GPLv3' __py_version__ = sys.version_info
--- a/rhodecode/config/routing.py Mon Sep 03 22:22:58 2012 +0200 +++ b/rhodecode/config/routing.py Fri Sep 07 23:20:33 2012 +0200 @@ -34,7 +34,7 @@ try: by_id = repo_name.split('_') - if len(by_id) == 2 and by_id[1].isdigit(): + if len(by_id) == 2 and by_id[1].isdigit() and by_id[0] == '': repo_name = Repository.get(by_id[1]).repo_name match_dict['repo_name'] = repo_name except:
--- a/rhodecode/controllers/admin/repos_groups.py Mon Sep 03 22:22:58 2012 +0200 +++ b/rhodecode/controllers/admin/repos_groups.py Fri Sep 07 23:20:33 2012 +0200 @@ -45,6 +45,7 @@ from rhodecode.model.meta import Session from rhodecode.model.repo import RepoModel from webob.exc import HTTPInternalServerError, HTTPNotFound +from rhodecode.lib.utils2 import str2bool log = logging.getLogger(__name__) @@ -162,7 +163,7 @@ Session().commit() h.flash(_('updated repos group %s') \ % form_result['group_name'], category='success') - #TODO: in futureaction_logger(, '', '', '', self.sa) + #TODO: in future action_logger(, '', '', '', self.sa) except formencode.Invalid, errors: return htmlfill.render( @@ -227,10 +228,11 @@ :param group_name: """ - try: - ReposGroupModel().revoke_user_permission( - repos_group=group_name, user=request.POST['user_id'] + recursive = str2bool(request.POST.get('recursive', False)) + ReposGroupModel().delete_permission( + repos_group=group_name, obj=request.POST['user_id'], + obj_type='user', recursive=recursive ) Session().commit() except Exception: @@ -248,9 +250,10 @@ """ try: - ReposGroupModel().revoke_users_group_permission( - repos_group=group_name, - group_name=request.POST['users_group_id'] + recursive = str2bool(request.POST.get('recursive', False)) + ReposGroupModel().delete_permission( + repos_group=group_name, obj=request.POST['users_group_id'], + obj_type='users_group', recursive=recursive ) Session().commit() except Exception:
--- a/rhodecode/controllers/admin/settings.py Mon Sep 03 22:22:58 2012 +0200 +++ b/rhodecode/controllers/admin/settings.py Fri Sep 07 23:20:33 2012 +0200 @@ -51,6 +51,7 @@ from rhodecode.model.db import User from rhodecode.model.notification import EmailNotificationModel from rhodecode.model.meta import Session +from rhodecode.lib.utils2 import str2bool log = logging.getLogger(__name__) @@ -471,6 +472,9 @@ if k == '/': k = 'root_path' + if k == 'push_ssl': + v = str2bool(v) + if k.find('.') != -1: k = k.replace('.', '_') @@ -478,5 +482,4 @@ v = each.ui_active settings[each.ui_section + '_' + k] = v - return settings
--- a/rhodecode/controllers/changeset.py Mon Sep 03 22:22:58 2012 +0200 +++ b/rhodecode/controllers/changeset.py Fri Sep 07 23:20:33 2012 +0200 @@ -376,9 +376,13 @@ def comment(self, repo_name, revision): status = request.POST.get('changeset_status') change_status = request.POST.get('change_changeset_status') + text = request.POST.get('text') + if status and change_status: + text = text or (_('Status change -> %s') + % ChangesetStatus.get_status_lbl(status)) comm = ChangesetCommentsModel().create( - text=request.POST.get('text'), + text=text, repo=c.rhodecode_db_repo.repo_id, user=c.rhodecode_user.user_id, revision=revision, @@ -391,7 +395,7 @@ # get status if set ! if status and change_status: # if latest status was from pull request and it's closed - # disallow changing status ! + # disallow changing status ! # dont_allow_on_closed_pull_request = True ! try:
--- a/rhodecode/controllers/pullrequests.py Mon Sep 03 22:22:58 2012 +0200 +++ b/rhodecode/controllers/pullrequests.py Fri Sep 07 23:20:33 2012 +0200 @@ -249,8 +249,7 @@ org_repo, org_ref, other_repo, other_ref ) - c.statuses = c.rhodecode_db_repo.statuses([x.raw_id for x in - c.cs_ranges]) + c.statuses = org_repo.statuses([x.raw_id for x in c.cs_ranges]) # defines that we need hidden inputs with changesets c.as_form = request.GET.get('as_form', False) @@ -277,6 +276,7 @@ c.users_array = repo_model.get_users_js() c.users_groups_array = repo_model.get_users_groups_js() c.pull_request = PullRequest.get_or_404(pull_request_id) + c.target_repo = c.pull_request.org_repo.repo_name cc_model = ChangesetCommentsModel() cs_model = ChangesetStatusModel() @@ -322,12 +322,20 @@ c.comments = cc_model.get_comments(c.rhodecode_db_repo.repo_id, pull_request=pull_request_id) - # changeset(pull-request) status - c.current_changeset_status = cs_model.calculate_status( - c.pull_request_reviewers - ) + try: + cur_status = c.statuses[c.pull_request.revisions[0]][0] + except: + log.error(traceback.format_exc()) + cur_status = 'undefined' + if c.pull_request.is_closed() and 0: + c.current_changeset_status = cur_status + else: + # changeset(pull-request) status calulation based on reviewers + c.current_changeset_status = cs_model.calculate_status( + c.pull_request_reviewers, + ) c.changeset_statuses = ChangesetStatus.STATUSES - c.target_repo = c.pull_request.org_repo.repo_name + return render('/pullrequests/pullrequest_show.html') @NotAnonymous() @@ -339,9 +347,12 @@ status = request.POST.get('changeset_status') change_status = request.POST.get('change_changeset_status') - + text = request.POST.get('text') + if status and change_status: + text = text or (_('Status change -> %s') + % ChangesetStatus.get_status_lbl(status)) comm = ChangesetCommentsModel().create( - text=request.POST.get('text'), + text=text, repo=c.rhodecode_db_repo.repo_id, user=c.rhodecode_user.user_id, pull_request=pull_request_id,
--- a/rhodecode/lib/celerylib/tasks.py Mon Sep 03 22:22:58 2012 +0200 +++ b/rhodecode/lib/celerylib/tasks.py Fri Sep 07 23:20:33 2012 +0200 @@ -400,9 +400,19 @@ log.info('creating fork of %s as %s', source_repo_path, destination_fork_path) backend = get_backend(repo_type) - backend(safe_str(destination_fork_path), create=True, - src_url=safe_str(source_repo_path), - update_after_clone=update_after_clone) + + if repo_type == 'git': + backend(safe_str(destination_fork_path), create=True, + src_url=safe_str(source_repo_path), + update_after_clone=update_after_clone, + bare=True) + elif repo_type == 'hg': + backend(safe_str(destination_fork_path), create=True, + src_url=safe_str(source_repo_path), + update_after_clone=update_after_clone) + else: + raise Exception('Unknown backend type %s' % repo_type) + log_create_repository(fork_repo.get_dict(), created_by=cur_user.username) action_logger(cur_user, 'user_forked_repo:%s' % fork_name,
--- a/rhodecode/lib/compat.py Mon Sep 03 22:22:58 2012 +0200 +++ b/rhodecode/lib/compat.py Fri Sep 07 23:20:33 2012 +0200 @@ -589,6 +589,3 @@ self.__cond.wait(timeout) finally: self.__cond.release() - - -
--- a/rhodecode/lib/db_manage.py Mon Sep 03 22:22:58 2012 +0200 +++ b/rhodecode/lib/db_manage.py Fri Sep 07 23:20:33 2012 +0200 @@ -247,7 +247,22 @@ Session().add(hggit) notify('re-check default permissions') - self.klass.populate_default_permissions() + default_user = User.get_by_username(User.DEFAULT_USER) + perm = Permission.get_by_key('hg.fork.repository') + reg_perm = UserToPerm() + reg_perm.user = default_user + reg_perm.permission = perm + Session().add(reg_perm) + + def step_7(self): + perm_fixes = self.klass.reset_permissions(User.DEFAULT_USER) + Session().commit() + if perm_fixes: + notify('There was an inconsistent state of permissions ' + 'detected for default user. Permissions are now ' + 'reset to the default value for default user. ' + 'Please validate and check default permissions ' + 'in admin panel') upgrade_steps = [0] + range(curr_version + 1, __dbversion__ + 1) @@ -470,6 +485,28 @@ log.debug('missing default permission for group %s adding' % g) ReposGroupModel()._create_default_perms(g) + def reset_permissions(self, username): + """ + Resets permissions to default state, usefull when old systems had + bad permissions, we must clean them up + + :param username: + :type username: + """ + default_user = User.get_by_username(username) + if not default_user: + return + + u2p = UserToPerm.query()\ + .filter(UserToPerm.user == default_user).all() + fixed = False + if len(u2p) != len(User.DEFAULT_PERMISSIONS): + for p in u2p: + Session().delete(p) + fixed = True + self.populate_default_permissions() + return fixed + def config_prompt(self, test_repo_path='', retries=3, defaults={}): _path = defaults.get('repos_location') if retries == 3: @@ -506,7 +543,15 @@ retries -= 1 return self.config_prompt(test_repo_path, retries) - return path + real_path = os.path.realpath(path) + + if real_path != path: + if not ask_ok(('Path looks like a symlink, Rhodecode will store ' + 'given path as %s ? [y/n]') % (real_path)): + log.error('Canceled by user') + sys.exit(-1) + + return real_path def create_settings(self, path): @@ -597,8 +642,7 @@ default_user = User.get_by_username('default') - for def_perm in ['hg.register.manual_activate', 'hg.create.repository', - 'hg.fork.repository', 'repository.read']: + for def_perm in User.DEFAULT_PERMISSIONS: perm = self.sa.query(Permission)\ .filter(Permission.permission_name == def_perm)\
--- a/rhodecode/lib/dbmigrate/schema/db_1_3_0.py Mon Sep 03 22:22:58 2012 +0200 +++ b/rhodecode/lib/dbmigrate/schema/db_1_3_0.py Fri Sep 07 23:20:33 2012 +0200 @@ -1317,4 +1317,4 @@ org_repo_id = Column('org_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False) org_ref = Column('org_ref', Unicode(256), nullable=False) other_repo_id = Column('other_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False) - other_ref = Column('other_ref', Unicode(256), nullable=False) \ No newline at end of file + other_ref = Column('other_ref', Unicode(256), nullable=False)
--- a/rhodecode/lib/dbmigrate/versions/006_version_1_4_0.py Mon Sep 03 22:22:58 2012 +0200 +++ b/rhodecode/lib/dbmigrate/versions/006_version_1_4_0.py Fri Sep 07 23:20:33 2012 +0200 @@ -49,7 +49,7 @@ tbl = ChangesetStatus.__table__ tbl.create() - ## RESET COMPLETLY THE metadata for sqlalchemy to use the 1_3_0 Base + ## RESET COMPLETLY THE metadata for sqlalchemy to use the 1_3_0 Base Base = declarative_base() Base.metadata.clear() Base.metadata = MetaData()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rhodecode/lib/dbmigrate/versions/007_version_1_4_0.py Fri Sep 07 23:20:33 2012 +0200 @@ -0,0 +1,51 @@ +import logging +import datetime + +from sqlalchemy import * +from sqlalchemy.exc import DatabaseError +from sqlalchemy.orm import relation, backref, class_mapper +from sqlalchemy.orm.session import Session +from sqlalchemy.ext.declarative import declarative_base + +from rhodecode.lib.dbmigrate.migrate import * +from rhodecode.lib.dbmigrate.migrate.changeset import * + +from rhodecode.model.meta import Base +from rhodecode.model import meta + +log = logging.getLogger(__name__) + + +def upgrade(migrate_engine): + """ + Upgrade operations go here. + Don't create your own engine; bind migrate_engine to your metadata + """ + + #========================================================================== + # CHANGESET_COMMENTS + #========================================================================== + from rhodecode.lib.dbmigrate.schema.db_1_4_0 import ChangesetComment + tbl_name = ChangesetComment.__tablename__ + tbl = Table(tbl_name, + MetaData(bind=migrate_engine), autoload=True, + autoload_with=migrate_engine) + col = tbl.columns.revision + + # remove nullability from revision field + col.alter(nullable=True) + + #========================================================================== + # REPOSITORY + #========================================================================== + from rhodecode.lib.dbmigrate.schema.db_1_4_0 import Repository + tbl = Repository.__table__ + updated_on = Column('updated_on', DateTime(timezone=False), + nullable=True, unique=None) + # create created on column for future lightweight main page + updated_on.create(table=tbl) + + +def downgrade(migrate_engine): + meta = MetaData() + meta.bind = migrate_engine
--- a/rhodecode/lib/diffs.py Mon Sep 03 22:22:58 2012 +0200 +++ b/rhodecode/lib/diffs.py Fri Sep 07 23:20:33 2012 +0200 @@ -610,7 +610,7 @@ other_repo.ui.setconfig('hooks', k, None) unbundle = other_repo.getbundle('incoming', common=common, - heads=rheads) + heads=None) buf = BytesIO() while True:
--- a/rhodecode/lib/ext_json.py Mon Sep 03 22:22:58 2012 +0200 +++ b/rhodecode/lib/ext_json.py Fri Sep 07 23:20:33 2012 +0200 @@ -92,7 +92,7 @@ return _obj_dump(obj) except NotImplementedError: pass - return json.JSONEncoder.default(self, obj) + raise TypeError("%r is not JSON serializable" % (obj,)) # monkey-patch JSON encoder to use extended version json.dumps = functools.partial(json.dumps, cls=ExtendedEncoder) json.dump = functools.partial(json.dump, cls=ExtendedEncoder)
--- a/rhodecode/lib/hooks.py Mon Sep 03 22:22:58 2012 +0200 +++ b/rhodecode/lib/hooks.py Fri Sep 07 23:20:33 2012 +0200 @@ -34,10 +34,9 @@ from rhodecode.lib.utils import action_logger from rhodecode.lib.vcs.backends.base import EmptyChangeset from rhodecode.lib.compat import json -from rhodecode.model.db import Repository, User +from rhodecode.lib.exceptions import HTTPLockedRC from rhodecode.lib.utils2 import safe_str -from rhodecode.lib.exceptions import HTTPLockedRC - +from rhodecode.model.db import Repository, User def _get_scm_size(alias, root_path): @@ -330,7 +329,12 @@ # fix if it's not a bare repo if repo_path.endswith('.git'): repo_path = repo_path[:-4] + repo = Repository.get_by_full_path(repo_path) + if not repo: + raise OSError('Repository %s not found in database' + % (safe_str(repo_path))) + _hooks = dict(baseui.configitems('hooks')) or {} extras = json.loads(env['RHODECODE_EXTRAS'])
--- a/rhodecode/lib/utils.py Mon Sep 03 22:22:58 2012 +0200 +++ b/rhodecode/lib/utils.py Fri Sep 07 23:20:33 2012 +0200 @@ -206,7 +206,7 @@ def is_valid_repo(repo_name, base_path, scm=None): """ Returns True if given path is a valid repository False otherwise. - If scm param is given also compare if given scm is the same as expected + If scm param is given also compare if given scm is the same as expected from scm parameter :param repo_name: @@ -413,6 +413,11 @@ raise Exception('Missing administrative account !') added = [] +# # clear cache keys +# log.debug("Clearing cache keys now...") +# CacheInvalidation.clear_cache() +# sa.commit() + for name, repo in initial_repo_list.items(): group = map_groups(name) db_repo = rm.get_by_repo_name(name) @@ -438,6 +443,11 @@ elif install_git_hook: if db_repo.repo_type == 'git': ScmModel().install_git_hook(db_repo.scm_instance) + # during starting install all cache keys for all repositories in the + # system, this will register all repos and multiple instances + key, _prefix, _org_key = CacheInvalidation._get_key(name) + log.debug("Creating cache key for %s instance_id:`%s`" % (name, _prefix)) + CacheInvalidation._get_or_create_key(key, _prefix, _org_key, commit=False) sa.commit() removed = [] if remove_obsolete: @@ -455,10 +465,6 @@ log.error(traceback.format_exc()) sa.rollback() - # clear cache keys - log.debug("Clearing cache keys now...") - CacheInvalidation.clear_cache() - sa.commit() return added, removed
--- a/rhodecode/lib/vcs/backends/git/repository.py Mon Sep 03 22:22:58 2012 +0200 +++ b/rhodecode/lib/vcs/backends/git/repository.py Fri Sep 07 23:20:33 2012 +0200 @@ -178,7 +178,7 @@ raise urllib2.URLError("[%s] %s" % (url, e)) def _get_repo(self, create, src_url=None, update_after_clone=False, - bare=False): + bare=False): if create and os.path.exists(self.path): raise RepositoryError("Location already exist") if src_url and not create:
--- a/rhodecode/lib/vcs/utils/hgcompat.py Mon Sep 03 22:22:58 2012 +0200 +++ b/rhodecode/lib/vcs/utils/hgcompat.py Fri Sep 07 23:20:33 2012 +0200 @@ -14,4 +14,5 @@ from mercurial.encoding import tolocal from mercurial import discovery from mercurial import localrepo -from mercurial import scmutil \ No newline at end of file +from mercurial import scmutil +from mercurial.discovery import findcommonoutgoing
--- a/rhodecode/model/changeset_status.py Mon Sep 03 22:22:58 2012 +0200 +++ b/rhodecode/model/changeset_status.py Fri Sep 07 23:20:33 2012 +0200 @@ -64,7 +64,7 @@ def calculate_status(self, statuses_by_reviewers): """ - leading one wins, if number of occurences are equal than weaker wins + leading one wins, if number of occurrences are equal than weaker wins :param statuses_by_reviewers: """
--- a/rhodecode/model/comment.py Mon Sep 03 22:22:58 2012 +0200 +++ b/rhodecode/model/comment.py Fri Sep 07 23:20:33 2012 +0200 @@ -123,18 +123,20 @@ recipients = ChangesetComment.get_users(revision=revision) # add changeset author if it's in rhodecode system recipients += [User.get_by_email(author_email)] + email_kwargs = { + 'status_change': status_change, + } #pull request elif pull_request: + _url = h.url('pullrequest_show', + repo_name=pull_request.other_repo.repo_name, + pull_request_id=pull_request.pull_request_id, + anchor='comment-%s' % comment.comment_id, + qualified=True, + ) subj = safe_unicode( h.link_to('Re pull request: %(desc)s %(line)s' % \ - {'desc': desc, 'line': line}, - h.url('pullrequest_show', - repo_name=pull_request.other_repo.repo_name, - pull_request_id=pull_request.pull_request_id, - anchor='comment-%s' % comment.comment_id, - qualified=True, - ) - ) + {'desc': desc, 'line': line}, _url) ) notification_type = Notification.TYPE_PULL_REQUEST_COMMENT @@ -144,22 +146,36 @@ # add pull request author recipients += [pull_request.author] + # add the reviewers to notification + recipients += [x.user for x in pull_request.reviewers] + + #set some variables for email notification + email_kwargs = { + 'pr_id': pull_request.pull_request_id, + 'status_change': status_change, + 'pr_comment_url': _url, + 'pr_comment_user': h.person(user.email), + 'pr_target_repo': h.url('summary_home', + repo_name=pull_request.other_repo.repo_name, + qualified=True) + } # create notification objects, and emails NotificationModel().create( - created_by=user, subject=subj, body=body, - recipients=recipients, type_=notification_type, - email_kwargs={'status_change': status_change} + created_by=user, subject=subj, body=body, + recipients=recipients, type_=notification_type, + email_kwargs=email_kwargs ) mention_recipients = set(self._extract_mentions(body))\ .difference(recipients) if mention_recipients: + email_kwargs.update({'pr_mention': True}) subj = _('[Mention]') + ' ' + subj NotificationModel().create( created_by=user, subject=subj, body=body, recipients=mention_recipients, type_=notification_type, - email_kwargs={'status_change': status_change} + email_kwargs=email_kwargs ) return comment
--- a/rhodecode/model/db.py Mon Sep 03 22:22:58 2012 +0200 +++ b/rhodecode/model/db.py Fri Sep 07 23:20:33 2012 +0200 @@ -278,6 +278,10 @@ Session().add(new_ui) + def __repr__(self): + return '<DB:%s[%s:%s]>' % (self.__class__.__name__, self.ui_key, + self.ui_value) + class User(Base, BaseModel): __tablename__ = 'users' @@ -289,7 +293,10 @@ 'mysql_charset': 'utf8'} ) DEFAULT_USER = 'default' - + DEFAULT_PERMISSIONS = [ + 'hg.register.manual_activate', 'hg.create.repository', + 'hg.fork.repository', 'repository.read' + ] user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True) username = Column("username", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None) password = Column("password", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None) @@ -602,6 +609,7 @@ enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True) description = Column("description", String(10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None) created_on = Column('created_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now) + updated_on = Column('updated_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now) landing_rev = Column("landing_revision", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default=None) enable_locking = Column("enable_locking", Boolean(), nullable=False, unique=None, default=False) _locked = Column("locked", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None) @@ -742,6 +750,16 @@ p += self.repo_name.split(Repository.url_sep()) return os.path.join(*p) + @property + def cache_keys(self): + """ + Returns associated cache keys for that repo + """ + return CacheInvalidation.query()\ + .filter(CacheInvalidation.cache_args == self.repo_name)\ + .order_by(CacheInvalidation.cache_key)\ + .all() + def get_new_name(self, repo_name): """ returns new full repository name based on assigned group and new new @@ -1398,6 +1416,13 @@ return u"<%s('%s:%s')>" % (self.__class__.__name__, self.cache_id, self.cache_key) + @property + def prefix(self): + _split = self.cache_key.split(self.cache_args, 1) + if _split and len(_split) == 2: + return _split[0] + return '' + @classmethod def clear_cache(cls): cls.query().delete() @@ -1421,13 +1446,14 @@ return cls.query().filter(cls.cache_key == key).scalar() @classmethod - def _get_or_create_key(cls, key, prefix, org_key): + def _get_or_create_key(cls, key, prefix, org_key, commit=True): inv_obj = Session().query(cls).filter(cls.cache_key == key).scalar() if not inv_obj: try: inv_obj = CacheInvalidation(key, org_key) Session().add(inv_obj) - Session().commit() + if commit: + Session().commit() except Exception: log.error(traceback.format_exc()) Session().rollback()
--- a/rhodecode/model/forms.py Mon Sep 03 22:22:58 2012 +0200 +++ b/rhodecode/model/forms.py Fri Sep 07 23:20:33 2012 +0200 @@ -128,6 +128,7 @@ testValueList=True, if_missing=None, not_empty=False) enable_locking = v.StringBoolean(if_missing=False) + recursive = v.StringBoolean(if_missing=False) chained_validators = [v.ValidReposGroup(edit, old_data), v.ValidPerms('group')] @@ -340,4 +341,4 @@ pullrequest_title = v.UnicodeString(strip=True, required=True, min=3) pullrequest_desc = v.UnicodeString(strip=True, required=False) - return _PullRequestForm \ No newline at end of file + return _PullRequestForm
--- a/rhodecode/model/notification.py Mon Sep 03 22:22:58 2012 +0200 +++ b/rhodecode/model/notification.py Fri Sep 07 23:20:33 2012 +0200 @@ -245,6 +245,7 @@ TYPE_PASSWORD_RESET = 'passoword_link' TYPE_REGISTRATION = Notification.TYPE_REGISTRATION TYPE_PULL_REQUEST = Notification.TYPE_PULL_REQUEST + TYPE_PULL_REQUEST_COMMENT = Notification.TYPE_PULL_REQUEST_COMMENT TYPE_DEFAULT = 'default' def __init__(self): @@ -255,7 +256,9 @@ self.TYPE_CHANGESET_COMMENT: 'email_templates/changeset_comment.html', self.TYPE_PASSWORD_RESET: 'email_templates/password_reset.html', self.TYPE_REGISTRATION: 'email_templates/registration.html', - self.TYPE_DEFAULT: 'email_templates/default.html' + self.TYPE_DEFAULT: 'email_templates/default.html', + self.TYPE_PULL_REQUEST: 'email_templates/pull_request.html', + self.TYPE_PULL_REQUEST_COMMENT: 'email_templates/pull_request_comment.html', } def get_email_tmpl(self, type_, **kwargs):
--- a/rhodecode/model/permission.py Mon Sep 03 22:22:58 2012 +0200 +++ b/rhodecode/model/permission.py Fri Sep 07 23:20:33 2012 +0200 @@ -77,7 +77,7 @@ form_result['perm_user_name']).scalar() u2p = self.sa.query(UserToPerm).filter(UserToPerm.user == perm_user).all() - if len(u2p) != 4: + if len(u2p) != len(User.DEFAULT_PERMISSIONS): raise Exception('Defined: %s should be 4 permissions for default' ' user. This should not happen please verify' ' your database' % len(u2p))
--- a/rhodecode/model/pull_request.py Mon Sep 03 22:22:58 2012 +0200 +++ b/rhodecode/model/pull_request.py Fri Sep 07 23:20:33 2012 +0200 @@ -36,7 +36,8 @@ from rhodecode.model.notification import NotificationModel from rhodecode.lib.utils2 import safe_unicode -from rhodecode.lib.vcs.utils.hgcompat import discovery, localrepo, scmutil +from rhodecode.lib.vcs.utils.hgcompat import discovery, localrepo, scmutil, \ + findcommonoutgoing log = logging.getLogger(__name__) @@ -79,22 +80,30 @@ #notification to reviewers notif = NotificationModel() + pr_url = h.url('pullrequest_show', repo_name=other_repo.repo_name, + pull_request_id=new.pull_request_id, + qualified=True, + ) subject = safe_unicode( h.link_to( _('%(user)s wants you to review pull request #%(pr_id)s') % \ {'user': created_by_user.username, 'pr_id': new.pull_request_id}, - h.url('pullrequest_show', repo_name=other_repo.repo_name, - pull_request_id=new.pull_request_id, - qualified=True, - ) + pr_url ) ) body = description + kwargs = { + 'pr_title': title, + 'pr_user_created': h.person(created_by_user.email), + 'pr_repo_url': h.url('summary_home', repo_name=other_repo.repo_name, + qualified=True,), + 'pr_url': pr_url, + 'pr_revisions': revisions + } notif.create(created_by=created_by_user, subject=subject, body=body, recipients=reviewers, - type_=Notification.TYPE_PULL_REQUEST,) - + type_=Notification.TYPE_PULL_REQUEST, email_kwargs=kwargs) return new def update_reviewers(self, pull_request, reviewers_ids): @@ -156,7 +165,10 @@ #case two independent repos common, incoming, rheads = discovery_data if org_repo != other_repo and incoming: - revs = org_repo._repo.changelog.findmissing(common, rheads) + obj = findcommonoutgoing(org_repo._repo, + localrepo.locallegacypeer(other_repo._repo.local()), + force=True) + revs = obj.missing for cs in reversed(map(binascii.hexlify, revs)): changesets.append(org_repo.get_changeset(cs)) @@ -205,6 +217,7 @@ log.debug('Doing discovery for %s@%s vs %s@%s' % ( org_repo, org_ref, other_repo, other_ref) ) + #log.debug('Filter heads are %s[%s]' % ('', org_ref[1])) org_peer = localrepo.locallegacypeer(_org_repo.local()) tmp = discovery.findcommonincoming( @@ -212,7 +225,7 @@ remote=org_peer, # org_repo source for incoming heads=[_other_repo[other_rev].node(), _org_repo[org_rev].node()], - force=False + force=True ) return tmp
--- a/rhodecode/model/repo.py Mon Sep 03 22:22:58 2012 +0200 +++ b/rhodecode/model/repo.py Fri Sep 07 23:20:33 2012 +0200 @@ -368,6 +368,7 @@ obj.user = user obj.permission = permission self.sa.add(obj) + log.debug('Granted perm %s to %s on %s' % (perm, user, repo)) def revoke_user_permission(self, repo, user): """ @@ -383,8 +384,10 @@ obj = self.sa.query(UserRepoToPerm)\ .filter(UserRepoToPerm.repository == repo)\ .filter(UserRepoToPerm.user == user)\ - .one() - self.sa.delete(obj) + .scalar() + if obj: + self.sa.delete(obj) + log.debug('Revoked perm on %s on %s' % (repo, user)) def grant_users_group_permission(self, repo, group_name, perm): """ @@ -414,6 +417,7 @@ obj.users_group = group_name obj.permission = permission self.sa.add(obj) + log.debug('Granted perm %s to %s on %s' % (perm, group_name, repo)) def revoke_users_group_permission(self, repo, group_name): """ @@ -429,8 +433,10 @@ obj = self.sa.query(UsersGroupRepoToPerm)\ .filter(UsersGroupRepoToPerm.repository == repo)\ .filter(UsersGroupRepoToPerm.users_group == group_name)\ - .one() - self.sa.delete(obj) + .scalar() + if obj: + self.sa.delete(obj) + log.debug('Revoked perm to %s on %s' % (repo, group_name)) def delete_stats(self, repo_name): """
--- a/rhodecode/model/repos_group.py Mon Sep 03 22:22:58 2012 +0200 +++ b/rhodecode/model/repos_group.py Fri Sep 07 23:20:33 2012 +0200 @@ -32,7 +32,7 @@ from rhodecode.model import BaseModel from rhodecode.model.db import RepoGroup, RhodeCodeUi, UserRepoGroupToPerm, \ - User, Permission, UsersGroupRepoGroupToPerm, UsersGroup + User, Permission, UsersGroupRepoGroupToPerm, UsersGroup, Repository log = logging.getLogger(__name__) @@ -115,11 +115,12 @@ 'existing dir %s' % new_path) shutil.move(old_path, new_path) - def __delete_group(self, group): + def __delete_group(self, group, force_delete=False): """ Deletes a group from a filesystem :param group: instance of group from database + :param force_delete: use shutil rmtree to remove all objects """ paths = group.full_path.split(RepoGroup.url_sep()) paths = os.sep.join(paths) @@ -127,7 +128,10 @@ rm_path = os.path.join(self.repos_path, paths) if os.path.isdir(rm_path): # delete only if that path really exists - os.rmdir(rm_path) + if force_delete: + shutil.rmtree(rm_path) + else: + os.rmdir(rm_path) # this raises an exception when there are still objects inside def create(self, group_name, group_description, parent=None, just_db=False): try: @@ -150,32 +154,79 @@ log.error(traceback.format_exc()) raise + def _update_permissions(self, repos_group, perms_new=None, + perms_updates=None, recursive=False): + from rhodecode.model.repo import RepoModel + if not perms_new: + perms_new = [] + if not perms_updates: + perms_updates = [] + + def _set_perm_user(obj, user, perm): + if isinstance(obj, RepoGroup): + ReposGroupModel().grant_user_permission( + repos_group=obj, user=user, perm=perm + ) + elif isinstance(obj, Repository): + # we set group permission but we have to switch to repo + # permission + perm = perm.replace('group.', 'repository.') + RepoModel().grant_user_permission( + repo=obj, user=user, perm=perm + ) + + def _set_perm_group(obj, users_group, perm): + if isinstance(obj, RepoGroup): + ReposGroupModel().grant_users_group_permission( + repos_group=obj, group_name=users_group, perm=perm + ) + elif isinstance(obj, Repository): + # we set group permission but we have to switch to repo + # permission + perm = perm.replace('group.', 'repository.') + RepoModel().grant_users_group_permission( + repo=obj, group_name=users_group, perm=perm + ) + updates = [] + log.debug('Now updating permissions for %s in recursive mode:%s' + % (repos_group, recursive)) + + for obj in repos_group.recursive_groups_and_repos(): + if not recursive: + obj = repos_group + + # update permissions + for member, perm, member_type in perms_updates: + ## set for user + if member_type == 'user': + # this updates also current one if found + _set_perm_user(obj, user=member, perm=perm) + ## set for users group + else: + _set_perm_group(obj, users_group=member, perm=perm) + # set new permissions + for member, perm, member_type in perms_new: + if member_type == 'user': + _set_perm_user(obj, user=member, perm=perm) + else: + _set_perm_group(obj, users_group=member, perm=perm) + updates.append(obj) + #if it's not recursive call + # break the loop and don't proceed with other changes + if not recursive: + break + return updates + def update(self, repos_group_id, form_data): try: repos_group = RepoGroup.get(repos_group_id) - - # update permissions - for member, perm, member_type in form_data['perms_updates']: - if member_type == 'user': - # this updates also current one if found - ReposGroupModel().grant_user_permission( - repos_group=repos_group, user=member, perm=perm - ) - else: - ReposGroupModel().grant_users_group_permission( - repos_group=repos_group, group_name=member, perm=perm - ) - # set new permissions - for member, perm, member_type in form_data['perms_new']: - if member_type == 'user': - ReposGroupModel().grant_user_permission( - repos_group=repos_group, user=member, perm=perm - ) - else: - ReposGroupModel().grant_users_group_permission( - repos_group=repos_group, group_name=member, perm=perm - ) + recursive = form_data['recursive'] + # iterate over all members(if in recursive mode) of this groups and + # set the permissions ! + # this can be potentially heavy operation + self._update_permissions(repos_group, form_data['perms_new'], + form_data['perms_updates'], recursive) old_path = repos_group.full_path @@ -191,7 +242,6 @@ # iterate over all members of this groups and set the locking ! # this can be potentially heavy operation - for obj in repos_group.recursive_groups_and_repos(): #set the value from it's parent obj.enable_locking = repos_group.enable_locking @@ -210,15 +260,54 @@ log.error(traceback.format_exc()) raise - def delete(self, repos_group): + def delete(self, repos_group, force_delete=False): repos_group = self._get_repos_group(repos_group) try: self.sa.delete(repos_group) - self.__delete_group(repos_group) + self.__delete_group(repos_group, force_delete) except: log.exception('Error removing repos_group %s' % repos_group) raise + def delete_permission(self, repos_group, obj, obj_type, recursive): + """ + Revokes permission for repos_group for given obj(user or users_group), + obj_type can be user or users group + + :param repos_group: + :param obj: user or users group id + :param obj_type: user or users group type + :param recursive: recurse to all children of group + """ + from rhodecode.model.repo import RepoModel + repos_group = self._get_repos_group(repos_group) + + for el in repos_group.recursive_groups_and_repos(): + if not recursive: + # if we don't recurse set the permission on only the top level + # object + el = repos_group + + if isinstance(el, RepoGroup): + if obj_type == 'user': + ReposGroupModel().revoke_user_permission(el, user=obj) + elif obj_type == 'users_group': + ReposGroupModel().revoke_users_group_permission(el, group_name=obj) + else: + raise Exception('undefined object type %s' % obj_type) + elif isinstance(el, Repository): + if obj_type == 'user': + RepoModel().revoke_user_permission(el, user=obj) + elif obj_type == 'users_group': + RepoModel().revoke_users_group_permission(el, group_name=obj) + else: + raise Exception('undefined object type %s' % obj_type) + + #if it's not recursive call + # break the loop and don't proceed with other changes + if not recursive: + break + def grant_user_permission(self, repos_group, user, perm): """ Grant permission for user on given repositories group, or update @@ -246,6 +335,7 @@ obj.user = user obj.permission = permission self.sa.add(obj) + log.debug('Granted perm %s to %s on %s' % (perm, user, repos_group)) def revoke_user_permission(self, repos_group, user): """ @@ -262,8 +352,10 @@ obj = self.sa.query(UserRepoGroupToPerm)\ .filter(UserRepoGroupToPerm.user == user)\ .filter(UserRepoGroupToPerm.group == repos_group)\ - .one() - self.sa.delete(obj) + .scalar() + if obj: + self.sa.delete(obj) + log.debug('Revoked perm on %s on %s' % (repos_group, user)) def grant_users_group_permission(self, repos_group, group_name, perm): """ @@ -294,6 +386,7 @@ obj.users_group = group_name obj.permission = permission self.sa.add(obj) + log.debug('Granted perm %s to %s on %s' % (perm, group_name, repos_group)) def revoke_users_group_permission(self, repos_group, group_name): """ @@ -310,5 +403,7 @@ obj = self.sa.query(UsersGroupRepoGroupToPerm)\ .filter(UsersGroupRepoGroupToPerm.group == repos_group)\ .filter(UsersGroupRepoGroupToPerm.users_group == group_name)\ - .one() - self.sa.delete(obj) + .scalar() + if obj: + self.sa.delete(obj) + log.debug('Revoked perm to %s on %s' % (repos_group, group_name))
--- a/rhodecode/model/user.py Mon Sep 03 22:22:58 2012 +0200 +++ b/rhodecode/model/user.py Fri Sep 07 23:20:33 2012 +0200 @@ -564,7 +564,7 @@ rg_k = perm.UserRepoGroupToPerm.group.group_name p = perm.Permission.permission_name cur_perm = user.permissions[GK][rg_k] - if PERM_WEIGHTS[p] > PERM_WEIGHTS[cur_perm]: + if PERM_WEIGHTS[p] > PERM_WEIGHTS[cur_perm] or 1: # disable check user.permissions[GK][rg_k] = p # REPO GROUP + USER GROUP @@ -588,7 +588,7 @@ cur_perm = user.permissions[GK][g_k] # overwrite permission only if it's greater than permission # given from other sources - if PERM_WEIGHTS[p] > PERM_WEIGHTS[cur_perm]: + if PERM_WEIGHTS[p] > PERM_WEIGHTS[cur_perm] or 1: # disable check user.permissions[GK][g_k] = p return user
--- a/rhodecode/model/validators.py Mon Sep 03 22:22:58 2012 +0200 +++ b/rhodecode/model/validators.py Fri Sep 07 23:20:33 2012 +0200 @@ -499,9 +499,9 @@ # fill new permissions in order of how they were added for k in sorted(map(int, new_perms_group.keys())): perm_dict = new_perms_group[str(k)] - new_member = perm_dict['name'] - new_perm = perm_dict['perm'] - new_type = perm_dict['type'] + new_member = perm_dict.get('name') + new_perm = perm_dict.get('perm') + new_type = perm_dict.get('type') if new_member and new_perm and new_type: perms_new.add((new_member, new_perm, new_type))
--- a/rhodecode/templates/admin/repos/repo_edit.html Mon Sep 03 22:22:58 2012 +0200 +++ b/rhodecode/templates/admin/repos/repo_edit.html Fri Sep 07 23:20:33 2012 +0200 @@ -115,7 +115,7 @@ ${h.checkbox('enable_locking',value="True")} <span class="help-block">${_('Enable lock-by-pulling on repository.')}</span> </div> - </div> + </div> <div class="field"> <div class="label"> <label for="user">${_('Owner')}:</label> @@ -188,6 +188,20 @@ <div class="form"> <div class="fields"> ${h.submit('reset_cache_%s' % c.repo_info.repo_name,_('Invalidate repository cache'),class_="ui-btn",onclick="return confirm('"+_('Confirm to invalidate repository cache')+"');")} + <div class="field" style="border:none;color:#888"> + <ul> + <li>${_('Manually invalidate cache for this repository. On first access repository will be cached again')} + </li> + </ul> + </div> + <div class="field" style="border:none;"> + ${_('List of cached values')} + <ul> + %for cache in c.repo_info.cache_keys: + <li>INSTANCE ID:${cache.prefix or '-'} ${cache.cache_args} CACHED: ${h.bool2icon(cache.cache_active)}</li> + %endfor + </ul> + </div> </div> </div> ${h.end_form()} @@ -195,20 +209,20 @@ <h3>${_('Public journal')}</h3> ${h.form(url('repo_public_journal', repo_name=c.repo_info.repo_name),method='put')} <div class="form"> - ${h.hidden('auth_token',str(h.get_token()))} - <div class="field"> - %if c.in_public_journal: - ${h.submit('set_public_%s' % c.repo_info.repo_name,_('Remove from public journal'),class_="ui-btn")} - %else: - ${h.submit('set_public_%s' % c.repo_info.repo_name,_('Add to public journal'),class_="ui-btn")} - %endif - </div> - <div class="field" style="border:none;color:#888"> - <ul> - <li>${_('All actions made on this repository will be accessible to everyone in public journal')} - </li> - </ul> - </div> + ${h.hidden('auth_token',str(h.get_token()))} + <div class="field"> + %if c.in_public_journal: + ${h.submit('set_public_%s' % c.repo_info.repo_name,_('Remove from public journal'),class_="ui-btn")} + %else: + ${h.submit('set_public_%s' % c.repo_info.repo_name,_('Add to public journal'),class_="ui-btn")} + %endif + </div> + <div class="field" style="border:none;color:#888"> + <ul> + <li>${_('All actions made on this repository will be accessible to everyone in public journal')} + </li> + </ul> + </div> </div> ${h.end_form()} @@ -229,7 +243,7 @@ <li>${_('Force locking on repository. Works only when anonymous access is disabled')} </li> </ul> - </div> + </div> </div> ${h.end_form()} @@ -245,9 +259,9 @@ <li>${_('''Manually set this repository as a fork of another from the list''')}</li> </ul> </div> - </div> + </div> ${h.end_form()} - + <h3>${_('Delete')}</h3> ${h.form(url('repo', repo_name=c.repo_info.repo_name),method='delete')} <div class="form"> @@ -262,7 +276,7 @@ </ul> </div> </div> - ${h.end_form()} + ${h.end_form()} </div> </%def>
--- a/rhodecode/templates/admin/repos/repo_edit_perms.html Mon Sep 03 22:22:58 2012 +0200 +++ b/rhodecode/templates/admin/repos/repo_edit_perms.html Fri Sep 07 23:20:33 2012 +0200 @@ -74,8 +74,8 @@ </div> \ </td> \ <td></td>'""") - %> - ## ADD HERE DYNAMICALLY NEW INPUTS FROM THE '_tmpl' + %> + ## ADD HERE DYNAMICALLY NEW INPUTS FROM THE '_tmpl' <tr class="new_members last_new_member" id="add_perm_input"></tr> <tr> <td colspan="6">
--- a/rhodecode/templates/admin/repos_groups/repos_group_edit_perms.html Mon Sep 03 22:22:58 2012 +0200 +++ b/rhodecode/templates/admin/repos_groups/repos_group_edit_perms.html Fri Sep 07 23:20:33 2012 +0200 @@ -58,8 +58,8 @@ </div> \ </td> \ <td></td>'""") - %> - ## ADD HERE DYNAMICALLY NEW INPUTS FROM THE '_tmpl' + %> + ## ADD HERE DYNAMICALLY NEW INPUTS FROM THE '_tmpl' <tr class="new_members last_new_member" id="add_perm_input"></tr> <tr> <td colspan="6"> @@ -68,6 +68,12 @@ </span> </td> </tr> + <tr> + <td colspan="6"> + ${h.checkbox('recursive',value="True", label=_('apply to parents'))} + <span class="help-block">${_('Set or revoke permission to all children of that group, including repositories and other groups')}</span> + </td> + </tr> </table> <script type="text/javascript"> function ajaxActionUser(user_id, field_id) { @@ -81,7 +87,8 @@ alert("${_('Failed to remove user')}"); }, }; - var postData = '_method=delete&user_id=' + user_id; + var recursive = YUD.get('recursive').checked; + var postData = '_method=delete&recursive={0}&user_id={1}'.format(recursive,user_id); var request = YAHOO.util.Connect.asyncRequest('POST', sUrl, callback, postData); }; @@ -96,7 +103,8 @@ alert("${_('Failed to remove users group')}"); }, }; - var postData = '_method=delete&users_group_id='+users_group_id; + var recursive = YUD.get('recursive').checked; + var postData = '_method=delete&recursive={0}&users_group_id={1}'.format(recursive,users_group_id); var request = YAHOO.util.Connect.asyncRequest('POST', sUrl, callback, postData); };
--- a/rhodecode/templates/admin/repos_groups/repos_groups.html Mon Sep 03 22:22:58 2012 +0200 +++ b/rhodecode/templates/admin/repos_groups/repos_groups.html Fri Sep 07 23:20:33 2012 +0200 @@ -5,7 +5,8 @@ </%def> <%def name="breadcrumbs()"> - <span class="groups_breadcrumbs"> ${_('Groups')} + <span class="groups_breadcrumbs"> + ${h.link_to(_(u'Home'),h.url('/'))} %if c.group.parent_group: » ${h.link_to(c.group.parent_group.name,h.url('repos_group_home',group_name=c.group.parent_group.group_name))} %endif
--- a/rhodecode/templates/admin/repos_groups/repos_groups_edit.html Mon Sep 03 22:22:58 2012 +0200 +++ b/rhodecode/templates/admin/repos_groups/repos_groups_edit.html Fri Sep 07 23:20:33 2012 +0200 @@ -69,7 +69,7 @@ ${h.checkbox('enable_locking',value="True")} <span class="help-block">${_('Enable lock-by-pulling on group. This option will be applied to all other groups and repositories inside')}</span> </div> - </div> + </div> <div class="buttons"> ${h.submit('save',_('Save'),class_="ui-btn large")} ${h.reset('reset',_('Reset'),class_="ui-btn large")}
--- a/rhodecode/templates/admin/settings/settings.html Mon Sep 03 22:22:58 2012 +0200 +++ b/rhodecode/templates/admin/settings/settings.html Fri Sep 07 23:20:33 2012 +0200 @@ -154,7 +154,7 @@ <li>[stale] <span class="metatag" tag="stale">stale</span></li> <li>[dead] <span class="metatag" tag="dead">dead</span></li> <li>[lang => lang] <span class="metatag" tag="lang" >lang</span></li> - <li>[license => License] <span class="metatag" tag="license"><a href="http://www.opensource.org/licenses/License" >License</a></span></li> + <li>[license => License] <span class="metatag" tag="license"><a href="http://www.opensource.org/licenses/License" >License</a></span></li> <li>[requires => Repo] <span class="metatag" tag="requires" >requires => <a href="#" >Repo</a></span></li> <li>[recommends => Repo] <span class="metatag" tag="recommends" >recommends => <a href="#" >Repo</a></span></li> <li>[see => URI] <span class="metatag" tag="see">see => <a href="#">URI</a> </span></li> @@ -186,7 +186,7 @@ </div> <div class="checkboxes"> <div class="checkbox"> - ${h.checkbox('web_push_ssl','true')} + ${h.checkbox('web_push_ssl', 'True')} <label for="web_push_ssl">${_('require ssl for vcs operations')}</label> </div> <span class="help-block">${_('RhodeCode will require SSL for pushing or pulling. If SSL is missing it will return HTTP Error 406: Not Acceptable')}</span> @@ -237,9 +237,9 @@ ## ${h.checkbox('extensions_hggit','True')} ## <label for="extensions_hggit">${_('hg-git extensions')}</label> ##</div> - ##<span class="help-block">${_('Requires hg-git library installed. Allows clonning from git remote locations')}</span> + ##<span class="help-block">${_('Requires hg-git library installed. Allows clonning from git remote locations')}</span> </div> - </div> + </div> <div class="field"> <div class="label"> <label for="paths_root_path">${_('Repositories location')}:</label>
--- a/rhodecode/templates/admin/users/user_edit.html Mon Sep 03 22:22:58 2012 +0200 +++ b/rhodecode/templates/admin/users/user_edit.html Fri Sep 07 23:20:33 2012 +0200 @@ -152,7 +152,7 @@ <span class="help-block">${h.literal(_('Select to inherit permissions from %s settings. ' 'With this selected below options does not have any action') % h.link_to('default', url('edit_permission', id='default')))}</span> </div> - <div id="inherit_overlay" style="${'opacity:0.3' if c.user.inherit_default_permissions else ''}" > + <div id="inherit_overlay" style="${'opacity:0.3' if c.user.inherit_default_permissions else ''}" > <div class="field"> <div class="label label-checkbox"> <label for="create_repo_perm">${_('Create repositories')}:</label> @@ -169,7 +169,7 @@ ${h.checkbox('fork_repo_perm',value=True)} </div> </div> - </div> + </div> <div class="buttons"> ${h.submit('save',_('Save'),class_="ui-btn large")} ${h.reset('reset',_('Reset'),class_="ui-btn large")}
--- a/rhodecode/templates/admin/users_groups/users_group_edit.html Mon Sep 03 22:22:58 2012 +0200 +++ b/rhodecode/templates/admin/users_groups/users_group_edit.html Fri Sep 07 23:20:33 2012 +0200 @@ -112,7 +112,7 @@ </div> <span class="help-block">${h.literal(_('Select to inherit permissions from %s settings. ' 'With this selected below options does not have any action') % h.link_to('default', url('edit_permission', id='default')))}</span> - </div> + </div> <div id="inherit_overlay" style="${'opacity:0.3' if c.users_group.inherit_default_permissions else ''}" > <div class="field"> <div class="label label-checkbox"> @@ -130,7 +130,7 @@ ${h.checkbox('fork_repo_perm',value=True)} </div> </div> - </div> + </div> <div class="buttons"> ${h.submit('save',_('Save'),class_="ui-btn large")} ${h.reset('reset',_('Reset'),class_="ui-btn large")}
--- a/rhodecode/templates/base/root.html Mon Sep 03 22:22:58 2012 +0200 +++ b/rhodecode/templates/base/root.html Fri Sep 07 23:20:33 2012 +0200 @@ -90,7 +90,7 @@ f.setAttribute('class','follow'); f.setAttribute('title',_TM['Start following this repository']); if(f_cnt){ - var cnt = Number(f_cnt.innerHTML)+1; + var cnt = Number(f_cnt.innerHTML)-1; f_cnt.innerHTML = cnt; } }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rhodecode/templates/email_templates/pull_request.html Fri Sep 07 23:20:33 2012 +0200 @@ -0,0 +1,20 @@ +## -*- coding: utf-8 -*- +<%inherit file="main.html"/> + +User <b>${pr_user_created}</b> opened pull request for repository +${pr_repo_url} and wants you to review changes. + +<div>title: ${pr_title}</div> +<div>description:</div> +<p> +${body} +</p> + +<div>revisions for reviewing</div> +<ul> +%for r in pr_revisions: + <li>${r}</li> +%endfor +</ul> + +View this pull request here: ${pr_url}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rhodecode/templates/email_templates/pull_request_comment.html Fri Sep 07 23:20:33 2012 +0200 @@ -0,0 +1,15 @@ +## -*- coding: utf-8 -*- +<%inherit file="main.html"/> + +User <b>${pr_comment_user}</b> commented on pull request #${pr_id} for +repository ${pr_target_repo} + +<p> +${body} + +%if status_change: + <span>New status -> ${status_change}</span> +%endif +</p> + +View this comment here: ${pr_comment_url}
--- a/rhodecode/templates/files/files.html Mon Sep 03 22:22:58 2012 +0200 +++ b/rhodecode/templates/files/files.html Fri Sep 07 23:20:33 2012 +0200 @@ -47,38 +47,38 @@ var ypjax_links = function(){ YUE.on(YUQ('.ypjax-link'), 'click',function(e){ - + //don't do ypjax on middle click - if(e.which == 2 || !History.enabled){ + if(e.which == 2 || !History.enabled){ return true; } - + var el = e.currentTarget; var url = el.href; var _base_url = '${h.url("files_home",repo_name=c.repo_name,revision='',f_path='')}'; _base_url = _base_url.replace('//','/') - + //extract rev and the f_path from url. parts = url.split(_base_url) if(parts.length != 2){ return false; } - + var parts2 = parts[1].split('/'); var rev = parts2.shift(); // pop the first element which is the revision var f_path = parts2.join('/'); - + var title = "${_('%s files') % c.repo_name}" + " - " + f_path; - + var _node_list_url = node_list_url.replace('__REV__',rev); var _url_base = url_base.replace('__REV__',rev).replace('__FPATH__', f_path); // Change our States and save some data for handling events var data = {url:url,title:title, url_base:_url_base, node_list_url:_node_list_url}; - History.pushState(data, title, url); - + History.pushState(data, title, url); + //now we're sure that we can do ypjax things YUE.preventDefault(e) return false; @@ -92,10 +92,10 @@ // Inform Google Analytics of the change if ( typeof window.pageTracker !== 'undefined' ) { window.pageTracker._trackPageview(State.url); - } + } } -YUE.onDOMReady(function(){ +YUE.onDOMReady(function(){ ypjax_links(); var container = 'files_data'; //Bind to StateChange Event @@ -124,8 +124,8 @@ } }); } - }); - + }); + // init the search filter var _State = { url: "${h.url.current()}",
--- a/rhodecode/templates/files/files_ypjax.html Mon Sep 03 22:22:58 2012 +0200 +++ b/rhodecode/templates/files/files_ypjax.html Fri Sep 07 23:20:33 2012 +0200 @@ -9,7 +9,7 @@ <%include file='files_browser.html'/> %else: <%include file='files_source.html'/> - %endif + %endif %else: <h2> <a href="#" onClick="javascript:parent.history.back();" target="main">${_('Go back')}</a>
--- a/rhodecode/templates/pullrequests/pullrequest_show.html Mon Sep 03 22:22:58 2012 +0200 +++ b/rhodecode/templates/pullrequests/pullrequest_show.html Fri Sep 07 23:20:33 2012 +0200 @@ -20,10 +20,10 @@ ${self.breadcrumbs()} </div> %if c.pull_request.is_closed(): - <div style="padding:10px; font-size:22px;width:100%;text-align: center; color:#88D882">${_('Closed %s') % (h.age(c.pull_request.updated_on))}</div> + <div style="padding:10px; font-size:22px;width:100%;text-align: center; color:#88D882">${_('Closed %s') % (h.age(c.pull_request.updated_on))} ${_('with status %s') % h.changeset_status_lbl(c.current_changeset_status)}</div> %endif <h3>${_('Title')}: ${c.pull_request.title}</h3> - + <div class="form"> <div id="summary" class="fields"> <div class="field"> @@ -46,9 +46,9 @@ <div class="input"> <div class="tooltip" title="${h.tooltip(','.join([x.username for x in c.pull_request_pending_reviewers]))}">${ungettext('%d reviewer', '%d reviewers',len(c.pull_request_pending_reviewers)) % len(c.pull_request_pending_reviewers)}</div> </div> - </div> + </div> </div> - </div> + </div> <div style="white-space:pre-wrap;padding:3px 3px 5px 20px">${h.literal(c.pull_request.description)}</div> <div style="padding:4px 4px 10px 20px"> <div>${_('Created on')}: ${h.fmt_date(c.pull_request.created_on)}</div>
--- a/rhodecode/templates/summary/summary.html Mon Sep 03 22:22:58 2012 +0200 +++ b/rhodecode/templates/summary/summary.html Fri Sep 07 23:20:33 2012 +0200 @@ -7,7 +7,7 @@ <%def name="breadcrumbs_links()"> ${h.link_to(_(u'Home'),h.url('/'))} » - ${h.link_to(c.dbrepo.just_name,h.url('summary_home',repo_name=c.repo_name))} + ${h.repo_link(c.dbrepo.groups_and_repo)} » ${_('summary')} </%def> @@ -108,7 +108,7 @@ <div class="input ${summary(c.show_stats)} desc">${h.urlify_text(h.desc_stylize(c.dbrepo.description))}</div> %else: <div class="input ${summary(c.show_stats)} desc">${h.urlify_text(c.dbrepo.description)}</div> - %endif + %endif </div> <div class="field">
--- a/rhodecode/tests/functional/test_compare.py Mon Sep 03 22:22:58 2012 +0200 +++ b/rhodecode/tests/functional/test_compare.py Fri Sep 07 23:20:33 2012 +0200 @@ -30,17 +30,17 @@ response.mustcontain('''<a href="/%s/changeset/c5ddebc06eaaba3010c2d66ea6ec9d074eb0f678">r112:c5ddebc06eaa</a>''' % HG_REPO) ## files diff - response.mustcontain('''<div class="node"><a href="/%s/compare/tag@%s...tag@%s#C--1c5cf9e91c12">docs/api/utils/index.rst</a></div>''' % (HG_REPO, tag1, tag2)) - response.mustcontain('''<div class="node"><a href="/%s/compare/tag@%s...tag@%s#C--e3305437df55">test_and_report.sh</a></div>''' % (HG_REPO, tag1, tag2)) - response.mustcontain('''<div class="node"><a href="/%s/compare/tag@%s...tag@%s#C--c8e92ef85cd1">.hgignore</a></div>''' % (HG_REPO, tag1, tag2)) - response.mustcontain('''<div class="node"><a href="/%s/compare/tag@%s...tag@%s#C--6e08b694d687">.hgtags</a></div>''' % (HG_REPO, tag1, tag2)) - response.mustcontain('''<div class="node"><a href="/%s/compare/tag@%s...tag@%s#C--2c14b00f3393">docs/api/index.rst</a></div>''' % (HG_REPO, tag1, tag2)) - response.mustcontain('''<div class="node"><a href="/%s/compare/tag@%s...tag@%s#C--430ccbc82bdf">vcs/__init__.py</a></div>''' % (HG_REPO, tag1, tag2)) - response.mustcontain('''<div class="node"><a href="/%s/compare/tag@%s...tag@%s#C--9c390eb52cd6">vcs/backends/hg.py</a></div>''' % (HG_REPO, tag1, tag2)) - response.mustcontain('''<div class="node"><a href="/%s/compare/tag@%s...tag@%s#C--ebb592c595c0">vcs/utils/__init__.py</a></div>''' % (HG_REPO, tag1, tag2)) - response.mustcontain('''<div class="node"><a href="/%s/compare/tag@%s...tag@%s#C--7abc741b5052">vcs/utils/annotate.py</a></div>''' % (HG_REPO, tag1, tag2)) - response.mustcontain('''<div class="node"><a href="/%s/compare/tag@%s...tag@%s#C--2ef0ef106c56">vcs/utils/diffs.py</a></div>''' % (HG_REPO, tag1, tag2)) - response.mustcontain('''<div class="node"><a href="/%s/compare/tag@%s...tag@%s#C--3150cb87d4b7">vcs/utils/lazy.py</a></div>''' % (HG_REPO, tag1, tag2)) + response.mustcontain('''<div class="node"><a href="/%s/compare/tag@%s...tag@%s#C--1c5cf9e91c12">docs/api/utils/index.rst</a></div>''' % (HG_REPO, tag1, tag2)) + response.mustcontain('''<div class="node"><a href="/%s/compare/tag@%s...tag@%s#C--e3305437df55">test_and_report.sh</a></div>''' % (HG_REPO, tag1, tag2)) + response.mustcontain('''<div class="node"><a href="/%s/compare/tag@%s...tag@%s#C--c8e92ef85cd1">.hgignore</a></div>''' % (HG_REPO, tag1, tag2)) + response.mustcontain('''<div class="node"><a href="/%s/compare/tag@%s...tag@%s#C--6e08b694d687">.hgtags</a></div>''' % (HG_REPO, tag1, tag2)) + response.mustcontain('''<div class="node"><a href="/%s/compare/tag@%s...tag@%s#C--2c14b00f3393">docs/api/index.rst</a></div>''' % (HG_REPO, tag1, tag2)) + response.mustcontain('''<div class="node"><a href="/%s/compare/tag@%s...tag@%s#C--430ccbc82bdf">vcs/__init__.py</a></div>''' % (HG_REPO, tag1, tag2)) + response.mustcontain('''<div class="node"><a href="/%s/compare/tag@%s...tag@%s#C--9c390eb52cd6">vcs/backends/hg.py</a></div>''' % (HG_REPO, tag1, tag2)) + response.mustcontain('''<div class="node"><a href="/%s/compare/tag@%s...tag@%s#C--ebb592c595c0">vcs/utils/__init__.py</a></div>''' % (HG_REPO, tag1, tag2)) + response.mustcontain('''<div class="node"><a href="/%s/compare/tag@%s...tag@%s#C--7abc741b5052">vcs/utils/annotate.py</a></div>''' % (HG_REPO, tag1, tag2)) + response.mustcontain('''<div class="node"><a href="/%s/compare/tag@%s...tag@%s#C--2ef0ef106c56">vcs/utils/diffs.py</a></div>''' % (HG_REPO, tag1, tag2)) + response.mustcontain('''<div class="node"><a href="/%s/compare/tag@%s...tag@%s#C--3150cb87d4b7">vcs/utils/lazy.py</a></div>''' % (HG_REPO, tag1, tag2)) def test_index_branch(self): self.log_user() @@ -183,7 +183,111 @@ ## files response.mustcontain("""<a href="/%s/compare/branch@%s...branch@%s#C--826e8142e6ba">file1</a>""" % (r2_name, rev1, rev2)) - finally: RepoModel().delete(r1_id) RepoModel().delete(r2_id) + + def test_org_repo_new_commits_after_forking(self): + self.log_user() + + repo1 = RepoModel().create_repo(repo_name='one', repo_type='hg', + description='diff-test', + owner=TEST_USER_ADMIN_LOGIN) + + Session().commit() + r1_id = repo1.repo_id + r1_name = repo1.repo_name + + #commit something initially ! + cs0 = ScmModel().create_node( + repo=repo1.scm_instance, repo_name=r1_name, + cs=EmptyChangeset(alias='hg'), user=TEST_USER_ADMIN_LOGIN, + author=TEST_USER_ADMIN_LOGIN, + message='commit1', + content='line1', + f_path='file1' + ) + Session().commit() + self.assertEqual(repo1.scm_instance.revisions, [cs0.raw_id]) + #fork the repo1 + repo2 = RepoModel().create_repo(repo_name='one-fork', repo_type='hg', + description='compare-test', + clone_uri=repo1.repo_full_path, + owner=TEST_USER_ADMIN_LOGIN, fork_of='one') + Session().commit() + self.assertEqual(repo2.scm_instance.revisions, [cs0.raw_id]) + r2_id = repo2.repo_id + r2_name = repo2.repo_name + + #make 3 new commits in fork + cs1 = ScmModel().create_node( + repo=repo2.scm_instance, repo_name=r2_name, + cs=repo2.scm_instance[-1], user=TEST_USER_ADMIN_LOGIN, + author=TEST_USER_ADMIN_LOGIN, + message='commit1-fork', + content='file1-line1-from-fork', + f_path='file1-fork' + ) + cs2 = ScmModel().create_node( + repo=repo2.scm_instance, repo_name=r2_name, + cs=cs1, user=TEST_USER_ADMIN_LOGIN, + author=TEST_USER_ADMIN_LOGIN, + message='commit2-fork', + content='file2-line1-from-fork', + f_path='file2-fork' + ) + cs3 = ScmModel().create_node( + repo=repo2.scm_instance, repo_name=r2_name, + cs=cs2, user=TEST_USER_ADMIN_LOGIN, + author=TEST_USER_ADMIN_LOGIN, + message='commit3-fork', + content='file3-line1-from-fork', + f_path='file3-fork' + ) + + #compare ! + rev1 = 'default' + rev2 = 'default' + response = self.app.get(url(controller='compare', action='index', + repo_name=r2_name, + org_ref_type="branch", + org_ref=rev1, + other_ref_type="branch", + other_ref=rev2, + repo=r1_name + )) + + try: + response.mustcontain('%s@%s -> %s@%s' % (r2_name, rev1, r1_name, rev2)) + response.mustcontain("""file1-line1-from-fork""") + response.mustcontain("""file2-line1-from-fork""") + response.mustcontain("""file3-line1-from-fork""") + + #add new commit into parent ! + cs0 = ScmModel().create_node( + repo=repo1.scm_instance, repo_name=r1_name, + cs=EmptyChangeset(alias='hg'), user=TEST_USER_ADMIN_LOGIN, + author=TEST_USER_ADMIN_LOGIN, + message='commit2', + content='line1', + f_path='file2' + ) + #compare ! + rev1 = 'default' + rev2 = 'default' + response = self.app.get(url(controller='compare', action='index', + repo_name=r2_name, + org_ref_type="branch", + org_ref=rev1, + other_ref_type="branch", + other_ref=rev2, + repo=r1_name + )) + + response.mustcontain('%s@%s -> %s@%s' % (r2_name, rev1, r1_name, rev2)) + response.mustcontain("""file1-line1-from-fork""") + response.mustcontain("""file2-line1-from-fork""") + response.mustcontain("""file3-line1-from-fork""") + finally: + RepoModel().delete(r2_id) + RepoModel().delete(r1_id)
--- a/rhodecode/tests/functional/test_summary.py Mon Sep 03 22:22:58 2012 +0200 +++ b/rhodecode/tests/functional/test_summary.py Fri Sep 07 23:20:33 2012 +0200 @@ -1,6 +1,9 @@ from rhodecode.tests import * from rhodecode.model.db import Repository from rhodecode.lib.utils import invalidate_cache +from rhodecode.model.repo import RepoModel +from rhodecode.tests.models.common import _make_repo +from rhodecode.model.meta import Session class TestSummaryController(TestController): @@ -82,6 +85,20 @@ """title="public repository" alt="public """ """repository" src="/images/icons/lock_open.png"/>""") + def test_index_by_repo_having_id_path_in_name_hg(self): + self.log_user() + _make_repo(name='repo_1') + Session().commit() + response = self.app.get(url(controller='summary', + action='index', + repo_name='repo_1')) + + try: + response.mustcontain("""repo_1""") + finally: + RepoModel().delete(Repository.get_by_repo_name('repo_1')) + Session().commit() + def test_index_by_id_git(self): self.log_user() ID = Repository.get_by_repo_name(GIT_REPO).repo_id
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rhodecode/tests/models/common.py Fri Sep 07 23:20:33 2012 +0200 @@ -0,0 +1,116 @@ +import os +import unittest +import functools +from rhodecode.tests import * + + +from rhodecode.model.repos_group import ReposGroupModel +from rhodecode.model.repo import RepoModel +from rhodecode.model.db import RepoGroup, Repository, User +from rhodecode.model.user import UserModel + +from rhodecode.lib.auth import AuthUser +from rhodecode.model.meta import Session + + +def _make_group(path, desc='desc', parent_id=None, + skip_if_exists=False): + + gr = RepoGroup.get_by_group_name(path) + if gr and skip_if_exists: + return gr + if isinstance(parent_id, RepoGroup): + parent_id = parent_id.group_id + gr = ReposGroupModel().create(path, desc, parent_id) + return gr + + +def _make_repo(name, repos_group=None, repo_type='hg'): + return RepoModel().create_repo(name, repo_type, 'desc', + TEST_USER_ADMIN_LOGIN, + repos_group=repos_group) + + +def _destroy_project_tree(test_u1_id): + Session.remove() + repos_group = RepoGroup.get_by_group_name(group_name='g0') + for el in reversed(repos_group.recursive_groups_and_repos()): + if isinstance(el, Repository): + RepoModel().delete(el) + elif isinstance(el, RepoGroup): + ReposGroupModel().delete(el, force_delete=True) + + u = User.get(test_u1_id) + Session().delete(u) + Session().commit() + + +def _create_project_tree(): + """ + Creates a tree of groups and repositories to test permissions + + structure + [g0] - group `g0` with 3 subgroups + | + |__[g0_1] group g0_1 with 2 groups 0 repos + | | + | |__[g0_1_1] group g0_1_1 with 1 group 2 repos + | | |__<g0/g0_1/g0_1_1/g0_1_1_r1> + | | |__<g0/g0_1/g0_1_1/g0_1_1_r2> + | |__<g0/g0_1/g0_1_r1> + | + |__[g0_2] 2 repos + | | + | |__<g0/g0_2/g0_2_r1> + | |__<g0/g0_2/g0_2_r2> + | + |__[g0_3] 1 repo + | + |_<g0/g0_3/g0_3_r1> + + """ + test_u1 = UserModel().create_or_update( + username=u'test_u1', password=u'qweqwe', + email=u'test_u1@rhodecode.org', firstname=u'test_u1', lastname=u'test_u1' + ) + g0 = _make_group('g0') + g0_1 = _make_group('g0_1', parent_id=g0) + g0_1_1 = _make_group('g0_1_1', parent_id=g0_1) + g0_1_1_r1 = _make_repo('g0/g0_1/g0_1_1/g0_1_1_r1', repos_group=g0_1_1) + g0_1_1_r2 = _make_repo('g0/g0_1/g0_1_1/g0_1_1_r2', repos_group=g0_1_1) + g0_1_r1 = _make_repo('g0/g0_1/g0_1_r1', repos_group=g0_1) + g0_2 = _make_group('g0_2', parent_id=g0) + g0_2_r1 = _make_repo('g0/g0_2/g0_2_r1', repos_group=g0_2) + g0_2_r2 = _make_repo('g0/g0_2/g0_2_r2', repos_group=g0_2) + g0_3 = _make_group('g0_3', parent_id=g0) + g0_3_r1 = _make_repo('g0/g0_3/g0_3_r1', repos_group=g0_3) + return test_u1 + + +def expected_count(group_name, objects=False): + repos_group = RepoGroup.get_by_group_name(group_name=group_name) + objs = repos_group.recursive_groups_and_repos() + if objects: + return objs + return len(objs) + + +def _check_expected_count(items, repo_items, expected): + should_be = len(items + repo_items) + there_are = len(expected) + assert should_be == there_are, ('%s != %s' % ((items + repo_items), expected)) + + +def check_tree_perms(obj_name, repo_perm, prefix, expected_perm): + assert repo_perm == expected_perm, ('obj:`%s` got perm:`%s` should:`%s`' + % (obj_name, repo_perm, expected_perm)) + + +def _get_perms(filter_='', recursive=True, key=None, test_u1_id=None): + test_u1 = AuthUser(user_id=test_u1_id) + for k, v in test_u1.permissions[key].items(): + if recursive and k.startswith(filter_): + yield k, v + elif not recursive: + if k == filter_: + yield k, v
--- a/rhodecode/tests/models/test_permissions.py Mon Sep 03 22:22:58 2012 +0200 +++ b/rhodecode/tests/models/test_permissions.py Fri Sep 07 23:20:33 2012 +0200 @@ -1,7 +1,7 @@ import os import unittest from rhodecode.tests import * - +from rhodecode.tests.models.common import _make_group from rhodecode.model.repos_group import ReposGroupModel from rhodecode.model.repo import RepoModel from rhodecode.model.db import RepoGroup, User, UsersGroupRepoGroupToPerm @@ -12,16 +12,6 @@ from rhodecode.lib.auth import AuthUser -def _make_group(path, desc='desc', parent_id=None, - skip_if_exists=False): - - gr = RepoGroup.get_by_group_name(path) - if gr and skip_if_exists: - return gr - - gr = ReposGroupModel().create(path, desc, parent_id) - return gr - class TestPermissions(unittest.TestCase): def __init__(self, methodName='runTest'): @@ -435,4 +425,3 @@ set(['hg.create.repository', 'hg.fork.repository', 'hg.register.manual_activate', 'repository.read'])) -
--- a/rhodecode/tests/models/test_repos_groups.py Mon Sep 03 22:22:58 2012 +0200 +++ b/rhodecode/tests/models/test_repos_groups.py Fri Sep 07 23:20:33 2012 +0200 @@ -4,7 +4,7 @@ from rhodecode.model.repos_group import ReposGroupModel from rhodecode.model.repo import RepoModel -from rhodecode.model.db import RepoGroup, User +from rhodecode.model.db import RepoGroup, User, Repository from rhodecode.model.meta import Session from sqlalchemy.exc import IntegrityError @@ -15,7 +15,8 @@ gr = RepoGroup.get_by_group_name(path) if gr and skip_if_exists: return gr - + if isinstance(parent_id, RepoGroup): + parent_id = parent_id.group_id gr = ReposGroupModel().create(path, desc, parent_id) return gr @@ -54,7 +55,8 @@ group_parent_id=parent_id, perms_updates=[], perms_new=[], - enable_locking=False + enable_locking=False, + recursive=False ) gr = ReposGroupModel().update(id_, form_data) return gr @@ -132,7 +134,8 @@ repo_type='hg', clone_uri=None, landing_rev='tip', - enable_locking=False) + enable_locking=False, + recursive=False) cur_user = User.get_by_username(TEST_USER_ADMIN_LOGIN) r = RepoModel().create(form_data, cur_user)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rhodecode/tests/models/test_user_permissions_on_groups.py Fri Sep 07 23:20:33 2012 +0200 @@ -0,0 +1,161 @@ +import os +import unittest +import functools +from rhodecode.tests import * + +from rhodecode.model.repos_group import ReposGroupModel +from rhodecode.model.db import RepoGroup, Repository, User + +from rhodecode.model.meta import Session +from nose.tools import with_setup +from rhodecode.tests.models.common import _create_project_tree, check_tree_perms, \ + _get_perms, _check_expected_count, expected_count, _destroy_project_tree +from rhodecode.model.repo import RepoModel + + +test_u1_id = None +_get_repo_perms = None +_get_group_perms = None + + +def permissions_setup_func(group_name='g0', perm='group.read', recursive=True): + """ + Resets all permissions to perm attribute + """ + repos_group = RepoGroup.get_by_group_name(group_name=group_name) + if not repos_group: + raise Exception('Cannot get group %s' % group_name) + perms_updates = [[test_u1_id, perm, 'user']] + ReposGroupModel()._update_permissions(repos_group, + perms_updates=perms_updates, + recursive=recursive) + Session().commit() + + +def setup_module(): + global test_u1_id, _get_repo_perms, _get_group_perms + test_u1 = _create_project_tree() + Session().commit() + test_u1_id = test_u1.user_id + _get_repo_perms = functools.partial(_get_perms, key='repositories', + test_u1_id=test_u1_id) + _get_group_perms = functools.partial(_get_perms, key='repositories_groups', + test_u1_id=test_u1_id) + + +def teardown_module(): + _destroy_project_tree(test_u1_id) + + +@with_setup(permissions_setup_func) +def test_user_permissions_on_group_without_recursive_mode(): + # set permission to g0 non-recursive mode + recursive = False + group = 'g0' + permissions_setup_func(group, 'group.write', recursive=recursive) + + items = [x for x in _get_repo_perms(group, recursive)] + expected = 0 + assert len(items) == expected, ' %s != %s' % (len(items), expected) + for name, perm in items: + yield check_tree_perms, name, perm, group, 'repository.read' + + items = [x for x in _get_group_perms(group, recursive)] + expected = 1 + assert len(items) == expected, ' %s != %s' % (len(items), expected) + for name, perm in items: + yield check_tree_perms, name, perm, group, 'group.write' + + +@with_setup(permissions_setup_func) +def test_user_permissions_on_group_without_recursive_mode_subgroup(): + # set permission to g0 non-recursive mode + recursive = False + group = 'g0/g0_1' + permissions_setup_func(group, 'group.write', recursive=recursive) + + items = [x for x in _get_repo_perms(group, recursive)] + expected = 0 + assert len(items) == expected, ' %s != %s' % (len(items), expected) + for name, perm in items: + yield check_tree_perms, name, perm, group, 'repository.read' + + items = [x for x in _get_group_perms(group, recursive)] + expected = 1 + assert len(items) == expected, ' %s != %s' % (len(items), expected) + for name, perm in items: + yield check_tree_perms, name, perm, group, 'group.write' + + +@with_setup(permissions_setup_func) +def test_user_permissions_on_group_with_recursive_mode(): + + # set permission to g0 recursive mode, all children including + # other repos and groups should have this permission now set ! + recursive = True + group = 'g0' + permissions_setup_func(group, 'group.write', recursive=recursive) + + repo_items = [x for x in _get_repo_perms(group, recursive)] + items = [x for x in _get_group_perms(group, recursive)] + _check_expected_count(items, repo_items, expected_count(group, True)) + + for name, perm in repo_items: + yield check_tree_perms, name, perm, group, 'repository.write' + + for name, perm in items: + yield check_tree_perms, name, perm, group, 'group.write' + + +@with_setup(permissions_setup_func) +def test_user_permissions_on_group_with_recursive_mode_inner_group(): + ## set permission to g0_3 group to none + recursive = True + group = 'g0/g0_3' + permissions_setup_func(group, 'group.none', recursive=recursive) + + repo_items = [x for x in _get_repo_perms(group, recursive)] + items = [x for x in _get_group_perms(group, recursive)] + _check_expected_count(items, repo_items, expected_count(group, True)) + + for name, perm in repo_items: + yield check_tree_perms, name, perm, group, 'repository.none' + + for name, perm in items: + yield check_tree_perms, name, perm, group, 'group.none' + + +@with_setup(permissions_setup_func) +def test_user_permissions_on_group_with_recursive_mode_deepest(): + ## set permission to g0_3 group to none + recursive = True + group = 'g0/g0_1/g0_1_1' + permissions_setup_func(group, 'group.write', recursive=recursive) + + repo_items = [x for x in _get_repo_perms(group, recursive)] + items = [x for x in _get_group_perms(group, recursive)] + _check_expected_count(items, repo_items, expected_count(group, True)) + + for name, perm in repo_items: + yield check_tree_perms, name, perm, group, 'repository.write' + + for name, perm in items: + yield check_tree_perms, name, perm, group, 'group.write' + + +@with_setup(permissions_setup_func) +def test_user_permissions_on_group_with_recursive_mode_only_with_repos(): + ## set permission to g0_3 group to none + recursive = True + group = 'g0/g0_2' + permissions_setup_func(group, 'group.admin', recursive=recursive) + + repo_items = [x for x in _get_repo_perms(group, recursive)] + items = [x for x in _get_group_perms(group, recursive)] + _check_expected_count(items, repo_items, expected_count(group, True)) + + for name, perm in repo_items: + yield check_tree_perms, name, perm, group, 'repository.admin' + + for name, perm in items: + yield check_tree_perms, name, perm, group, 'group.admin'
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rhodecode/tests/models/test_users_group_permissions_on_groups.py Fri Sep 07 23:20:33 2012 +0200 @@ -0,0 +1,170 @@ +import os +import unittest +import functools +from rhodecode.tests import * + +from rhodecode.model.repos_group import ReposGroupModel +from rhodecode.model.db import RepoGroup, Repository, User + +from rhodecode.model.meta import Session +from nose.tools import with_setup +from rhodecode.tests.models.common import _create_project_tree, check_tree_perms, \ + _get_perms, _check_expected_count, expected_count, _destroy_project_tree +from rhodecode.model.users_group import UsersGroupModel +from rhodecode.model.repo import RepoModel + + +test_u2_id = None +test_u2_gr_id = None +_get_repo_perms = None +_get_group_perms = None + + +def permissions_setup_func(group_name='g0', perm='group.read', recursive=True): + """ + Resets all permissions to perm attribute + """ + repos_group = RepoGroup.get_by_group_name(group_name=group_name) + if not repos_group: + raise Exception('Cannot get group %s' % group_name) + perms_updates = [[test_u2_gr_id, perm, 'users_group']] + ReposGroupModel()._update_permissions(repos_group, + perms_updates=perms_updates, + recursive=recursive) + Session().commit() + + +def setup_module(): + global test_u2_id, test_u2_gr_id, _get_repo_perms, _get_group_perms + test_u2 = _create_project_tree() + Session().commit() + test_u2_id = test_u2.user_id + + gr1 = UsersGroupModel().create(name='perms_group_1') + Session().commit() + test_u2_gr_id = gr1.users_group_id + UsersGroupModel().add_user_to_group(gr1, user=test_u2_id) + Session().commit() + + _get_repo_perms = functools.partial(_get_perms, key='repositories', + test_u1_id=test_u2_id) + _get_group_perms = functools.partial(_get_perms, key='repositories_groups', + test_u1_id=test_u2_id) + + +def teardown_module(): + _destroy_project_tree(test_u2_id) + + +@with_setup(permissions_setup_func) +def test_user_permissions_on_group_without_recursive_mode(): + # set permission to g0 non-recursive mode + recursive = False + group = 'g0' + permissions_setup_func(group, 'group.write', recursive=recursive) + + items = [x for x in _get_repo_perms(group, recursive)] + expected = 0 + assert len(items) == expected, ' %s != %s' % (len(items), expected) + for name, perm in items: + yield check_tree_perms, name, perm, group, 'repository.read' + + items = [x for x in _get_group_perms(group, recursive)] + expected = 1 + assert len(items) == expected, ' %s != %s' % (len(items), expected) + for name, perm in items: + yield check_tree_perms, name, perm, group, 'group.write' + + +@with_setup(permissions_setup_func) +def test_user_permissions_on_group_without_recursive_mode_subgroup(): + # set permission to g0 non-recursive mode + recursive = False + group = 'g0/g0_1' + permissions_setup_func(group, 'group.write', recursive=recursive) + + items = [x for x in _get_repo_perms(group, recursive)] + expected = 0 + assert len(items) == expected, ' %s != %s' % (len(items), expected) + for name, perm in items: + yield check_tree_perms, name, perm, group, 'repository.read' + + items = [x for x in _get_group_perms(group, recursive)] + expected = 1 + assert len(items) == expected, ' %s != %s' % (len(items), expected) + for name, perm in items: + yield check_tree_perms, name, perm, group, 'group.write' + + +@with_setup(permissions_setup_func) +def test_user_permissions_on_group_with_recursive_mode(): + + # set permission to g0 recursive mode, all children including + # other repos and groups should have this permission now set ! + recursive = True + group = 'g0' + permissions_setup_func(group, 'group.write', recursive=recursive) + + repo_items = [x for x in _get_repo_perms(group, recursive)] + items = [x for x in _get_group_perms(group, recursive)] + _check_expected_count(items, repo_items, expected_count(group, True)) + + for name, perm in repo_items: + yield check_tree_perms, name, perm, group, 'repository.write' + + for name, perm in items: + yield check_tree_perms, name, perm, group, 'group.write' + + +@with_setup(permissions_setup_func) +def test_user_permissions_on_group_with_recursive_mode_inner_group(): + ## set permission to g0_3 group to none + recursive = True + group = 'g0/g0_3' + permissions_setup_func(group, 'group.none', recursive=recursive) + + repo_items = [x for x in _get_repo_perms(group, recursive)] + items = [x for x in _get_group_perms(group, recursive)] + _check_expected_count(items, repo_items, expected_count(group, True)) + + for name, perm in repo_items: + yield check_tree_perms, name, perm, group, 'repository.none' + + for name, perm in items: + yield check_tree_perms, name, perm, group, 'group.none' + + +@with_setup(permissions_setup_func) +def test_user_permissions_on_group_with_recursive_mode_deepest(): + ## set permission to g0_3 group to none + recursive = True + group = 'g0/g0_1/g0_1_1' + permissions_setup_func(group, 'group.write', recursive=recursive) + + repo_items = [x for x in _get_repo_perms(group, recursive)] + items = [x for x in _get_group_perms(group, recursive)] + _check_expected_count(items, repo_items, expected_count(group, True)) + + for name, perm in repo_items: + yield check_tree_perms, name, perm, group, 'repository.write' + + for name, perm in items: + yield check_tree_perms, name, perm, group, 'group.write' + + +@with_setup(permissions_setup_func) +def test_user_permissions_on_group_with_recursive_mode_only_with_repos(): + ## set permission to g0_3 group to none + recursive = True + group = 'g0/g0_2' + permissions_setup_func(group, 'group.admin', recursive=recursive) + + repo_items = [x for x in _get_repo_perms(group, recursive)] + items = [x for x in _get_group_perms(group, recursive)] + _check_expected_count(items, repo_items, expected_count(group, True)) + + for name, perm in repo_items: + yield check_tree_perms, name, perm, group, 'repository.admin' + + for name, perm in items: + yield check_tree_perms, name, perm, group, 'group.admin'
--- a/rhodecode/tests/scripts/create_rc.sh Mon Sep 03 22:22:58 2012 +0200 +++ b/rhodecode/tests/scripts/create_rc.sh Fri Sep 07 23:20:33 2012 +0200 @@ -3,10 +3,14 @@ paster setup-rhodecode rc.ini -q --user=marcink --password=qweqwe --email=marcin@python-blog.com --repos=/home/marcink/repos API_KEY=`psql -R " " -A -U postgres -h localhost -c "select api_key from users where admin=TRUE" -d rhodecode | awk '{print $2}'` echo "run those after running server" -echo "rhodecode-api --apikey=$API_KEY --apihost=http://127.0.0.1:5001 create_user username:demo1 password:qweqwe email:demo1@rhodecode.org" -echo "rhodecode-api --apikey=$API_KEY --apihost=http://127.0.0.1:5001 create_user username:demo2 password:qweqwe email:demo2@rhodecode.org" -echo "rhodecode-api --apikey=$API_KEY --apihost=http://127.0.0.1:5001 create_user username:demo3 password:qweqwe email:demo3@rhodecode.org" -echo "rhodecode-api --apikey=$API_KEY --apihost=http://127.0.0.1:5001 create_users_group group_name:demo12" -echo "rhodecode-api --apikey=$API_KEY --apihost=http://127.0.0.1:5001 add_user_to_users_group usersgroupid:demo12 userid:demo1" -echo "rhodecode-api --apikey=$API_KEY --apihost=http://127.0.0.1:5001 add_user_to_users_group usersgroupid:demo12 userid:demo2" - +paster serve rc.ini --pid-file=rc.pid --daemon +sleep 3 +rhodecode-api --apikey=$API_KEY --apihost=http://127.0.0.1:5001 create_user username:demo1 password:qweqwe email:demo1@rhodecode.org +rhodecode-api --apikey=$API_KEY --apihost=http://127.0.0.1:5001 create_user username:demo2 password:qweqwe email:demo2@rhodecode.org +rhodecode-api --apikey=$API_KEY --apihost=http://127.0.0.1:5001 create_user username:demo3 password:qweqwe email:demo3@rhodecode.org +rhodecode-api --apikey=$API_KEY --apihost=http://127.0.0.1:5001 create_users_group group_name:demo12 +rhodecode-api --apikey=$API_KEY --apihost=http://127.0.0.1:5001 add_user_to_users_group usersgroupid:demo12 userid:demo1 +rhodecode-api --apikey=$API_KEY --apihost=http://127.0.0.1:5001 add_user_to_users_group usersgroupid:demo12 userid:demo2 +echo "killing server" +kill `cat rc.pid` +rm rc.pid