##// END OF EJS Templates
Merge with beta
marcink -
r1564:752b0a7b merge default
parent child Browse files
Show More
@@ -0,0 +1,132 b''
1 #!/bin/sh
2 ########################################
3 #### THIS IS A REDHAT INIT.D SCRIPT ####
4 ########################################
5
6 ##################################################
7 #
8 # RhodeCode server startup script
9 # Recommended default-startup: 2 3 4 5
10 # Recommended default-stop: 0 1 6
11 #
12 ##################################################
13
14
15 APP_NAME="rhodecode"
16 # the location of your app
17 # since this is a web app, it should go in /var/www
18 APP_PATH="/var/www/$APP_NAME"
19
20 CONF_NAME="production.ini"
21
22 # write to wherever the PID should be stored, just ensure
23 # that the user you run paster as has the appropriate permissions
24 # same goes for the log file
25 PID_PATH="/var/run/rhodecode/pid"
26 LOG_PATH="/var/log/rhodecode/rhodecode.log"
27
28 # replace this with the path to the virtual environment you
29 # made for RhodeCode
30 PYTHON_PATH="/opt/python_virtualenvironments/rhodecode-venv"
31
32 RUN_AS="rhodecode"
33
34 DAEMON="$PYTHON_PATH/bin/paster"
35
36 DAEMON_OPTS="serve --daemon \
37 --user=$RUN_AS \
38 --group=$RUN_AS \
39 --pid-file=$PID_PATH \
40 --log-file=$LOG_PATH $APP_PATH/$CONF_NAME"
41
42 DESC="rhodecode-server"
43 LOCK_FILE="/var/lock/subsys/$APP_NAME"
44
45 # source CentOS init functions
46 . /etc/init.d/functions
47
48 RETVAL=0
49
50 remove_pid () {
51 rm -f ${PID_PATH}
52 rmdir `dirname ${PID_PATH}`
53 }
54
55 ensure_pid_dir () {
56 PID_DIR=`dirname ${PID_PATH}`
57 if [ ! -d ${PID_DIR} ] ; then
58 mkdir -p ${PID_DIR}
59 chown -R ${RUN_AS}:${RUN_AS} ${PID_DIR}
60 chmod 755 ${PID_DIR}
61 fi
62 }
63
64 start_rhodecode () {
65 ensure_pid_dir
66 PYTHON_EGG_CACHE="/tmp" daemon --pidfile $PID_PATH \
67 --user $RUN_AS "$DAEMON $DAEMON_OPTS"
68 RETVAL=$?
69 [ $RETVAL -eq 0 ] && touch $LOCK_FILE
70 return $RETVAL
71 }
72
73 stop_rhodecode () {
74 if [ -e $LOCK_FILE ]; then
75 killproc -p $PID_PATH
76 RETVAL=$?
77 rm -f $LOCK_FILE
78 rm -f $PID_PATH
79 else
80 RETVAL=1
81 fi
82 return $RETVAL
83 }
84
85 status_rhodecode() {
86 if [ -e $LOCK_FILE ]; then
87 # exit with non-zero to indicate failure
88 RETVAL=1
89 else
90 RETVAL=0
91 fi
92 return $RETVAL
93 }
94
95 restart_rhodecode () {
96 stop_rhodecode
97 start_rhodecode
98 RETVAL=$?
99 }
100
101 case "$1" in
102 start)
103 echo -n $"Starting $DESC: "
104 start_rhodecode
105 echo
106 ;;
107 stop)
108 echo -n $"Stopping $DESC: "
109 stop_rhodecode
110 echo
111 ;;
112 status)
113 status_rhodecode
114 RETVAL=$?
115 if [ ! $RETVAL -eq 0 ]; then
116 echo "RhodeCode server is running..."
117 else
118 echo "RhodeCode server is stopped."
119 fi
120 ;;
121 restart)
122 echo -n $"Restarting $DESC: "
123 restart_rhodecode
124 echo
125 ;;
126 *)
127 echo $"Usage: $0 {start|stop|restart|status}"
128 RETVAL=1
129 ;;
130 esac
131
132 exit $RETVAL No newline at end of file
@@ -0,0 +1,189 b''
1 # -*- coding: utf-8 -*-
2 """
3 rhodecode.tests.test_hg_operations
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
6 Test suite for making push/pull operations
7
8 :created_on: Dec 30, 2010
9 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
10 :license: GPLv3, see COPYING for more details.
11 """
12 # This program is free software: you can redistribute it and/or modify
13 # it under the terms of the GNU General Public License as published by
14 # the Free Software Foundation, either version 3 of the License, or
15 # (at your option) any later version.
16 #
17 # This program is distributed in the hope that it will be useful,
18 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # GNU General Public License for more details.
21 #
22 # You should have received a copy of the GNU General Public License
23 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24
25 import os
26 import sys
27 import shutil
28 import logging
29 from os.path import join as jn
30 from os.path import dirname as dn
31
32 from tempfile import _RandomNameSequence
33 from subprocess import Popen, PIPE
34
35 from paste.deploy import appconfig
36 from pylons import config
37 from sqlalchemy import engine_from_config
38
39 from rhodecode.lib.utils import add_cache
40 from rhodecode.model import init_model
41 from rhodecode.model import meta
42 from rhodecode.model.db import User, Repository
43 from rhodecode.lib.auth import get_crypt_password
44
45 from rhodecode.tests import TESTS_TMP_PATH, NEW_HG_REPO, HG_REPO
46 from rhodecode.config.environment import load_environment
47
48 rel_path = dn(dn(dn(os.path.abspath(__file__))))
49 conf = appconfig('config:development.ini', relative_to=rel_path)
50 load_environment(conf.global_conf, conf.local_conf)
51
52 add_cache(conf)
53
54 USER = 'test_admin'
55 PASS = 'test12'
56 HOST = '127.0.0.1:5000'
57 DEBUG = True
58 log = logging.getLogger(__name__)
59
60
61 class Command(object):
62
63 def __init__(self, cwd):
64 self.cwd = cwd
65
66 def execute(self, cmd, *args):
67 """Runs command on the system with given ``args``.
68 """
69
70 command = cmd + ' ' + ' '.join(args)
71 log.debug('Executing %s' % command)
72 if DEBUG:
73 print command
74 p = Popen(command, shell=True, stdout=PIPE, stderr=PIPE, cwd=self.cwd)
75 stdout, stderr = p.communicate()
76 if DEBUG:
77 print stdout, stderr
78 return stdout, stderr
79
80 def get_session():
81 engine = engine_from_config(conf, 'sqlalchemy.db1.')
82 init_model(engine)
83 sa = meta.Session()
84 return sa
85
86
87 def create_test_user(force=True):
88 print 'creating test user'
89 sa = get_session()
90
91 user = sa.query(User).filter(User.username == USER).scalar()
92
93 if force and user is not None:
94 print 'removing current user'
95 for repo in sa.query(Repository).filter(Repository.user == user).all():
96 sa.delete(repo)
97 sa.delete(user)
98 sa.commit()
99
100 if user is None or force:
101 print 'creating new one'
102 new_usr = User()
103 new_usr.username = USER
104 new_usr.password = get_crypt_password(PASS)
105 new_usr.email = 'mail@mail.com'
106 new_usr.name = 'test'
107 new_usr.lastname = 'lasttestname'
108 new_usr.active = True
109 new_usr.admin = True
110 sa.add(new_usr)
111 sa.commit()
112
113 print 'done'
114
115
116 def create_test_repo(force=True):
117 print 'creating test repo'
118 from rhodecode.model.repo import RepoModel
119 sa = get_session()
120
121 user = sa.query(User).filter(User.username == USER).scalar()
122 if user is None:
123 raise Exception('user not found')
124
125
126 repo = sa.query(Repository).filter(Repository.repo_name == HG_REPO).scalar()
127
128 if repo is None:
129 print 'repo not found creating'
130
131 form_data = {'repo_name':HG_REPO,
132 'repo_type':'hg',
133 'private':False,
134 'clone_uri':'' }
135 rm = RepoModel(sa)
136 rm.base_path = '/home/hg'
137 rm.create(form_data, user)
138
139 print 'done'
140
141 def set_anonymous_access(enable=True):
142 sa = get_session()
143 user = sa.query(User).filter(User.username == 'default').one()
144 user.active = enable
145 sa.add(user)
146 sa.commit()
147
148 def get_anonymous_access():
149 sa = get_session()
150 return sa.query(User).filter(User.username == 'default').one().active
151
152
153 #==============================================================================
154 # TESTS
155 #==============================================================================
156 def test_clone_with_credentials(no_errors=False, repo=HG_REPO):
157 cwd = path = jn(TESTS_TMP_PATH, repo)
158
159
160 try:
161 shutil.rmtree(path, ignore_errors=True)
162 os.makedirs(path)
163 #print 'made dirs %s' % jn(path)
164 except OSError:
165 raise
166
167
168 clone_url = 'http://%(user)s:%(pass)s@%(host)s/%(cloned_repo)s %(dest)s' % \
169 {'user':USER,
170 'pass':PASS,
171 'host':HOST,
172 'cloned_repo':repo,
173 'dest':path + _RandomNameSequence().next()}
174
175 stdout, stderr = Command(cwd).execute('hg clone', clone_url)
176
177 if no_errors is False:
178 assert """adding file changes""" in stdout, 'no messages about cloning'
179 assert """abort""" not in stderr , 'got error from clone'
180
181 if __name__ == '__main__':
182 try:
183 create_test_user(force=False)
184
185 for i in range(int(sys.argv[2])):
186 test_clone_with_credentials(repo=sys.argv[1])
187
188 except Exception, e:
189 sys.exit('stop on %s' % e)
@@ -12,4 +12,4 b' List of contributors to RhodeCode projec'
12 Augosto Hermann <augusto.herrmann@planejamento.gov.br>
12 Augosto Hermann <augusto.herrmann@planejamento.gov.br>
13 Ankit Solanki <ankit.solanki@gmail.com>
13 Ankit Solanki <ankit.solanki@gmail.com>
14 Liad Shani <liadff@gmail.com>
14 Liad Shani <liadff@gmail.com>
15 No newline at end of file
15 Les Peabody <lpeabody@gmail.com>
@@ -3,6 +3,30 b''
3 Changelog
3 Changelog
4 =========
4 =========
5
5
6
7 1.2.2 (**2011-10-17**)
8 ======================
9
10 news
11 ----
12
13 - #226 repo groups are available by path instead of numerical id
14
15 fixes
16 -----
17
18 - #259 Groups with the same name but with different parent group
19 - #260 Put repo in group, then move group to another group -> repo becomes unavailable
20 - #258 RhodeCode 1.2 assumes egg folder is writable (lockfiles problems)
21 - #265 ldap save fails sometimes on converting attributes to booleans,
22 added getter and setter into model that will prevent from this on db model level
23 - fixed problems with timestamps issues #251 and #213
24 - fixes #266 Rhodecode allows to create repo with the same name and in
25 the same parent as group
26 - fixes #245 Rescan of the repositories on Windows
27 - fixes #248 cannot edit repos inside a group on windows
28 - fixes #219 forking problems on windows
29
6 1.2.1 (**2011-10-08**)
30 1.2.1 (**2011-10-08**)
7 ======================
31 ======================
8
32
@@ -17,11 +41,9 b' fixes'
17 - gui fixes
41 - gui fixes
18 - fixed logger
42 - fixed logger
19
43
20
21 1.2.0 (**2011-10-07**)
44 1.2.0 (**2011-10-07**)
22 ======================
45 ======================
23
46
24
25 news
47 news
26 ----
48 ----
27
49
@@ -443,8 +443,8 b' in the production.ini file::'
443 In order to not have the statics served by the application. This improves speed.
443 In order to not have the statics served by the application. This improves speed.
444
444
445
445
446 Apache virtual host example
446 Apache virtual host reverse proxy example
447 ---------------------------
447 -----------------------------------------
448
448
449 Here is a sample configuration file for apache using proxy::
449 Here is a sample configuration file for apache using proxy::
450
450
@@ -503,6 +503,31 b' then change <someprefix> into your choos'
503 Apache's WSGI config
503 Apache's WSGI config
504 --------------------
504 --------------------
505
505
506 Alternatively, RhodeCode can be set up with Apache under mod_wsgi. For
507 that, you'll need to:
508
509 - Install mod_wsgi. If using a Debian-based distro, you can install
510 the package libapache2-mod-wsgi::
511
512 aptitude install libapache2-mod-wsgi
513
514 - Enable mod_wsgi::
515
516 a2enmod wsgi
517
518 - Create a wsgi dispatch script, like the one below. Make sure you
519 check the paths correctly point to where you installed RhodeCode
520 and its Python Virtual Environment.
521 - Enable the WSGIScriptAlias directive for the wsgi dispatch script,
522 as in the following example. Once again, check the paths are
523 correctly specified.
524
525 Here is a sample excerpt from an Apache Virtual Host configuration file::
526
527 WSGIDaemonProcess pylons user=www-data group=www-data processes=1 \
528 threads=4 \
529 python-path=/home/web/rhodecode/pyenv/lib/python2.6/site-packages
530 WSGIScriptAlias / /home/web/rhodecode/dispatch.wsgi
506
531
507 Example wsgi dispatch script::
532 Example wsgi dispatch script::
508
533
@@ -512,6 +537,9 b' Example wsgi dispatch script::'
512
537
513 # sometimes it's needed to set the curent dir
538 # sometimes it's needed to set the curent dir
514 os.chdir('/home/web/rhodecode/')
539 os.chdir('/home/web/rhodecode/')
540
541 import site
542 site.addsitedir("/home/web/rhodecode/pyenv/lib/python2.6/site-packages")
515
543
516 from paste.deploy import loadapp
544 from paste.deploy import loadapp
517 from paste.script.util.logging_config import fileConfig
545 from paste.script.util.logging_config import fileConfig
@@ -519,6 +547,10 b' Example wsgi dispatch script::'
519 fileConfig('/home/web/rhodecode/production.ini')
547 fileConfig('/home/web/rhodecode/production.ini')
520 application = loadapp('config:/home/web/rhodecode/production.ini')
548 application = loadapp('config:/home/web/rhodecode/production.ini')
521
549
550 Note: when using mod_wsgi you'll need to install the same version of
551 Mercurial that's inside RhodeCode's virtualenv also on the system's Python
552 environment.
553
522
554
523 Other configuration files
555 Other configuration files
524 -------------------------
556 -------------------------
@@ -25,7 +25,7 b''
25 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 # along with this program. If not, see <http://www.gnu.org/licenses/>.
26 import platform
26 import platform
27
27
28 VERSION = (1, 2, 1)
28 VERSION = (1, 2, 2)
29 __version__ = '.'.join((str(each) for each in VERSION[:4]))
29 __version__ = '.'.join((str(each) for each in VERSION[:4]))
30 __dbversion__ = 3 #defines current db version for migrations
30 __dbversion__ = 3 #defines current db version for migrations
31 __platform__ = platform.system()
31 __platform__ = platform.system()
@@ -35,7 +35,7 b" PLATFORM_WIN = ('Windows')"
35 PLATFORM_OTHERS = ('Linux', 'Darwin', 'FreeBSD', 'OpenBSD', 'SunOS')
35 PLATFORM_OTHERS = ('Linux', 'Darwin', 'FreeBSD', 'OpenBSD', 'SunOS')
36
36
37 try:
37 try:
38 from rhodecode.lib.utils import get_current_revision
38 from rhodecode.lib import get_current_revision
39 _rev = get_current_revision()
39 _rev = get_current_revision()
40 except ImportError:
40 except ImportError:
41 #this is needed when doing some setup.py operations
41 #this is needed when doing some setup.py operations
@@ -19,10 +19,10 b' def make_map(config):'
19 always_scan=config['debug'])
19 always_scan=config['debug'])
20 rmap.minimization = False
20 rmap.minimization = False
21 rmap.explicit = False
21 rmap.explicit = False
22
22
23 from rhodecode.lib.utils import is_valid_repo
23 from rhodecode.lib.utils import is_valid_repo
24 from rhodecode.lib.utils import is_valid_repos_group
24 from rhodecode.lib.utils import is_valid_repos_group
25
25
26 def check_repo(environ, match_dict):
26 def check_repo(environ, match_dict):
27 """
27 """
28 check for valid repository for proper 404 handling
28 check for valid repository for proper 404 handling
@@ -30,7 +30,7 b' def make_map(config):'
30 :param environ:
30 :param environ:
31 :param match_dict:
31 :param match_dict:
32 """
32 """
33
33
34 repo_name = match_dict.get('repo_name')
34 repo_name = match_dict.get('repo_name')
35 return is_valid_repo(repo_name, config['base_path'])
35 return is_valid_repo(repo_name, config['base_path'])
36
36
@@ -42,7 +42,7 b' def make_map(config):'
42 :param match_dict:
42 :param match_dict:
43 """
43 """
44 repos_group_name = match_dict.get('group_name')
44 repos_group_name = match_dict.get('group_name')
45
45
46 return is_valid_repos_group(repos_group_name, config['base_path'])
46 return is_valid_repos_group(repos_group_name, config['base_path'])
47
47
48
48
@@ -333,13 +333,13 b' def make_map(config):'
333 # REPOSITORY ROUTES
333 # REPOSITORY ROUTES
334 #==========================================================================
334 #==========================================================================
335 rmap.connect('summary_home', '/{repo_name:.*}',
335 rmap.connect('summary_home', '/{repo_name:.*}',
336 controller='summary',
336 controller='summary',
337 conditions=dict(function=check_repo))
337 conditions=dict(function=check_repo))
338
338
339 # rmap.connect('repo_group_home', '/{group_name:.*}',
339 rmap.connect('repos_group_home', '/{group_name:.*}',
340 # controller='admin/repos_groups',action="show_by_name",
340 controller='admin/repos_groups', action="show_by_name",
341 # conditions=dict(function=check_group))
341 conditions=dict(function=check_group))
342
342
343 rmap.connect('changeset_home', '/{repo_name:.*}/changeset/{revision}',
343 rmap.connect('changeset_home', '/{repo_name:.*}/changeset/{revision}',
344 controller='changeset', revision='tip',
344 controller='changeset', revision='tip',
345 conditions=dict(function=check_repo))
345 conditions=dict(function=check_repo))
@@ -144,7 +144,7 b' class PermissionsController(BaseControll'
144 c.create_choices = self.create_choices
144 c.create_choices = self.create_choices
145
145
146 if id == 'default':
146 if id == 'default':
147 default_user = User.by_username('default')
147 default_user = User.get_by_username('default')
148 defaults = {'_method': 'put',
148 defaults = {'_method': 'put',
149 'anonymous': default_user.active}
149 'anonymous': default_user.active}
150
150
@@ -64,20 +64,10 b' class ReposController(BaseController):'
64 super(ReposController, self).__before__()
64 super(ReposController, self).__before__()
65
65
66 def __load_defaults(self):
66 def __load_defaults(self):
67 c.repo_groups = Group.groups_choices()
68 c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups)
69
67 repo_model = RepoModel()
70 repo_model = RepoModel()
68
69 c.repo_groups = [('', '')]
70 parents_link = lambda k: h.literal('&raquo;'.join(
71 map(lambda k: k.group_name,
72 k.parents + [k])
73 )
74 )
75
76 c.repo_groups.extend([(x.group_id, parents_link(x)) for \
77 x in self.sa.query(Group).all()])
78 c.repo_groups = sorted(c.repo_groups,
79 key=lambda t: t[1].split('&raquo;')[0])
80 c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups)
81 c.users_array = repo_model.get_users_js()
71 c.users_array = repo_model.get_users_js()
82 c.users_groups_array = repo_model.get_users_groups_js()
72 c.users_groups_array = repo_model.get_users_groups_js()
83
73
@@ -89,8 +79,8 b' class ReposController(BaseController):'
89 """
79 """
90 self.__load_defaults()
80 self.__load_defaults()
91
81
92 c.repo_info = db_repo = Repository.by_repo_name(repo_name)
82 c.repo_info = db_repo = Repository.get_by_repo_name(repo_name)
93 repo = scm_repo = db_repo.scm_instance
83 repo = db_repo.scm_instance
94
84
95 if c.repo_info is None:
85 if c.repo_info is None:
96 h.flash(_('%s repository is not mapped to db perhaps'
86 h.flash(_('%s repository is not mapped to db perhaps'
@@ -101,7 +91,7 b' class ReposController(BaseController):'
101
91
102 return redirect(url('repos'))
92 return redirect(url('repos'))
103
93
104 c.default_user_id = User.by_username('default').user_id
94 c.default_user_id = User.get_by_username('default').user_id
105 c.in_public_journal = self.sa.query(UserFollowing)\
95 c.in_public_journal = self.sa.query(UserFollowing)\
106 .filter(UserFollowing.user_id == c.default_user_id)\
96 .filter(UserFollowing.user_id == c.default_user_id)\
107 .filter(UserFollowing.follows_repository == c.repo_info).scalar()
97 .filter(UserFollowing.follows_repository == c.repo_info).scalar()
@@ -234,11 +224,11 b' class ReposController(BaseController):'
234 repo_groups=c.repo_groups_choices)()
224 repo_groups=c.repo_groups_choices)()
235 try:
225 try:
236 form_result = _form.to_python(dict(request.POST))
226 form_result = _form.to_python(dict(request.POST))
237 repo_model.update(repo_name, form_result)
227 repo = repo_model.update(repo_name, form_result)
238 invalidate_cache('get_repo_cached_%s' % repo_name)
228 invalidate_cache('get_repo_cached_%s' % repo_name)
239 h.flash(_('Repository %s updated successfully' % repo_name),
229 h.flash(_('Repository %s updated successfully' % repo_name),
240 category='success')
230 category='success')
241 changed_name = form_result['repo_name_full']
231 changed_name = repo.repo_name
242 action_logger(self.rhodecode_user, 'admin_updated_repo',
232 action_logger(self.rhodecode_user, 'admin_updated_repo',
243 changed_name, '', self.sa)
233 changed_name, '', self.sa)
244
234
@@ -381,8 +371,8 b' class ReposController(BaseController):'
381 token = get_token()
371 token = get_token()
382 if cur_token == token:
372 if cur_token == token:
383 try:
373 try:
384 repo_id = Repository.by_repo_name(repo_name).repo_id
374 repo_id = Repository.get_by_repo_name(repo_name).repo_id
385 user_id = User.by_username('default').user_id
375 user_id = User.get_by_username('default').user_id
386 self.scm_model.toggle_following_repo(repo_id, user_id)
376 self.scm_model.toggle_following_repo(repo_id, user_id)
387 h.flash(_('Updated repository visibility in public journal'),
377 h.flash(_('Updated repository visibility in public journal'),
388 category='success')
378 category='success')
@@ -9,9 +9,10 b' from pylons import request, response, se'
9 from pylons.controllers.util import abort, redirect
9 from pylons.controllers.util import abort, redirect
10 from pylons.i18n.translation import _
10 from pylons.i18n.translation import _
11
11
12 from sqlalchemy.exc import IntegrityError
13
12 from rhodecode.lib import helpers as h
14 from rhodecode.lib import helpers as h
13 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator, \
15 from rhodecode.lib.auth import LoginRequired, HasPermissionAnyDecorator
14 HasPermissionAnyDecorator
15 from rhodecode.lib.base import BaseController, render
16 from rhodecode.lib.base import BaseController, render
16 from rhodecode.model.db import Group
17 from rhodecode.model.db import Group
17 from rhodecode.model.repos_group import ReposGroupModel
18 from rhodecode.model.repos_group import ReposGroupModel
@@ -31,19 +32,7 b' class ReposGroupsController(BaseControll'
31 super(ReposGroupsController, self).__before__()
32 super(ReposGroupsController, self).__before__()
32
33
33 def __load_defaults(self):
34 def __load_defaults(self):
34
35 c.repo_groups = Group.groups_choices()
35 c.repo_groups = [('', '')]
36 parents_link = lambda k: h.literal('&raquo;'.join(
37 map(lambda k: k.group_name,
38 k.parents + [k])
39 )
40 )
41
42 c.repo_groups.extend([(x.group_id, parents_link(x)) for \
43 x in self.sa.query(Group).all()])
44
45 c.repo_groups = sorted(c.repo_groups,
46 key=lambda t: t[1].split('&raquo;')[0])
47 c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups)
36 c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups)
48
37
49 def __load_data(self, group_id):
38 def __load_data(self, group_id):
@@ -58,6 +47,8 b' class ReposGroupsController(BaseControll'
58
47
59 data = repo_group.get_dict()
48 data = repo_group.get_dict()
60
49
50 data['group_name'] = repo_group.name
51
61 return data
52 return data
62
53
63 @HasPermissionAnyDecorator('hg.admin')
54 @HasPermissionAnyDecorator('hg.admin')
@@ -169,13 +160,28 b' class ReposGroupsController(BaseControll'
169 repos_group_model.delete(id)
160 repos_group_model.delete(id)
170 h.flash(_('removed repos group %s' % gr.group_name), category='success')
161 h.flash(_('removed repos group %s' % gr.group_name), category='success')
171 #TODO: in future action_logger(, '', '', '', self.sa)
162 #TODO: in future action_logger(, '', '', '', self.sa)
163 except IntegrityError, e:
164 if e.message.find('groups_group_parent_id_fkey'):
165 log.error(traceback.format_exc())
166 h.flash(_('Cannot delete this group it still contains '
167 'subgroups'),
168 category='warning')
169 else:
170 log.error(traceback.format_exc())
171 h.flash(_('error occurred during deletion of repos '
172 'group %s' % gr.group_name), category='error')
173
172 except Exception:
174 except Exception:
173 log.error(traceback.format_exc())
175 log.error(traceback.format_exc())
174 h.flash(_('error occurred during deletion of repos group %s' % gr.group_name),
176 h.flash(_('error occurred during deletion of repos '
175 category='error')
177 'group %s' % gr.group_name), category='error')
176
178
177 return redirect(url('repos_groups'))
179 return redirect(url('repos_groups'))
178
180
181 def show_by_name(self, group_name):
182 id_ = Group.get_by_group_name(group_name).group_id
183 return self.show(id_)
184
179 def show(self, id, format='html'):
185 def show(self, id, format='html'):
180 """GET /repos_groups/id: Show a specific item"""
186 """GET /repos_groups/id: Show a specific item"""
181 # url('repos_group', id=ID)
187 # url('repos_group', id=ID)
@@ -366,17 +366,7 b' class SettingsController(BaseController)'
366 def create_repository(self):
366 def create_repository(self):
367 """GET /_admin/create_repository: Form to create a new item"""
367 """GET /_admin/create_repository: Form to create a new item"""
368
368
369 c.repo_groups = [('', '')]
369 c.repo_groups = Group.groups_choices()
370 parents_link = lambda k: h.literal('&raquo;'.join(
371 map(lambda k: k.group_name,
372 k.parents + [k])
373 )
374 )
375
376 c.repo_groups.extend([(x.group_id, parents_link(x)) for \
377 x in self.sa.query(Group).all()])
378 c.repo_groups = sorted(c.repo_groups,
379 key=lambda t: t[1].split('&raquo;')[0])
380 c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups)
370 c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups)
381
371
382 new_repo = request.GET.get('repo', '')
372 new_repo = request.GET.get('repo', '')
@@ -64,8 +64,7 b' class LoginController(BaseController):'
64 c.form_result = login_form.to_python(dict(request.POST))
64 c.form_result = login_form.to_python(dict(request.POST))
65 #form checks for username/password, now we're authenticated
65 #form checks for username/password, now we're authenticated
66 username = c.form_result['username']
66 username = c.form_result['username']
67 user = User.by_username(username,
67 user = User.get_by_username(username, case_insensitive=True)
68 case_insensitive=True)
69 auth_user = AuthUser(user.user_id)
68 auth_user = AuthUser(user.user_id)
70 auth_user.set_authenticated()
69 auth_user.set_authenticated()
71 session['rhodecode_user'] = auth_user
70 session['rhodecode_user'] = auth_user
@@ -95,8 +94,7 b' class LoginController(BaseController):'
95 def register(self):
94 def register(self):
96 user_model = UserModel()
95 user_model = UserModel()
97 c.auto_active = False
96 c.auto_active = False
98 for perm in user_model.get_by_username('default',
97 for perm in User.get_by_username('default').user_perms:
99 cache=False).user_perms:
100 if perm.permission.permission_name == 'hg.register.auto_activate':
98 if perm.permission.permission_name == 'hg.register.auto_activate':
101 c.auto_active = True
99 c.auto_active = True
102 break
100 break
@@ -23,6 +23,8 b''
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25
25
26 import os
27
26 def __get_lem():
28 def __get_lem():
27 from pygments import lexers
29 from pygments import lexers
28 from string import lower
30 from string import lower
@@ -379,3 +381,24 b' def get_changeset_safe(repo, rev):'
379 from rhodecode.lib.utils import EmptyChangeset
381 from rhodecode.lib.utils import EmptyChangeset
380 cs = EmptyChangeset(requested_revision=rev)
382 cs = EmptyChangeset(requested_revision=rev)
381 return cs
383 return cs
384
385
386 def get_current_revision():
387 """
388 Returns tuple of (number, id) from repository containing this package
389 or None if repository could not be found.
390 """
391
392 try:
393 from vcs import get_repo
394 from vcs.utils.helpers import get_scm
395 from vcs.exceptions import RepositoryError, VCSError
396 repopath = os.path.join(os.path.dirname(__file__), '..', '..')
397 scm = get_scm(repopath)[0]
398 repo = get_repo(path=repopath, alias=scm)
399 tip = repo.get_changeset()
400 return (tip.revision, tip.short_id)
401 except (ImportError, RepositoryError, VCSError), err:
402 print ("Cannot retrieve rhodecode's revision. Original error "
403 "was: %s" % err)
404 return None
@@ -6,8 +6,8 b''
6 authentication and permission libraries
6 authentication and permission libraries
7
7
8 :created_on: Apr 4, 2010
8 :created_on: Apr 4, 2010
9 :copyright: (c) 2010 by marcink.
9 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
10 :license: LICENSE_NAME, see LICENSE_FILE for more details.
10 :license: GPLv3, see COPYING for more details.
11 """
11 """
12 # This program is free software: you can redistribute it and/or modify
12 # This program is free software: you can redistribute it and/or modify
13 # it under the terms of the GNU General Public License as published by
13 # it under the terms of the GNU General Public License as published by
@@ -48,7 +48,7 b' from rhodecode.lib.auth_ldap import Auth'
48
48
49 from rhodecode.model import meta
49 from rhodecode.model import meta
50 from rhodecode.model.user import UserModel
50 from rhodecode.model.user import UserModel
51 from rhodecode.model.db import Permission, RhodeCodeSettings
51 from rhodecode.model.db import Permission, RhodeCodeSettings, User
52
52
53 log = logging.getLogger(__name__)
53 log = logging.getLogger(__name__)
54
54
@@ -151,7 +151,7 b' def authenticate(username, password):'
151 """
151 """
152
152
153 user_model = UserModel()
153 user_model = UserModel()
154 user = user_model.get_by_username(username, cache=False)
154 user = User.get_by_username(username)
155
155
156 log.debug('Authenticating user using RhodeCode account')
156 log.debug('Authenticating user using RhodeCode account')
157 if user is not None and not user.ldap_dn:
157 if user is not None and not user.ldap_dn:
@@ -170,8 +170,7 b' def authenticate(username, password):'
170
170
171 else:
171 else:
172 log.debug('Regular authentication failed')
172 log.debug('Regular authentication failed')
173 user_obj = user_model.get_by_username(username, cache=False,
173 user_obj = User.get_by_username(username, case_insensitive=True)
174 case_insensitive=True)
175
174
176 if user_obj is not None and not user_obj.ldap_dn:
175 if user_obj is not None and not user_obj.ldap_dn:
177 log.debug('this user already exists as non ldap')
176 log.debug('this user already exists as non ldap')
@@ -252,7 +251,7 b' class AuthUser(object):'
252
251
253 def propagate_data(self):
252 def propagate_data(self):
254 user_model = UserModel()
253 user_model = UserModel()
255 self.anonymous_user = user_model.get_by_username('default', cache=True)
254 self.anonymous_user = User.get_by_username('default')
256 if self._api_key and self._api_key != self.anonymous_user.api_key:
255 if self._api_key and self._api_key != self.anonymous_user.api_key:
257 #try go get user by api key
256 #try go get user by api key
258 log.debug('Auth User lookup by API KEY %s', self._api_key)
257 log.debug('Auth User lookup by API KEY %s', self._api_key)
@@ -7,8 +7,8 b''
7 repositories and send it to backup server using RSA key via ssh.
7 repositories and send it to backup server using RSA key via ssh.
8
8
9 :created_on: Feb 28, 2010
9 :created_on: Feb 28, 2010
10 :copyright: (c) 2010 by marcink.
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 :license: LICENSE_NAME, see LICENSE_FILE for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software: you can redistribute it and/or modify
13 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
14 # it under the terms of the GNU General Public License as published by
@@ -67,7 +67,7 b' class BaseRepoController(BaseController)'
67 super(BaseRepoController, self).__before__()
67 super(BaseRepoController, self).__before__()
68 if c.repo_name:
68 if c.repo_name:
69
69
70 c.rhodecode_db_repo = Repository.by_repo_name(c.repo_name)
70 c.rhodecode_db_repo = Repository.get_by_repo_name(c.repo_name)
71 c.rhodecode_repo = c.rhodecode_db_repo.scm_instance
71 c.rhodecode_repo = c.rhodecode_db_repo.scm_instance
72
72
73 if c.rhodecode_repo is None:
73 if c.rhodecode_repo is None:
@@ -94,11 +94,11 b' def __get_lockkey(func, *fargs, **fkwarg'
94 def locked_task(func):
94 def locked_task(func):
95 def __wrapper(func, *fargs, **fkwargs):
95 def __wrapper(func, *fargs, **fkwargs):
96 lockkey = __get_lockkey(func, *fargs, **fkwargs)
96 lockkey = __get_lockkey(func, *fargs, **fkwargs)
97 lockkey_path = dn(dn(dn(os.path.abspath(__file__))))
97 lockkey_path = config['here']
98
98
99 log.info('running task with lockkey %s', lockkey)
99 log.info('running task with lockkey %s', lockkey)
100 try:
100 try:
101 l = DaemonLock(jn(lockkey_path, lockkey))
101 l = DaemonLock(file_=jn(lockkey_path, lockkey))
102 ret = func(*fargs, **fkwargs)
102 ret = func(*fargs, **fkwargs)
103 l.release()
103 l.release()
104 return ret
104 return ret
@@ -97,10 +97,11 b' def get_commits_stats(repo_name, ts_min_'
97
97
98 lockkey = __get_lockkey('get_commits_stats', repo_name, ts_min_y,
98 lockkey = __get_lockkey('get_commits_stats', repo_name, ts_min_y,
99 ts_max_y)
99 ts_max_y)
100 lockkey_path = dn(dn(dn(dn(os.path.abspath(__file__)))))
100 lockkey_path = config['here']
101
101 log.info('running task with lockkey %s', lockkey)
102 log.info('running task with lockkey %s', lockkey)
102 try:
103 try:
103 lock = l = DaemonLock(jn(lockkey_path, lockkey))
104 lock = l = DaemonLock(file_=jn(lockkey_path, lockkey))
104
105
105 #for js data compatibilty cleans the key for person from '
106 #for js data compatibilty cleans the key for person from '
106 akc = lambda k: person(k).replace('"', "")
107 akc = lambda k: person(k).replace('"', "")
@@ -24,6 +24,9 b''
24 # You should have received a copy of the GNU General Public License
24 # You should have received a copy of the GNU General Public License
25 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 # along with this program. If not, see <http://www.gnu.org/licenses/>.
26
26
27 import os
28 from rhodecode import __platform__, PLATFORM_WIN
29
27 #==============================================================================
30 #==============================================================================
28 # json
31 # json
29 #==============================================================================
32 #==============================================================================
@@ -358,3 +361,19 b' class OrderedDict(_odict, dict):'
358 # OrderedSet
361 # OrderedSet
359 #==============================================================================
362 #==============================================================================
360 from sqlalchemy.util import OrderedSet
363 from sqlalchemy.util import OrderedSet
364
365
366 #==============================================================================
367 # kill FUNCTIONS
368 #==============================================================================
369 if __platform__ in PLATFORM_WIN:
370 import ctypes
371
372 def kill(pid, sig):
373 """kill function for Win32"""
374 kernel32 = ctypes.windll.kernel32
375 handle = kernel32.OpenProcess(1, 0, pid)
376 return (0 != kernel32.TerminateProcess(handle, 0))
377
378 else:
379 kill = os.kill
@@ -6,8 +6,8 b''
6 Set of custom exceptions used in RhodeCode
6 Set of custom exceptions used in RhodeCode
7
7
8 :created_on: Nov 17, 2010
8 :created_on: Nov 17, 2010
9 :copyright: (c) 2010 by marcink.
9 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
10 :license: LICENSE_NAME, see LICENSE_FILE for more details.
10 :license: GPLv3, see COPYING for more details.
11 """
11 """
12 # This program is free software: you can redistribute it and/or modify
12 # This program is free software: you can redistribute it and/or modify
13 # it under the terms of the GNU General Public License as published by
13 # it under the terms of the GNU General Public License as published by
@@ -598,12 +598,11 b' def repo_link(groups_and_repos):'
598 return repo_name
598 return repo_name
599 else:
599 else:
600 def make_link(group):
600 def make_link(group):
601 return link_to(group.group_name, url('repos_group',
601 return link_to(group.name, url('repos_group_home',
602 id=group.group_id))
602 group_name=group.group_name))
603 return literal(' &raquo; '.join(map(make_link, groups)) + \
603 return literal(' &raquo; '.join(map(make_link, groups)) + \
604 " &raquo; " + repo_name)
604 " &raquo; " + repo_name)
605
605
606
607 def fancy_file_stats(stats):
606 def fancy_file_stats(stats):
608 """
607 """
609 Displays a fancy two colored bar for number of added/deleted
608 Displays a fancy two colored bar for number of added/deleted
@@ -101,7 +101,7 b' class MakeIndex(BasePasterCommand):'
101 from rhodecode.lib.pidlock import LockHeld, DaemonLock
101 from rhodecode.lib.pidlock import LockHeld, DaemonLock
102 from rhodecode.lib.indexers.daemon import WhooshIndexingDaemon
102 from rhodecode.lib.indexers.daemon import WhooshIndexingDaemon
103 try:
103 try:
104 l = DaemonLock(file=jn(dn(dn(index_location)), 'make_index.lock'))
104 l = DaemonLock(file_=jn(dn(dn(index_location)), 'make_index.lock'))
105 WhooshIndexingDaemon(index_location=index_location,
105 WhooshIndexingDaemon(index_location=index_location,
106 repo_location=repo_location,
106 repo_location=repo_location,
107 repo_list=repo_list)\
107 repo_list=repo_list)\
@@ -262,7 +262,7 b' class SimpleGit(object):'
262 return repo_name
262 return repo_name
263
263
264 def __get_user(self, username):
264 def __get_user(self, username):
265 return User.by_username(username)
265 return User.get_by_username(username)
266
266
267 def __get_action(self, environ):
267 def __get_action(self, environ):
268 """Maps git request commands into a pull or push command.
268 """Maps git request commands into a pull or push command.
@@ -76,7 +76,7 b' class SimpleHg(object):'
76
76
77 # skip passing error to error controller
77 # skip passing error to error controller
78 environ['pylons.status_code_redirect'] = True
78 environ['pylons.status_code_redirect'] = True
79
79
80 #======================================================================
80 #======================================================================
81 # EXTRACT REPOSITORY NAME FROM ENV
81 # EXTRACT REPOSITORY NAME FROM ENV
82 #======================================================================
82 #======================================================================
@@ -90,12 +90,13 b' class SimpleHg(object):'
90 # GET ACTION PULL or PUSH
90 # GET ACTION PULL or PUSH
91 #======================================================================
91 #======================================================================
92 action = self.__get_action(environ)
92 action = self.__get_action(environ)
93
93
94 #======================================================================
94 #======================================================================
95 # CHECK ANONYMOUS PERMISSION
95 # CHECK ANONYMOUS PERMISSION
96 #======================================================================
96 #======================================================================
97 if action in ['pull', 'push']:
97 if action in ['pull', 'push']:
98 anonymous_user = self.__get_user('default')
98 anonymous_user = self.__get_user('default')
99
99 username = anonymous_user.username
100 username = anonymous_user.username
100 anonymous_perm = self.__check_permission(action,
101 anonymous_perm = self.__check_permission(action,
101 anonymous_user,
102 anonymous_user,
@@ -152,13 +153,13 b' class SimpleHg(object):'
152 #======================================================================
153 #======================================================================
153 # MERCURIAL REQUEST HANDLING
154 # MERCURIAL REQUEST HANDLING
154 #======================================================================
155 #======================================================================
155
156
156 repo_path = safe_str(os.path.join(self.basepath, repo_name))
157 repo_path = safe_str(os.path.join(self.basepath, repo_name))
157 log.debug('Repository path is %s' % repo_path)
158 log.debug('Repository path is %s' % repo_path)
158
159
159 baseui = make_ui('db')
160 baseui = make_ui('db')
160 self.__inject_extras(repo_path, baseui, extras)
161 self.__inject_extras(repo_path, baseui, extras)
161
162
162
163
163 # quick check if that dir exists...
164 # quick check if that dir exists...
164 if is_valid_repo(repo_name, self.basepath) is False:
165 if is_valid_repo(repo_name, self.basepath) is False:
@@ -228,7 +229,7 b' class SimpleHg(object):'
228 return repo_name
229 return repo_name
229
230
230 def __get_user(self, username):
231 def __get_user(self, username):
231 return User.by_username(username)
232 return User.get_by_username(username)
232
233
233 def __get_action(self, environ):
234 def __get_action(self, environ):
234 """
235 """
@@ -257,7 +258,7 b' class SimpleHg(object):'
257 push requests"""
258 push requests"""
258 invalidate_cache('get_repo_cached_%s' % repo_name)
259 invalidate_cache('get_repo_cached_%s' % repo_name)
259
260
260 def __inject_extras(self,repo_path, baseui, extras={}):
261 def __inject_extras(self, repo_path, baseui, extras={}):
261 """
262 """
262 Injects some extra params into baseui instance
263 Injects some extra params into baseui instance
263
264
@@ -6,20 +6,7 b' import errno'
6 from warnings import warn
6 from warnings import warn
7 from multiprocessing.util import Finalize
7 from multiprocessing.util import Finalize
8
8
9 from rhodecode import __platform__, PLATFORM_WIN
9 from rhodecode.lib.compat import kill
10
11 if __platform__ in PLATFORM_WIN:
12 import ctypes
13
14 def kill(pid, sig):
15 """kill function for Win32"""
16 kernel32 = ctypes.windll.kernel32
17 handle = kernel32.OpenProcess(1, 0, pid)
18 return (0 != kernel32.TerminateProcess(handle, 0))
19
20 else:
21 kill = os.kill
22
23
10
24 class LockHeld(Exception):
11 class LockHeld(Exception):
25 pass
12 pass
@@ -29,17 +16,17 b' class DaemonLock(object):'
29 """daemon locking
16 """daemon locking
30 USAGE:
17 USAGE:
31 try:
18 try:
32 l = DaemonLock(desc='test lock')
19 l = DaemonLock(file_='/path/tolockfile',desc='test lock')
33 main()
20 main()
34 l.release()
21 l.release()
35 except LockHeld:
22 except LockHeld:
36 sys.exit(1)
23 sys.exit(1)
37 """
24 """
38
25
39 def __init__(self, file=None, callbackfn=None,
26 def __init__(self, file_=None, callbackfn=None,
40 desc='daemon lock', debug=False):
27 desc='daemon lock', debug=False):
41
28
42 self.pidfile = file if file else os.path.join(
29 self.pidfile = file_ if file_ else os.path.join(
43 os.path.dirname(__file__),
30 os.path.dirname(__file__),
44 'running.lock')
31 'running.lock')
45 self.callbackfn = callbackfn
32 self.callbackfn = callbackfn
@@ -6,9 +6,21 b''
6 Simple smtp mailer used in RhodeCode
6 Simple smtp mailer used in RhodeCode
7
7
8 :created_on: Sep 13, 2010
8 :created_on: Sep 13, 2010
9 :copyright: (c) 2011 by marcink.
9 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
10 :license: LICENSE_NAME, see LICENSE_FILE for more details.
10 :license: GPLv3, see COPYING for more details.
11 """
11 """
12 # This program is free software: you can redistribute it and/or modify
13 # it under the terms of the GNU General Public License as published by
14 # the Free Software Foundation, either version 3 of the License, or
15 # (at your option) any later version.
16 #
17 # This program is distributed in the hope that it will be useful,
18 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # GNU General Public License for more details.
21 #
22 # You should have received a copy of the GNU General Public License
23 # along with this program. If not, see <http://www.gnu.org/licenses/>.
12
24
13 import logging
25 import logging
14 import smtplib
26 import smtplib
@@ -112,7 +112,7 b' def action_logger(user, action, repo, ip'
112 if hasattr(user, 'user_id'):
112 if hasattr(user, 'user_id'):
113 user_obj = user
113 user_obj = user
114 elif isinstance(user, basestring):
114 elif isinstance(user, basestring):
115 user_obj = User.by_username(user)
115 user_obj = User.get_by_username(user)
116 else:
116 else:
117 raise Exception('You have to provide user object or username')
117 raise Exception('You have to provide user object or username')
118
118
@@ -360,16 +360,19 b' def map_groups(groups):'
360
360
361 parent = None
361 parent = None
362 group = None
362 group = None
363 for lvl, group_name in enumerate(groups[:-1]):
363
364 # last element is repo in nested groups structure
365 groups = groups[:-1]
366
367 for lvl, group_name in enumerate(groups):
368 group_name = '/'.join(groups[:lvl] + [group_name])
364 group = sa.query(Group).filter(Group.group_name == group_name).scalar()
369 group = sa.query(Group).filter(Group.group_name == group_name).scalar()
365
370
366 if group is None:
371 if group is None:
367 group = Group(group_name, parent)
372 group = Group(group_name, parent)
368 sa.add(group)
373 sa.add(group)
369 sa.commit()
374 sa.commit()
370
371 parent = group
375 parent = group
372
373 return group
376 return group
374
377
375
378
@@ -386,8 +389,13 b' def repo2db_mapper(initial_repo_list, re'
386 rm = RepoModel()
389 rm = RepoModel()
387 user = sa.query(User).filter(User.admin == True).first()
390 user = sa.query(User).filter(User.admin == True).first()
388 added = []
391 added = []
392 # fixup groups paths to new format on the fly
393 # TODO: remove this in future
394 for g in Group.query().all():
395 g.group_name = g.get_new_name(g.name)
396 sa.add(g)
389 for name, repo in initial_repo_list.items():
397 for name, repo in initial_repo_list.items():
390 group = map_groups(name.split(os.sep))
398 group = map_groups(name.split(Repository.url_sep()))
391 if not rm.get_by_repo_name(name, cache=False):
399 if not rm.get_by_repo_name(name, cache=False):
392 log.info('repository %s not found creating default', name)
400 log.info('repository %s not found creating default', name)
393 added.append(name)
401 added.append(name)
@@ -442,26 +450,6 b' def add_cache(settings):'
442 beaker.cache.cache_regions[region] = region_settings
450 beaker.cache.cache_regions[region] = region_settings
443
451
444
452
445 def get_current_revision():
446 """Returns tuple of (number, id) from repository containing this package
447 or None if repository could not be found.
448 """
449
450 try:
451 from vcs import get_repo
452 from vcs.utils.helpers import get_scm
453 from vcs.exceptions import RepositoryError, VCSError
454 repopath = os.path.join(os.path.dirname(__file__), '..', '..')
455 scm = get_scm(repopath)[0]
456 repo = get_repo(path=repopath, alias=scm)
457 tip = repo.get_changeset()
458 return (tip.revision, tip.short_id)
459 except (ImportError, RepositoryError, VCSError), err:
460 logging.debug("Cannot retrieve rhodecode's revision. Original error "
461 "was: %s" % err)
462 return None
463
464
465 #==============================================================================
453 #==============================================================================
466 # TEST FUNCTIONS AND CREATORS
454 # TEST FUNCTIONS AND CREATORS
467 #==============================================================================
455 #==============================================================================
@@ -483,7 +471,7 b' def create_test_index(repo_location, con'
483 os.makedirs(index_location)
471 os.makedirs(index_location)
484
472
485 try:
473 try:
486 l = DaemonLock(file=jn(dn(index_location), 'make_index.lock'))
474 l = DaemonLock(file_=jn(dn(index_location), 'make_index.lock'))
487 WhooshIndexingDaemon(index_location=index_location,
475 WhooshIndexingDaemon(index_location=index_location,
488 repo_location=repo_location)\
476 repo_location=repo_location)\
489 .run(full_index=full_index)
477 .run(full_index=full_index)
@@ -31,9 +31,10 b' from datetime import date'
31
31
32 from sqlalchemy import *
32 from sqlalchemy import *
33 from sqlalchemy.exc import DatabaseError
33 from sqlalchemy.exc import DatabaseError
34 from sqlalchemy.orm import relationship, backref, joinedload, class_mapper
34 from sqlalchemy.ext.hybrid import hybrid_property
35 from sqlalchemy.orm import relationship, backref, joinedload, class_mapper, \
36 validates
35 from sqlalchemy.orm.interfaces import MapperExtension
37 from sqlalchemy.orm.interfaces import MapperExtension
36
37 from beaker.cache import cache_region, region_invalidate
38 from beaker.cache import cache_region, region_invalidate
38
39
39 from vcs import get_backend
40 from vcs import get_backend
@@ -42,13 +43,14 b' from vcs.exceptions import VCSError'
42 from vcs.utils.lazy import LazyProperty
43 from vcs.utils.lazy import LazyProperty
43
44
44 from rhodecode.lib import str2bool, safe_str, get_changeset_safe, \
45 from rhodecode.lib import str2bool, safe_str, get_changeset_safe, \
45 generate_api_key
46 generate_api_key, safe_unicode
46 from rhodecode.lib.exceptions import UsersGroupsAssignedException
47 from rhodecode.lib.exceptions import UsersGroupsAssignedException
47 from rhodecode.lib.compat import json
48 from rhodecode.lib.compat import json
48
49
49 from rhodecode.model.meta import Base, Session
50 from rhodecode.model.meta import Base, Session
50 from rhodecode.model.caching_query import FromCache
51 from rhodecode.model.caching_query import FromCache
51
52
53
52 log = logging.getLogger(__name__)
54 log = logging.getLogger(__name__)
53
55
54 #==============================================================================
56 #==============================================================================
@@ -126,7 +128,8 b' class BaseModel(object):'
126
128
127 @classmethod
129 @classmethod
128 def get(cls, id_):
130 def get(cls, id_):
129 return Session.query(cls).get(id_)
131 if id_:
132 return Session.query(cls).get(id_)
130
133
131 @classmethod
134 @classmethod
132 def delete(cls, id_):
135 def delete(cls, id_):
@@ -140,12 +143,34 b' class RhodeCodeSettings(Base, BaseModel)'
140 __table_args__ = (UniqueConstraint('app_settings_name'), {'extend_existing':True})
143 __table_args__ = (UniqueConstraint('app_settings_name'), {'extend_existing':True})
141 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
144 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
142 app_settings_name = Column("app_settings_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
145 app_settings_name = Column("app_settings_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
143 app_settings_value = Column("app_settings_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
146 _app_settings_value = Column("app_settings_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
144
147
145 def __init__(self, k='', v=''):
148 def __init__(self, k='', v=''):
146 self.app_settings_name = k
149 self.app_settings_name = k
147 self.app_settings_value = v
150 self.app_settings_value = v
148
151
152
153 @validates('_app_settings_value')
154 def validate_settings_value(self, key, val):
155 assert type(val) == unicode
156 return val
157
158 @hybrid_property
159 def app_settings_value(self):
160 v = self._app_settings_value
161 if v == 'ldap_active':
162 v = str2bool(v)
163 return v
164
165 @app_settings_value.setter
166 def app_settings_value(self,val):
167 """
168 Setter that will always make sure we use unicode in app_settings_value
169
170 :param val:
171 """
172 self._app_settings_value = safe_unicode(val)
173
149 def __repr__(self):
174 def __repr__(self):
150 return "<%s('%s:%s')>" % (self.__class__.__name__,
175 return "<%s('%s:%s')>" % (self.__class__.__name__,
151 self.app_settings_name, self.app_settings_value)
176 self.app_settings_name, self.app_settings_value)
@@ -176,14 +201,11 b' class RhodeCodeSettings(Base, BaseModel)'
176 @classmethod
201 @classmethod
177 def get_ldap_settings(cls, cache=False):
202 def get_ldap_settings(cls, cache=False):
178 ret = Session.query(cls)\
203 ret = Session.query(cls)\
179 .filter(cls.app_settings_name.startswith('ldap_'))\
204 .filter(cls.app_settings_name.startswith('ldap_')).all()
180 .all()
181 fd = {}
205 fd = {}
182 for row in ret:
206 for row in ret:
183 fd.update({row.app_settings_name:row.app_settings_value})
207 fd.update({row.app_settings_name:row.app_settings_value})
184
208
185 fd.update({'ldap_active':str2bool(fd.get('ldap_active'))})
186
187 return fd
209 return fd
188
210
189
211
@@ -281,17 +303,16 b' class User(Base, BaseModel):'
281 return self.__class__.__name__
303 return self.__class__.__name__
282
304
283 @classmethod
305 @classmethod
284 def by_username(cls, username, case_insensitive=False):
306 def get_by_username(cls, username, case_insensitive=False):
285 if case_insensitive:
307 if case_insensitive:
286 return Session.query(cls).filter(cls.username.like(username)).one()
308 return Session.query(cls).filter(cls.username.ilike(username)).scalar()
287 else:
309 else:
288 return Session.query(cls).filter(cls.username == username).one()
310 return Session.query(cls).filter(cls.username == username).scalar()
289
311
290 @classmethod
312 @classmethod
291 def get_by_api_key(cls, api_key):
313 def get_by_api_key(cls, api_key):
292 return Session.query(cls).filter(cls.api_key == api_key).one()
314 return Session.query(cls).filter(cls.api_key == api_key).one()
293
315
294
295 def update_lastlogin(self):
316 def update_lastlogin(self):
296 """Update user lastlogin"""
317 """Update user lastlogin"""
297
318
@@ -487,12 +508,16 b' class Repository(Base, BaseModel):'
487 self.repo_id, self.repo_name)
508 self.repo_id, self.repo_name)
488
509
489 @classmethod
510 @classmethod
490 def by_repo_name(cls, repo_name):
511 def url_sep(cls):
512 return '/'
513
514 @classmethod
515 def get_by_repo_name(cls, repo_name):
491 q = Session.query(cls).filter(cls.repo_name == repo_name)
516 q = Session.query(cls).filter(cls.repo_name == repo_name)
492
517
493 q = q.options(joinedload(Repository.fork))\
518 q = q.options(joinedload(Repository.fork))\
494 .options(joinedload(Repository.user))\
519 .options(joinedload(Repository.user))\
495 .options(joinedload(Repository.group))\
520 .options(joinedload(Repository.group))\
496
521
497 return q.one()
522 return q.one()
498
523
@@ -507,13 +532,14 b' class Repository(Base, BaseModel):'
507
532
508 :param cls:
533 :param cls:
509 """
534 """
510 q = Session.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key == '/')
535 q = Session.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key ==
536 cls.url_sep())
511 q.options(FromCache("sql_cache_short", "repository_repo_path"))
537 q.options(FromCache("sql_cache_short", "repository_repo_path"))
512 return q.one().ui_value
538 return q.one().ui_value
513
539
514 @property
540 @property
515 def just_name(self):
541 def just_name(self):
516 return self.repo_name.split(os.sep)[-1]
542 return self.repo_name.split(Repository.url_sep())[-1]
517
543
518 @property
544 @property
519 def groups_with_parents(self):
545 def groups_with_parents(self):
@@ -542,7 +568,8 b' class Repository(Base, BaseModel):'
542 Returns base full path for that repository means where it actually
568 Returns base full path for that repository means where it actually
543 exists on a filesystem
569 exists on a filesystem
544 """
570 """
545 q = Session.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key == '/')
571 q = Session.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key ==
572 Repository.url_sep())
546 q.options(FromCache("sql_cache_short", "repository_repo_path"))
573 q.options(FromCache("sql_cache_short", "repository_repo_path"))
547 return q.one().ui_value
574 return q.one().ui_value
548
575
@@ -552,9 +579,18 b' class Repository(Base, BaseModel):'
552 # we need to split the name by / since this is how we store the
579 # we need to split the name by / since this is how we store the
553 # names in the database, but that eventually needs to be converted
580 # names in the database, but that eventually needs to be converted
554 # into a valid system path
581 # into a valid system path
555 p += self.repo_name.split('/')
582 p += self.repo_name.split(Repository.url_sep())
556 return os.path.join(*p)
583 return os.path.join(*p)
557
584
585 def get_new_name(self, repo_name):
586 """
587 returns new full repository name based on assigned group and new new
588
589 :param group_name:
590 """
591 path_prefix = self.group.full_path_splitted if self.group else []
592 return Repository.url_sep().join(path_prefix + [repo_name])
593
558 @property
594 @property
559 def _ui(self):
595 def _ui(self):
560 """
596 """
@@ -719,9 +755,26 b' class Group(Base, BaseModel):'
719 self.group_name)
755 self.group_name)
720
756
721 @classmethod
757 @classmethod
758 def groups_choices(cls):
759 from webhelpers.html import literal as _literal
760 repo_groups = [('', '')]
761 sep = ' &raquo; '
762 _name = lambda k: _literal(sep.join(k))
763
764 repo_groups.extend([(x.group_id, _name(x.full_path_splitted))
765 for x in cls.query().all()])
766
767 repo_groups = sorted(repo_groups,key=lambda t: t[1].split(sep)[0])
768 return repo_groups
769
770 @classmethod
722 def url_sep(cls):
771 def url_sep(cls):
723 return '/'
772 return '/'
724
773
774 @classmethod
775 def get_by_group_name(cls, group_name):
776 return cls.query().filter(cls.group_name == group_name).scalar()
777
725 @property
778 @property
726 def parents(self):
779 def parents(self):
727 parents_recursion_limit = 5
780 parents_recursion_limit = 5
@@ -751,9 +804,16 b' class Group(Base, BaseModel):'
751 return Session.query(Group).filter(Group.parent_group == self)
804 return Session.query(Group).filter(Group.parent_group == self)
752
805
753 @property
806 @property
807 def name(self):
808 return self.group_name.split(Group.url_sep())[-1]
809
810 @property
754 def full_path(self):
811 def full_path(self):
755 return Group.url_sep().join([g.group_name for g in self.parents] +
812 return self.group_name
756 [self.group_name])
813
814 @property
815 def full_path_splitted(self):
816 return self.group_name.split(Group.url_sep())
757
817
758 @property
818 @property
759 def repositories(self):
819 def repositories(self):
@@ -772,6 +832,17 b' class Group(Base, BaseModel):'
772
832
773 return cnt + children_count(self)
833 return cnt + children_count(self)
774
834
835
836 def get_new_name(self, group_name):
837 """
838 returns new full group name based on parent and new name
839
840 :param group_name:
841 """
842 path_prefix = self.parent_group.full_path_splitted if self.parent_group else []
843 return Group.url_sep().join(path_prefix + [group_name])
844
845
775 class Permission(Base, BaseModel):
846 class Permission(Base, BaseModel):
776 __tablename__ = 'permissions'
847 __tablename__ = 'permissions'
777 __table_args__ = {'extend_existing':True}
848 __table_args__ = {'extend_existing':True}
@@ -32,6 +32,7 b' from formencode.validators import Unicod'
32 from pylons.i18n.translation import _
32 from pylons.i18n.translation import _
33 from webhelpers.pylonslib.secure_form import authentication_token
33 from webhelpers.pylonslib.secure_form import authentication_token
34
34
35 from rhodecode.config.routing import ADMIN_PREFIX
35 from rhodecode.lib.utils import repo_name_slug
36 from rhodecode.lib.utils import repo_name_slug
36 from rhodecode.lib.auth import authenticate, get_crypt_password
37 from rhodecode.lib.auth import authenticate, get_crypt_password
37 from rhodecode.lib.exceptions import LdapImportError
38 from rhodecode.lib.exceptions import LdapImportError
@@ -70,8 +71,7 b' def ValidUsername(edit, old_data):'
70 old_un = UserModel().get(old_data.get('user_id')).username
71 old_un = UserModel().get(old_data.get('user_id')).username
71
72
72 if old_un != value or not edit:
73 if old_un != value or not edit:
73 if UserModel().get_by_username(value, cache=False,
74 if User.get_by_username(value, case_insensitive=True):
74 case_insensitive=True):
75 raise formencode.Invalid(_('This username already '
75 raise formencode.Invalid(_('This username already '
76 'exists') , value, state)
76 'exists') , value, state)
77
77
@@ -122,7 +122,7 b' def ValidReposGroup(edit, old_data):'
122 def validate_python(self, value, state):
122 def validate_python(self, value, state):
123 #TODO WRITE VALIDATIONS
123 #TODO WRITE VALIDATIONS
124 group_name = value.get('group_name')
124 group_name = value.get('group_name')
125 group_parent_id = int(value.get('group_parent_id') or - 1)
125 group_parent_id = int(value.get('group_parent_id') or -1)
126
126
127 # slugify repo group just in case :)
127 # slugify repo group just in case :)
128 slug = repo_name_slug(group_name)
128 slug = repo_name_slug(group_name)
@@ -206,7 +206,7 b' class ValidAuth(formencode.validators.Fa'
206 def validate_python(self, value, state):
206 def validate_python(self, value, state):
207 password = value['password']
207 password = value['password']
208 username = value['username']
208 username = value['username']
209 user = UserModel().get_by_username(username)
209 user = User.get_by_username(username)
210
210
211 if authenticate(username, password):
211 if authenticate(username, password):
212 return value
212 return value
@@ -241,7 +241,7 b' def ValidRepoName(edit, old_data):'
241 repo_name = value.get('repo_name')
241 repo_name = value.get('repo_name')
242
242
243 slug = repo_name_slug(repo_name)
243 slug = repo_name_slug(repo_name)
244 if slug in ['_admin', '']:
244 if slug in [ADMIN_PREFIX, '']:
245 e_dict = {'repo_name': _('This repository name is disallowed')}
245 e_dict = {'repo_name': _('This repository name is disallowed')}
246 raise formencode.Invalid('', value, state, error_dict=e_dict)
246 raise formencode.Invalid('', value, state, error_dict=e_dict)
247
247
@@ -250,7 +250,7 b' def ValidRepoName(edit, old_data):'
250 gr = Group.get(value.get('repo_group'))
250 gr = Group.get(value.get('repo_group'))
251 group_path = gr.full_path
251 group_path = gr.full_path
252 # value needs to be aware of group name in order to check
252 # value needs to be aware of group name in order to check
253 # db key This is an actuall just the name to store in the
253 # db key This is an actual just the name to store in the
254 # database
254 # database
255 repo_name_full = group_path + Group.url_sep() + repo_name
255 repo_name_full = group_path + Group.url_sep() + repo_name
256 else:
256 else:
@@ -259,25 +259,32 b' def ValidRepoName(edit, old_data):'
259
259
260
260
261 value['repo_name_full'] = repo_name_full
261 value['repo_name_full'] = repo_name_full
262 if old_data.get('repo_name') != repo_name_full or not edit:
262 rename = old_data.get('repo_name') != repo_name_full
263 create = not edit
264 if rename or create:
263
265
264 if group_path != '':
266 if group_path != '':
265 if RepoModel().get_by_repo_name(repo_name_full,):
267 if RepoModel().get_by_repo_name(repo_name_full,):
266 e_dict = {'repo_name':_('This repository already '
268 e_dict = {'repo_name':_('This repository already '
267 'exists in group "%s"') %
269 'exists in a group "%s"') %
268 gr.group_name}
270 gr.group_name}
269 raise formencode.Invalid('', value, state,
271 raise formencode.Invalid('', value, state,
270 error_dict=e_dict)
272 error_dict=e_dict)
273 elif Group.get_by_group_name(repo_name_full):
274 e_dict = {'repo_name':_('There is a group with this'
275 ' name already "%s"') %
276 repo_name_full}
277 raise formencode.Invalid('', value, state,
278 error_dict=e_dict)
271
279
272 else:
280 elif RepoModel().get_by_repo_name(repo_name_full):
273 if RepoModel().get_by_repo_name(repo_name_full):
274 e_dict = {'repo_name':_('This repository '
281 e_dict = {'repo_name':_('This repository '
275 'already exists')}
282 'already exists')}
276 raise formencode.Invalid('', value, state,
283 raise formencode.Invalid('', value, state,
277 error_dict=e_dict)
284 error_dict=e_dict)
285
278 return value
286 return value
279
287
280
281 return _ValidRepoName
288 return _ValidRepoName
282
289
283 def ValidForkName():
290 def ValidForkName():
@@ -287,7 +294,7 b' def ValidForkName():'
287 repo_name = value.get('fork_name')
294 repo_name = value.get('fork_name')
288
295
289 slug = repo_name_slug(repo_name)
296 slug = repo_name_slug(repo_name)
290 if slug in ['_admin', '']:
297 if slug in [ADMIN_PREFIX, '']:
291 e_dict = {'repo_name': _('This repository name is disallowed')}
298 e_dict = {'repo_name': _('This repository name is disallowed')}
292 raise formencode.Invalid('', value, state, error_dict=e_dict)
299 raise formencode.Invalid('', value, state, error_dict=e_dict)
293
300
@@ -98,11 +98,11 b' class RepoModel(BaseModel):'
98 try:
98 try:
99 cur_repo = self.get_by_repo_name(repo_name, cache=False)
99 cur_repo = self.get_by_repo_name(repo_name, cache=False)
100
100
101 #update permissions
101 # update permissions
102 for member, perm, member_type in form_data['perms_updates']:
102 for member, perm, member_type in form_data['perms_updates']:
103 if member_type == 'user':
103 if member_type == 'user':
104 r2p = self.sa.query(RepoToPerm)\
104 r2p = self.sa.query(RepoToPerm)\
105 .filter(RepoToPerm.user == User.by_username(member))\
105 .filter(RepoToPerm.user == User.get_by_username(member))\
106 .filter(RepoToPerm.repository == cur_repo)\
106 .filter(RepoToPerm.repository == cur_repo)\
107 .one()
107 .one()
108
108
@@ -122,12 +122,12 b' class RepoModel(BaseModel):'
122 perm).scalar()
122 perm).scalar()
123 self.sa.add(g2p)
123 self.sa.add(g2p)
124
124
125 #set new permissions
125 # set new permissions
126 for member, perm, member_type in form_data['perms_new']:
126 for member, perm, member_type in form_data['perms_new']:
127 if member_type == 'user':
127 if member_type == 'user':
128 r2p = RepoToPerm()
128 r2p = RepoToPerm()
129 r2p.repository = cur_repo
129 r2p.repository = cur_repo
130 r2p.user = User.by_username(member)
130 r2p.user = User.get_by_username(member)
131
131
132 r2p.permission = self.sa.query(Permission)\
132 r2p.permission = self.sa.query(Permission)\
133 .filter(Permission.
133 .filter(Permission.
@@ -144,26 +144,29 b' class RepoModel(BaseModel):'
144 .scalar()
144 .scalar()
145 self.sa.add(g2p)
145 self.sa.add(g2p)
146
146
147 #update current repo
147 # update current repo
148 for k, v in form_data.items():
148 for k, v in form_data.items():
149 if k == 'user':
149 if k == 'user':
150 cur_repo.user = User.by_username(v)
150 cur_repo.user = User.get_by_username(v)
151 elif k == 'repo_name':
151 elif k == 'repo_name':
152 cur_repo.repo_name = form_data['repo_name_full']
152 pass
153 elif k == 'repo_group':
153 elif k == 'repo_group':
154 cur_repo.group_id = v
154 cur_repo.group_id = v
155
155
156 else:
156 else:
157 setattr(cur_repo, k, v)
157 setattr(cur_repo, k, v)
158
158
159 new_name = cur_repo.get_new_name(form_data['repo_name'])
160 cur_repo.repo_name = new_name
161
159 self.sa.add(cur_repo)
162 self.sa.add(cur_repo)
160
163
161 if repo_name != form_data['repo_name_full']:
164 if repo_name != new_name:
162 # rename repository
165 # rename repository
163 self.__rename_repo(old=repo_name,
166 self.__rename_repo(old=repo_name, new=new_name)
164 new=form_data['repo_name_full'])
165
167
166 self.sa.commit()
168 self.sa.commit()
169 return cur_repo
167 except:
170 except:
168 log.error(traceback.format_exc())
171 log.error(traceback.format_exc())
169 self.sa.rollback()
172 self.sa.rollback()
@@ -208,8 +211,7 b' class RepoModel(BaseModel):'
208 #create default permission
211 #create default permission
209 repo_to_perm = RepoToPerm()
212 repo_to_perm = RepoToPerm()
210 default = 'repository.read'
213 default = 'repository.read'
211 for p in UserModel(self.sa).get_by_username('default',
214 for p in User.get_by_username('default').user_perms:
212 cache=False).user_perms:
213 if p.permission.permission_name.startswith('repository.'):
215 if p.permission.permission_name.startswith('repository.'):
214 default = p.permission.permission_name
216 default = p.permission.permission_name
215 break
217 break
@@ -221,8 +223,7 b' class RepoModel(BaseModel):'
221 .one().permission_id
223 .one().permission_id
222
224
223 repo_to_perm.repository = new_repo
225 repo_to_perm.repository = new_repo
224 repo_to_perm.user_id = UserModel(self.sa)\
226 repo_to_perm.user_id = User.get_by_username('default').user_id
225 .get_by_username('default', cache=False).user_id
226
227
227 self.sa.add(repo_to_perm)
228 self.sa.add(repo_to_perm)
228
229
@@ -237,7 +238,7 b' class RepoModel(BaseModel):'
237 from rhodecode.model.scm import ScmModel
238 from rhodecode.model.scm import ScmModel
238 ScmModel(self.sa).toggle_following_repo(new_repo.repo_id,
239 ScmModel(self.sa).toggle_following_repo(new_repo.repo_id,
239 cur_user.user_id)
240 cur_user.user_id)
240
241 return new_repo
241 except:
242 except:
242 log.error(traceback.format_exc())
243 log.error(traceback.format_exc())
243 self.sa.rollback()
244 self.sa.rollback()
@@ -304,7 +305,7 b' class RepoModel(BaseModel):'
304 :param parent_id:
305 :param parent_id:
305 :param clone_uri:
306 :param clone_uri:
306 """
307 """
307 from rhodecode.lib.utils import is_valid_repo
308 from rhodecode.lib.utils import is_valid_repo,is_valid_repos_group
308
309
309 if new_parent_id:
310 if new_parent_id:
310 paths = Group.get(new_parent_id).full_path.split(Group.url_sep())
311 paths = Group.get(new_parent_id).full_path.split(Group.url_sep())
@@ -315,12 +316,20 b' class RepoModel(BaseModel):'
315 repo_path = os.path.join(*map(lambda x:safe_str(x),
316 repo_path = os.path.join(*map(lambda x:safe_str(x),
316 [self.repos_path, new_parent_path, repo_name]))
317 [self.repos_path, new_parent_path, repo_name]))
317
318
318 if is_valid_repo(repo_path, self.repos_path) is False:
319
319 log.info('creating repo %s in %s @ %s', repo_name, repo_path,
320 # check if this path is not a repository
320 clone_uri)
321 if is_valid_repo(repo_path, self.repos_path):
321 backend = get_backend(alias)
322 raise Exception('This path %s is a valid repository' % repo_path)
322
323
323 backend(repo_path, create=True, src_url=clone_uri)
324 # check if this path is a group
325 if is_valid_repos_group(repo_path, self.repos_path):
326 raise Exception('This path %s is a valid group' % repo_path)
327
328 log.info('creating repo %s in %s @ %s', repo_name, repo_path,
329 clone_uri)
330 backend = get_backend(alias)
331
332 backend(repo_path, create=True, src_url=clone_uri)
324
333
325
334
326 def __rename_repo(self, old, new):
335 def __rename_repo(self, old, new):
@@ -50,7 +50,7 b' class ReposGroupModel(BaseModel):'
50 q = RhodeCodeUi.get_by_key('/').one()
50 q = RhodeCodeUi.get_by_key('/').one()
51 return q.ui_value
51 return q.ui_value
52
52
53 def __create_group(self, group_name, parent_id):
53 def __create_group(self, group_name):
54 """
54 """
55 makes repositories group on filesystem
55 makes repositories group on filesystem
56
56
@@ -58,44 +58,30 b' class ReposGroupModel(BaseModel):'
58 :param parent_id:
58 :param parent_id:
59 """
59 """
60
60
61 if parent_id:
61 create_path = os.path.join(self.repos_path, group_name)
62 paths = Group.get(parent_id).full_path.split(Group.url_sep())
63 parent_path = os.sep.join(paths)
64 else:
65 parent_path = ''
66
67 create_path = os.path.join(self.repos_path, parent_path, group_name)
68 log.debug('creating new group in %s', create_path)
62 log.debug('creating new group in %s', create_path)
69
63
70 if os.path.isdir(create_path):
64 if os.path.isdir(create_path):
71 raise Exception('That directory already exists !')
65 raise Exception('That directory already exists !')
72
66
73
74 os.makedirs(create_path)
67 os.makedirs(create_path)
75
68
76
69 def __rename_group(self, old, new):
77 def __rename_group(self, old, old_parent_id, new, new_parent_id):
78 """
70 """
79 Renames a group on filesystem
71 Renames a group on filesystem
80
72
81 :param group_name:
73 :param group_name:
82 """
74 """
75
76 if old == new:
77 log.debug('skipping group rename')
78 return
79
83 log.debug('renaming repos group from %s to %s', old, new)
80 log.debug('renaming repos group from %s to %s', old, new)
84
81
85 if new_parent_id:
86 paths = Group.get(new_parent_id).full_path.split(Group.url_sep())
87 new_parent_path = os.sep.join(paths)
88 else:
89 new_parent_path = ''
90
82
91 if old_parent_id:
83 old_path = os.path.join(self.repos_path, old)
92 paths = Group.get(old_parent_id).full_path.split(Group.url_sep())
84 new_path = os.path.join(self.repos_path, new)
93 old_parent_path = os.sep.join(paths)
94 else:
95 old_parent_path = ''
96
97 old_path = os.path.join(self.repos_path, old_parent_path, old)
98 new_path = os.path.join(self.repos_path, new_parent_path, new)
99
85
100 log.debug('renaming repos paths from %s to %s', old_path, new_path)
86 log.debug('renaming repos paths from %s to %s', old_path, new_path)
101
87
@@ -114,22 +100,23 b' class ReposGroupModel(BaseModel):'
114 paths = os.sep.join(paths)
100 paths = os.sep.join(paths)
115
101
116 rm_path = os.path.join(self.repos_path, paths)
102 rm_path = os.path.join(self.repos_path, paths)
117 os.rmdir(rm_path)
103 if os.path.isdir(rm_path):
104 # delete only if that path really exists
105 os.rmdir(rm_path)
118
106
119 def create(self, form_data):
107 def create(self, form_data):
120 try:
108 try:
121 new_repos_group = Group()
109 new_repos_group = Group()
122 new_repos_group.group_name = form_data['group_name']
110 new_repos_group.group_description = form_data['group_description']
123 new_repos_group.group_description = \
111 new_repos_group.parent_group = Group.get(form_data['group_parent_id'])
124 form_data['group_description']
112 new_repos_group.group_name = new_repos_group.get_new_name(form_data['group_name'])
125 new_repos_group.group_parent_id = form_data['group_parent_id']
126
113
127 self.sa.add(new_repos_group)
114 self.sa.add(new_repos_group)
128
115
129 self.__create_group(form_data['group_name'],
116 self.__create_group(new_repos_group.group_name)
130 form_data['group_parent_id'])
131
117
132 self.sa.commit()
118 self.sa.commit()
119 return new_repos_group
133 except:
120 except:
134 log.error(traceback.format_exc())
121 log.error(traceback.format_exc())
135 self.sa.rollback()
122 self.sa.rollback()
@@ -139,23 +126,27 b' class ReposGroupModel(BaseModel):'
139
126
140 try:
127 try:
141 repos_group = Group.get(repos_group_id)
128 repos_group = Group.get(repos_group_id)
142 old_name = repos_group.group_name
129 old_path = repos_group.full_path
143 old_parent_id = repos_group.group_parent_id
144
130
145 repos_group.group_name = form_data['group_name']
131 #change properties
146 repos_group.group_description = \
132 repos_group.group_description = form_data['group_description']
147 form_data['group_description']
133 repos_group.parent_group = Group.get(form_data['group_parent_id'])
148 repos_group.group_parent_id = form_data['group_parent_id']
134 repos_group.group_name = repos_group.get_new_name(form_data['group_name'])
135
136 new_path = repos_group.full_path
149
137
150 self.sa.add(repos_group)
138 self.sa.add(repos_group)
151
139
152 if old_name != form_data['group_name'] or (old_parent_id !=
140 self.__rename_group(old_path, new_path)
153 form_data['group_parent_id']):
141
154 self.__rename_group(old=old_name, old_parent_id=old_parent_id,
142 # we need to get all repositories from this new group and
155 new=form_data['group_name'],
143 # rename them accordingly to new group path
156 new_parent_id=form_data['group_parent_id'])
144 for r in repos_group.repositories:
145 r.repo_name = r.get_new_name(r.just_name)
146 self.sa.add(r)
157
147
158 self.sa.commit()
148 self.sa.commit()
149 return repos_group
159 except:
150 except:
160 log.error(traceback.format_exc())
151 log.error(traceback.format_exc())
161 self.sa.rollback()
152 self.sa.rollback()
@@ -41,9 +41,8 b' from rhodecode.lib.auth import HasRepoPe'
41 from rhodecode.lib.utils import get_repos as get_filesystem_repos, make_ui, \
41 from rhodecode.lib.utils import get_repos as get_filesystem_repos, make_ui, \
42 action_logger, EmptyChangeset
42 action_logger, EmptyChangeset
43 from rhodecode.model import BaseModel
43 from rhodecode.model import BaseModel
44 from rhodecode.model.user import UserModel
45 from rhodecode.model.db import Repository, RhodeCodeUi, CacheInvalidation, \
44 from rhodecode.model.db import Repository, RhodeCodeUi, CacheInvalidation, \
46 UserFollowing, UserLog
45 UserFollowing, UserLog, User
47
46
48 log = logging.getLogger(__name__)
47 log = logging.getLogger(__name__)
49
48
@@ -148,10 +147,15 b' class ScmModel(BaseModel):'
148 repos_list = {}
147 repos_list = {}
149
148
150 for name, path in get_filesystem_repos(repos_path, recursive=True):
149 for name, path in get_filesystem_repos(repos_path, recursive=True):
150
151 # name need to be decomposed and put back together using the /
152 # since this is internal storage separator for rhodecode
153 name = Repository.url_sep().join(name.split(os.sep))
154
151 try:
155 try:
152 if name in repos_list:
156 if name in repos_list:
153 raise RepositoryError('Duplicate repository name %s '
157 raise RepositoryError('Duplicate repository name %s '
154 'found in %s' % (name, path))
158 'found in %s' % (name, path))
155 else:
159 else:
156
160
157 klass = get_backend(path[0])
161 klass = get_backend(path[0])
@@ -283,7 +287,7 b' class ScmModel(BaseModel):'
283 return f is not None
287 return f is not None
284
288
285 def is_following_user(self, username, user_id, cache=False):
289 def is_following_user(self, username, user_id, cache=False):
286 u = UserModel(self.sa).get_by_username(username)
290 u = User.get_by_username(username)
287
291
288 f = self.sa.query(UserFollowing)\
292 f = self.sa.query(UserFollowing)\
289 .filter(UserFollowing.follows_user == u)\
293 .filter(UserFollowing.follows_user == u)\
@@ -293,24 +297,24 b' class ScmModel(BaseModel):'
293
297
294 def get_followers(self, repo_id):
298 def get_followers(self, repo_id):
295 if not isinstance(repo_id, int):
299 if not isinstance(repo_id, int):
296 repo_id = getattr(Repository.by_repo_name(repo_id), 'repo_id')
300 repo_id = getattr(Repository.get_by_repo_name(repo_id), 'repo_id')
297
301
298 return self.sa.query(UserFollowing)\
302 return self.sa.query(UserFollowing)\
299 .filter(UserFollowing.follows_repo_id == repo_id).count()
303 .filter(UserFollowing.follows_repo_id == repo_id).count()
300
304
301 def get_forks(self, repo_id):
305 def get_forks(self, repo_id):
302 if not isinstance(repo_id, int):
306 if not isinstance(repo_id, int):
303 repo_id = getattr(Repository.by_repo_name(repo_id), 'repo_id')
307 repo_id = getattr(Repository.get_by_repo_name(repo_id), 'repo_id')
304
308
305 return self.sa.query(Repository)\
309 return self.sa.query(Repository)\
306 .filter(Repository.fork_id == repo_id).count()
310 .filter(Repository.fork_id == repo_id).count()
307
311
308 def pull_changes(self, repo_name, username):
312 def pull_changes(self, repo_name, username):
309 dbrepo = Repository.by_repo_name(repo_name)
313 dbrepo = Repository.get_by_repo_name(repo_name)
310 clone_uri = dbrepo.clone_uri
314 clone_uri = dbrepo.clone_uri
311 if not clone_uri:
315 if not clone_uri:
312 raise Exception("This repository doesn't have a clone uri")
316 raise Exception("This repository doesn't have a clone uri")
313
317
314 repo = dbrepo.scm_instance
318 repo = dbrepo.scm_instance
315 try:
319 try:
316 extras = {'ip': '',
320 extras = {'ip': '',
@@ -363,12 +367,12 b' class ScmModel(BaseModel):'
363 from vcs.backends.git import GitInMemoryChangeset as IMC
367 from vcs.backends.git import GitInMemoryChangeset as IMC
364 # decoding here will force that we have proper encoded values
368 # decoding here will force that we have proper encoded values
365 # in any other case this will throw exceptions and deny commit
369 # in any other case this will throw exceptions and deny commit
366
370
367 if isinstance(content,(basestring,)):
371 if isinstance(content, (basestring,)):
368 content = safe_str(content)
372 content = safe_str(content)
369 elif isinstance(content,file):
373 elif isinstance(content, file):
370 content = content.read()
374 content = content.read()
371
375
372 message = safe_str(message)
376 message = safe_str(message)
373 path = safe_str(f_path)
377 path = safe_str(f_path)
374 author = safe_str(author)
378 author = safe_str(author)
@@ -246,12 +246,13 b' color:#FFF;'
246 }
246 }
247
247
248 #header #header-inner {
248 #header #header-inner {
249 height:40px;
249 min-height:40px;
250 clear:both;
250 clear:both;
251 position:relative;
251 position:relative;
252 background:#003367 url("../images/header_inner.png") repeat-x;
252 background:#003367 url("../images/header_inner.png") repeat-x;
253 margin:0;
253 margin:0;
254 padding:0;
254 padding:0;
255 display:block;
255 box-shadow: 0 2px 2px rgba(0, 0, 0, 0.6);
256 box-shadow: 0 2px 2px rgba(0, 0, 0, 0.6);
256 -webkit-border-radius: 4px 4px 4px 4px;
257 -webkit-border-radius: 4px 4px 4px 4px;
257 -khtml-border-radius: 4px 4px 4px 4px;
258 -khtml-border-radius: 4px 4px 4px 4px;
@@ -272,7 +273,10 b' padding:0;'
272 #header #header-inner #home a:hover {
273 #header #header-inner #home a:hover {
273 background-position:0 -40px;
274 background-position:0 -40px;
274 }
275 }
275
276 #header #header-inner #logo {
277 float: left;
278 position: absolute;
279 }
276 #header #header-inner #logo h1 {
280 #header #header-inner #logo h1 {
277 color:#FFF;
281 color:#FFF;
278 font-size:18px;
282 font-size:18px;
@@ -348,7 +352,7 b' top:0;'
348 left:0;
352 left:0;
349 border-left:none;
353 border-left:none;
350 border-right:1px solid #2e5c89;
354 border-right:1px solid #2e5c89;
351 padding:8px 8px 4px;
355 padding:8px 6px 4px;
352 }
356 }
353
357
354 #header #header-inner #quick li span.icon_short {
358 #header #header-inner #quick li span.icon_short {
@@ -356,7 +360,10 b' top:0;'
356 left:0;
360 left:0;
357 border-left:none;
361 border-left:none;
358 border-right:1px solid #2e5c89;
362 border-right:1px solid #2e5c89;
359 padding:9px 4px 4px;
363 padding:8px 6px 4px;
364 }
365 #header #header-inner #quick li span.icon img, #header #header-inner #quick li span.icon_short img {
366 margin: 0px -2px 0px 0px;
360 }
367 }
361
368
362 #header #header-inner #quick li a:hover {
369 #header #header-inner #quick li a:hover {
@@ -564,6 +571,14 b' padding:12px 9px 7px 24px;'
564 }
571 }
565
572
566
573
574 .groups_breadcrumbs a {
575 color: #fff;
576 }
577 .groups_breadcrumbs a:hover {
578 color: #bfe3ff;
579 text-decoration: none;
580 }
581
567 .quick_repo_menu{
582 .quick_repo_menu{
568 background: #FFF url("../images/vertical-indicator.png") 8px 50% no-repeat !important;
583 background: #FFF url("../images/vertical-indicator.png") 8px 50% no-repeat !important;
569 cursor: pointer;
584 cursor: pointer;
@@ -1895,19 +1910,6 b' div.browserblock .add_node{'
1895 padding-left: 5px;
1910 padding-left: 5px;
1896 }
1911 }
1897
1912
1898 div.browserblock .search_activate #filter_activate,div.browserblock .add_node a{
1899 vertical-align: sub;
1900 border: 1px solid;
1901 padding:2px;
1902 -webkit-border-radius: 4px 4px 4px 4px;
1903 -khtml-border-radius: 4px 4px 4px 4px;
1904 -moz-border-radius: 4px 4px 4px 4px;
1905 border-radius: 4px 4px 4px 4px;
1906 background: url("../images/button.png") repeat-x scroll 0 0 #E5E3E3;
1907 border-color: #DDDDDD #DDDDDD #C6C6C6 #C6C6C6;
1908 color: #515151;
1909 }
1910
1911 div.browserblock .search_activate a:hover,div.browserblock .add_node a:hover{
1913 div.browserblock .search_activate a:hover,div.browserblock .add_node a:hover{
1912 text-decoration: none !important;
1914 text-decoration: none !important;
1913 }
1915 }
@@ -2371,8 +2373,10 b' border-left:1px solid #316293;'
2371 border:1px solid #316293;
2373 border:1px solid #316293;
2372 }
2374 }
2373
2375
2374
2376 .ui-button-small a:hover {
2375 input.ui-button-small {
2377
2378 }
2379 input.ui-button-small,.ui-button-small {
2376 background:#e5e3e3 url("../images/button.png") repeat-x !important;
2380 background:#e5e3e3 url("../images/button.png") repeat-x !important;
2377 border-top:1px solid #DDD !important;
2381 border-top:1px solid #DDD !important;
2378 border-left:1px solid #c6c6c6 !important;
2382 border-left:1px solid #c6c6c6 !important;
@@ -2387,17 +2391,19 b' margin:0 !important;'
2387 border-radius: 4px 4px 4px 4px !important;
2391 border-radius: 4px 4px 4px 4px !important;
2388 box-shadow: 0 1px 0 #ececec !important;
2392 box-shadow: 0 1px 0 #ececec !important;
2389 cursor: pointer !important;
2393 cursor: pointer !important;
2390 }
2394 padding:0px 2px 1px 2px;
2391
2395 }
2392 input.ui-button-small:hover {
2396
2397 input.ui-button-small:hover,.ui-button-small:hover {
2393 background:#b4b4b4 url("../images/button_selected.png") repeat-x !important;
2398 background:#b4b4b4 url("../images/button_selected.png") repeat-x !important;
2394 border-top:1px solid #ccc !important;
2399 border-top:1px solid #ccc !important;
2395 border-left:1px solid #bebebe !important;
2400 border-left:1px solid #bebebe !important;
2396 border-right:1px solid #b1b1b1 !important;
2401 border-right:1px solid #b1b1b1 !important;
2397 border-bottom:1px solid #afafaf !important;
2402 border-bottom:1px solid #afafaf !important;
2398 }
2403 text-decoration: none;
2399
2404 }
2400 input.ui-button-small-blue {
2405
2406 input.ui-button-small-blue,.ui-button-small-blue {
2401 background:#4e85bb url("../images/button_highlight.png") repeat-x;
2407 background:#4e85bb url("../images/button_highlight.png") repeat-x;
2402 border-top:1px solid #5c91a4;
2408 border-top:1px solid #5c91a4;
2403 border-left:1px solid #2a6f89;
2409 border-left:1px solid #2a6f89;
@@ -2410,6 +2416,7 b' color:#fff;'
2410 border-radius: 4px 4px 4px 4px;
2416 border-radius: 4px 4px 4px 4px;
2411 box-shadow: 0 1px 0 #ececec;
2417 box-shadow: 0 1px 0 #ececec;
2412 cursor: pointer;
2418 cursor: pointer;
2419 padding:0px 2px 1px 2px;
2413 }
2420 }
2414
2421
2415 input.ui-button-small-blue:hover {
2422 input.ui-button-small-blue:hover {
@@ -5,12 +5,14 b''
5 </%def>
5 </%def>
6
6
7 <%def name="breadcrumbs()">
7 <%def name="breadcrumbs()">
8 <span class="groups_breadcrumbs">
8 ${_('Groups')}
9 ${_('Groups')}
9 %if c.group.parent_group:
10 %if c.group.parent_group:
10 &raquo; ${h.link_to(c.group.parent_group.group_name,
11 &raquo; ${h.link_to(c.group.parent_group.name,
11 h.url('repos_group',id=c.group.parent_group.group_id))}
12 h.url('repos_group_home',group_name=c.group.parent_group.group_name))}
12 %endif
13 %endif
13 &raquo; "${c.group.group_name}" ${_('with')}
14 &raquo; "${c.group.name}" ${_('with')}
15 </span>
14 </%def>
16 </%def>
15
17
16 <%def name="page_nav()">
18 <%def name="page_nav()">
@@ -2,14 +2,14 b''
2 <%inherit file="/base/base.html"/>
2 <%inherit file="/base/base.html"/>
3
3
4 <%def name="title()">
4 <%def name="title()">
5 ${_('Edit repos group')} ${c.repos_group.group_name} - ${c.rhodecode_name}
5 ${_('Edit repos group')} ${c.repos_group.name} - ${c.rhodecode_name}
6 </%def>
6 </%def>
7 <%def name="breadcrumbs_links()">
7 <%def name="breadcrumbs_links()">
8 ${h.link_to(_('Admin'),h.url('admin_home'))}
8 ${h.link_to(_('Admin'),h.url('admin_home'))}
9 &raquo;
9 &raquo;
10 ${h.link_to(_('Repos groups'),h.url('repos_groups'))}
10 ${h.link_to(_('Repos groups'),h.url('repos_groups'))}
11 &raquo;
11 &raquo;
12 ${_('edit repos group')} "${c.repos_group.group_name}"
12 ${_('edit repos group')} "${c.repos_group.name}"
13 </%def>
13 </%def>
14
14
15 <%def name="page_nav()">
15 <%def name="page_nav()">
@@ -44,14 +44,14 b''
44 <td>
44 <td>
45 <div style="white-space: nowrap">
45 <div style="white-space: nowrap">
46 <img class="icon" alt="${_('Repositories group')}" src="${h.url('/images/icons/database_link.png')}"/>
46 <img class="icon" alt="${_('Repositories group')}" src="${h.url('/images/icons/database_link.png')}"/>
47 ${h.link_to(h.literal(' &raquo; '.join([g.group_name for g in gr.parents+[gr]])),url('edit_repos_group',id=gr.group_id))}
47 ${h.link_to(h.literal(' &raquo; '.join([g.name for g in gr.parents+[gr]])),url('edit_repos_group',id=gr.group_id))}
48 </div>
48 </div>
49 </td>
49 </td>
50 <td>${gr.group_description}</td>
50 <td>${gr.group_description}</td>
51 <td><b>${gr.repositories.count()}</b></td>
51 <td><b>${gr.repositories.count()}</b></td>
52 <td>
52 <td>
53 ${h.form(url('repos_group', id=gr.group_id),method='delete')}
53 ${h.form(url('repos_group', id=gr.group_id),method='delete')}
54 ${h.submit('remove_%s' % gr.group_name,'delete',class_="delete_icon action_button",onclick="return confirm('"+_('Confirm to delete this group')+"');")}
54 ${h.submit('remove_%s' % gr.name,'delete',class_="delete_icon action_button",onclick="return confirm('"+_('Confirm to delete this group')+"');")}
55 ${h.end_form()}
55 ${h.end_form()}
56 </td>
56 </td>
57 </tr>
57 </tr>
@@ -11,9 +11,9 b''
11 ${h.form(h.url.current())}
11 ${h.form(h.url.current())}
12 <div class="info_box">
12 <div class="info_box">
13 <span class="rev">${_('view')}@rev</span>
13 <span class="rev">${_('view')}@rev</span>
14 <a class="rev" href="${c.url_prev}" title="${_('previous revision')}">&laquo;</a>
14 <a class="ui-button-small" href="${c.url_prev}" title="${_('previous revision')}">&laquo;</a>
15 ${h.text('at_rev',value=c.changeset.revision,size=5)}
15 ${h.text('at_rev',value=c.changeset.revision,size=5)}
16 <a class="rev" href="${c.url_next}" title="${_('next revision')}">&raquo;</a>
16 <a class="ui-button-small" href="${c.url_next}" title="${_('next revision')}">&raquo;</a>
17 ## ${h.submit('view',_('view'),class_="ui-button-small")}
17 ## ${h.submit('view',_('view'),class_="ui-button-small")}
18 </div>
18 </div>
19 ${h.end_form()}
19 ${h.end_form()}
@@ -24,11 +24,11 b''
24 </div>
24 </div>
25 <div class="browser-search">
25 <div class="browser-search">
26 <div id="search_activate_id" class="search_activate">
26 <div id="search_activate_id" class="search_activate">
27 <a id="filter_activate" href="#">${_('search file list')}</a>
27 <a class="ui-button-small" id="filter_activate" href="#">${_('search file list')}</a>
28 </div>
28 </div>
29 % if h.HasRepoPermissionAny('repository.write','repository.admin')(c.repo_name):
29 % if h.HasRepoPermissionAny('repository.write','repository.admin')(c.repo_name):
30 <div id="add_node_id" class="add_node">
30 <div id="add_node_id" class="add_node">
31 <a href="${h.url('files_add_home',repo_name=c.repo_name,revision=c.changeset.raw_id,f_path=c.f_path)}">${_('add new file')}</a>
31 <a class="ui-button-small" href="${h.url('files_add_home',repo_name=c.repo_name,revision=c.changeset.raw_id,f_path=c.f_path)}">${_('add new file')}</a>
32 </div>
32 </div>
33 % endif
33 % endif
34 <div>
34 <div>
@@ -35,7 +35,7 b''
35 <td>
35 <td>
36 <div style="white-space: nowrap">
36 <div style="white-space: nowrap">
37 <img class="icon" alt="${_('Repositories group')}" src="${h.url('/images/icons/database_link.png')}"/>
37 <img class="icon" alt="${_('Repositories group')}" src="${h.url('/images/icons/database_link.png')}"/>
38 ${h.link_to(gr.group_name,url('repos_group',id=gr.group_id))}
38 ${h.link_to(gr.name,url('repos_group_home',group_name=gr.group_name))}
39 </div>
39 </div>
40 </td>
40 </td>
41 <td>${gr.group_description}</td>
41 <td>${gr.group_description}</td>
@@ -45,17 +45,17 b''
45
45
46 ##REPO TYPE
46 ##REPO TYPE
47 %if c.dbrepo.repo_type =='hg':
47 %if c.dbrepo.repo_type =='hg':
48 <img style="margin-bottom:2px" class="icon" title="${_('Mercurial repository')}" alt="${_('Mercurial repository')}" src="${h.url("/images/icons/hgicon.png")}"/>
48 <img style="margin-bottom:2px" class="icon" title="${_('Mercurial repository')}" alt="${_('Mercurial repository')}" src="${h.url('/images/icons/hgicon.png')}"/>
49 %endif
49 %endif
50 %if c.dbrepo.repo_type =='git':
50 %if c.dbrepo.repo_type =='git':
51 <img style="margin-bottom:2px" class="icon" title="${_('Git repository')}" alt="${_('Git repository')}" src="${h.url("/images/icons/giticon.png")}"/>
51 <img style="margin-bottom:2px" class="icon" title="${_('Git repository')}" alt="${_('Git repository')}" src="${h.url('/images/icons/giticon.png')}"/>
52 %endif
52 %endif
53
53
54 ##PUBLIC/PRIVATE
54 ##PUBLIC/PRIVATE
55 %if c.dbrepo.private:
55 %if c.dbrepo.private:
56 <img style="margin-bottom:2px" class="icon" title="${_('private repository')}" alt="${_('private repository')}" src="${h.url("/images/icons/lock.png")}"/>
56 <img style="margin-bottom:2px" class="icon" title="${_('private repository')}" alt="${_('private repository')}" src="${h.url('/images/icons/lock.png')}"/>
57 %else:
57 %else:
58 <img style="margin-bottom:2px" class="icon" title="${_('public repository')}" alt="${_('public repository')}" src="${h.url("/images/icons/lock_open.png")}"/>
58 <img style="margin-bottom:2px" class="icon" title="${_('public repository')}" alt="${_('public repository')}" src="${h.url('/images/icons/lock_open.png')}"/>
59 %endif
59 %endif
60
60
61 ##REPO NAME
61 ##REPO NAME
@@ -67,7 +67,7 b''
67 <a href="${h.url('summary_home',repo_name=c.dbrepo.fork.repo_name)}">
67 <a href="${h.url('summary_home',repo_name=c.dbrepo.fork.repo_name)}">
68 <img class="icon" alt="${_('public')}"
68 <img class="icon" alt="${_('public')}"
69 title="${_('Fork of')} ${c.dbrepo.fork.repo_name}"
69 title="${_('Fork of')} ${c.dbrepo.fork.repo_name}"
70 src="${h.url("/images/icons/arrow_divide.png")}"/>
70 src="${h.url('/images/icons/arrow_divide.png')}"/>
71 ${_('Fork of')} ${c.dbrepo.fork.repo_name}
71 ${_('Fork of')} ${c.dbrepo.fork.repo_name}
72 </a>
72 </a>
73 </div>
73 </div>
@@ -78,7 +78,7 b''
78 <a href="${h.url(str(h.hide_credentials(c.dbrepo.clone_uri)))}">
78 <a href="${h.url(str(h.hide_credentials(c.dbrepo.clone_uri)))}">
79 <img class="icon" alt="${_('remote clone')}"
79 <img class="icon" alt="${_('remote clone')}"
80 title="${_('Clone from')} ${h.hide_credentials(c.dbrepo.clone_uri)}"
80 title="${_('Clone from')} ${h.hide_credentials(c.dbrepo.clone_uri)}"
81 src="${h.url("/images/icons/connect.png")}"/>
81 src="${h.url('/images/icons/connect.png')}"/>
82 ${_('Clone from')} ${h.hide_credentials(c.dbrepo.clone_uri)}
82 ${_('Clone from')} ${h.hide_credentials(c.dbrepo.clone_uri)}
83 </a>
83 </a>
84 </div>
84 </div>
@@ -151,7 +151,7 b''
151 %elif c.enable_downloads is False:
151 %elif c.enable_downloads is False:
152 ${_('Downloads are disabled for this repository')}
152 ${_('Downloads are disabled for this repository')}
153 %if h.HasPermissionAll('hg.admin')('enable stats on from summary'):
153 %if h.HasPermissionAll('hg.admin')('enable stats on from summary'):
154 [${h.link_to(_('enable'),h.url('edit_repo',repo_name=c.repo_name))}]
154 ${h.link_to(_('enable'),h.url('edit_repo',repo_name=c.repo_name),class_="ui-button-small")}
155 %endif
155 %endif
156 %else:
156 %else:
157 ${h.select('download_options',c.rhodecode_repo.get_changeset().raw_id,c.download_options)}
157 ${h.select('download_options',c.rhodecode_repo.get_changeset().raw_id,c.download_options)}
@@ -317,7 +317,7 b''
317 %if c.no_data:
317 %if c.no_data:
318 ${c.no_data_msg}
318 ${c.no_data_msg}
319 %if h.HasPermissionAll('hg.admin')('enable stats on from summary'):
319 %if h.HasPermissionAll('hg.admin')('enable stats on from summary'):
320 [${h.link_to(_('enable'),h.url('edit_repo',repo_name=c.repo_name))}]
320 ${h.link_to(_('enable'),h.url('edit_repo',repo_name=c.repo_name),class_="ui-button-small")}
321 %endif
321 %endif
322
322
323 %else:
323 %else:
@@ -13,7 +13,7 b' class TestChangelogController(TestContro'
13 """name="5e204e7583b9" type="checkbox" value="1" />"""
13 """name="5e204e7583b9" type="checkbox" value="1" />"""
14 in response.body)
14 in response.body)
15 self.assertTrue("""<span>commit 154: 5e204e7583b9@2010-08-10 """
15 self.assertTrue("""<span>commit 154: 5e204e7583b9@2010-08-10 """
16 """01:18:46</span>""" in response.body)
16 """02:18:46</span>""" in response.body)
17 self.assertTrue("""Small update at simplevcs app""" in response.body)
17 self.assertTrue("""Small update at simplevcs app""" in response.body)
18
18
19
19
@@ -43,7 +43,7 b' class TestChangelogController(TestContro'
43 """name="46ad32a4f974" type="checkbox" value="1" />"""
43 """name="46ad32a4f974" type="checkbox" value="1" />"""
44 in response.body)
44 in response.body)
45 self.assertTrue("""<span>commit 64: 46ad32a4f974@2010-04-20"""
45 self.assertTrue("""<span>commit 64: 46ad32a4f974@2010-04-20"""
46 """ 00:33:21</span>"""in response.body)
46 """ 01:33:21</span>"""in response.body)
47
47
48 self.assertTrue("""<span id="46ad32a4f974e45472a898c6b0acb600320"""
48 self.assertTrue("""<span id="46ad32a4f974e45472a898c6b0acb600320"""
49 """579b1" class="changed_total tooltip" """
49 """579b1" class="changed_total tooltip" """
@@ -145,8 +145,8 b' class TestLoginController(TestController'
145 'lastname':'test'})
145 'lastname':'test'})
146
146
147 self.assertEqual(response.status , '200 OK')
147 self.assertEqual(response.status , '200 OK')
148 assert 'An email address must contain a single @' in response.body
148 self.assertTrue('An email address must contain a single @' in response.body)
149 assert 'This username already exists' in response.body
149 self.assertTrue('This username already exists' in response.body)
150
150
151
151
152
152
@@ -160,7 +160,7 b' class TestLoginController(TestController'
160 'lastname':'test'})
160 'lastname':'test'})
161
161
162 self.assertEqual(response.status , '200 OK')
162 self.assertEqual(response.status , '200 OK')
163 assert 'Invalid characters in password' in response.body
163 self.assertTrue('Invalid characters in password' in response.body)
164
164
165
165
166 def test_register_password_mismatch(self):
166 def test_register_password_mismatch(self):
@@ -246,7 +246,7 b' class TestLoginController(TestController'
246
246
247 # GOOD KEY
247 # GOOD KEY
248
248
249 key = User.by_username(username).api_key
249 key = User.get_by_username(username).api_key
250
250
251 response = self.app.get(url(controller='login',
251 response = self.app.get(url(controller='login',
252 action='password_reset_confirmation',
252 action='password_reset_confirmation',
@@ -41,7 +41,7 b' class TestSummaryController(TestControll'
41
41
42
42
43 def _enable_stats(self):
43 def _enable_stats(self):
44 r = Repository.by_repo_name(HG_REPO)
44 r = Repository.get_by_repo_name(HG_REPO)
45 r.enable_statistics = True
45 r.enable_statistics = True
46 self.sa.add(r)
46 self.sa.add(r)
47 self.sa.commit()
47 self.sa.commit()
@@ -6,9 +6,21 b''
6 Test suite for making push/pull operations
6 Test suite for making push/pull operations
7
7
8 :created_on: Dec 30, 2010
8 :created_on: Dec 30, 2010
9 :copyright: (c) 2010 by marcink.
9 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
10 :license: LICENSE_NAME, see LICENSE_FILE for more details.
10 :license: GPLv3, see COPYING for more details.
11 """
11 """
12 # This program is free software: you can redistribute it and/or modify
13 # it under the terms of the GNU General Public License as published by
14 # the Free Software Foundation, either version 3 of the License, or
15 # (at your option) any later version.
16 #
17 # This program is distributed in the hope that it will be useful,
18 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # GNU General Public License for more details.
21 #
22 # You should have received a copy of the GNU General Public License
23 # along with this program. If not, see <http://www.gnu.org/licenses/>.
12
24
13 import os
25 import os
14 import time
26 import time
@@ -29,7 +41,7 b' from sqlalchemy import engine_from_confi'
29 from rhodecode.lib.utils import add_cache
41 from rhodecode.lib.utils import add_cache
30 from rhodecode.model import init_model
42 from rhodecode.model import init_model
31 from rhodecode.model import meta
43 from rhodecode.model import meta
32 from rhodecode.model.db import User, Repository
44 from rhodecode.model.db import User, Repository, UserLog
33 from rhodecode.lib.auth import get_crypt_password
45 from rhodecode.lib.auth import get_crypt_password
34
46
35 from rhodecode.tests import TESTS_TMP_PATH, NEW_HG_REPO, HG_REPO
47 from rhodecode.tests import TESTS_TMP_PATH, NEW_HG_REPO, HG_REPO
@@ -44,8 +56,8 b' add_cache(conf)'
44 USER = 'test_admin'
56 USER = 'test_admin'
45 PASS = 'test12'
57 PASS = 'test12'
46 HOST = '127.0.0.1:5000'
58 HOST = '127.0.0.1:5000'
47 DEBUG = bool(int(sys.argv[1]))
59 DEBUG = True if sys.argv[1:] else False
48 print 'DEBUG:',DEBUG
60 print 'DEBUG:', DEBUG
49 log = logging.getLogger(__name__)
61 log = logging.getLogger(__name__)
50
62
51
63
@@ -70,15 +82,17 b' class Command(object):'
70
82
71
83
72 def test_wrapp(func):
84 def test_wrapp(func):
73
85
74 def __wrapp(*args,**kwargs):
86 def __wrapp(*args, **kwargs):
75 print '###%s###' %func.__name__
87 print '>>>%s' % func.__name__
76 try:
88 try:
77 res = func(*args,**kwargs)
89 res = func(*args, **kwargs)
78 except:
90 except Exception, e:
79 print '--%s failed--' % func.__name__
91 print ('###############\n-'
80 return
92 '--%s failed %s--\n'
81 print 'ok'
93 '###############\n' % (func.__name__, e))
94 sys.exit()
95 print '++OK++'
82 return res
96 return res
83 return __wrapp
97 return __wrapp
84
98
@@ -90,20 +104,20 b' def get_session():'
90
104
91
105
92 def create_test_user(force=True):
106 def create_test_user(force=True):
93 print 'creating test user'
107 print '\tcreating test user'
94 sa = get_session()
108 sa = get_session()
95
109
96 user = sa.query(User).filter(User.username == USER).scalar()
110 user = sa.query(User).filter(User.username == USER).scalar()
97
111
98 if force and user is not None:
112 if force and user is not None:
99 print 'removing current user'
113 print '\tremoving current user'
100 for repo in sa.query(Repository).filter(Repository.user == user).all():
114 for repo in sa.query(Repository).filter(Repository.user == user).all():
101 sa.delete(repo)
115 sa.delete(repo)
102 sa.delete(user)
116 sa.delete(user)
103 sa.commit()
117 sa.commit()
104
118
105 if user is None or force:
119 if user is None or force:
106 print 'creating new one'
120 print '\tcreating new one'
107 new_usr = User()
121 new_usr = User()
108 new_usr.username = USER
122 new_usr.username = USER
109 new_usr.password = get_crypt_password(PASS)
123 new_usr.password = get_crypt_password(PASS)
@@ -115,7 +129,7 b' def create_test_user(force=True):'
115 sa.add(new_usr)
129 sa.add(new_usr)
116 sa.commit()
130 sa.commit()
117
131
118 print 'done'
132 print '\tdone'
119
133
120
134
121 def create_test_repo(force=True):
135 def create_test_repo(force=True):
@@ -130,7 +144,7 b' def create_test_repo(force=True):'
130 repo = sa.query(Repository).filter(Repository.repo_name == HG_REPO).scalar()
144 repo = sa.query(Repository).filter(Repository.repo_name == HG_REPO).scalar()
131
145
132 if repo is None:
146 if repo is None:
133 print 'repo not found creating'
147 print '\trepo not found creating'
134
148
135 form_data = {'repo_name':HG_REPO,
149 form_data = {'repo_name':HG_REPO,
136 'repo_type':'hg',
150 'repo_type':'hg',
@@ -144,12 +158,13 b' def create_test_repo(force=True):'
144 def set_anonymous_access(enable=True):
158 def set_anonymous_access(enable=True):
145 sa = get_session()
159 sa = get_session()
146 user = sa.query(User).filter(User.username == 'default').one()
160 user = sa.query(User).filter(User.username == 'default').one()
161 sa.expire(user)
147 user.active = enable
162 user.active = enable
148 sa.add(user)
163 sa.add(user)
149 sa.commit()
164 sa.commit()
150 sa.remove()
165 sa.remove()
151
166 import time;time.sleep(3)
152 print 'anonymous access is now:',enable
167 print '\tanonymous access is now:', enable
153
168
154
169
155 def get_anonymous_access():
170 def get_anonymous_access():
@@ -173,13 +188,13 b' def test_clone_with_credentials(no_error'
173 except OSError:
188 except OSError:
174 raise
189 raise
175
190
176 print 'checking if anonymous access is enabled'
191 print '\tchecking if anonymous access is enabled'
177 anonymous_access = get_anonymous_access()
192 anonymous_access = get_anonymous_access()
178 if anonymous_access:
193 if anonymous_access:
179 print 'enabled, disabling it '
194 print '\tenabled, disabling it '
180 set_anonymous_access(enable=False)
195 set_anonymous_access(enable=False)
181 time.sleep(1)
196 time.sleep(1)
182
197
183 clone_url = 'http://%(user)s:%(pass)s@%(host)s/%(cloned_repo)s %(dest)s' % \
198 clone_url = 'http://%(user)s:%(pass)s@%(host)s/%(cloned_repo)s %(dest)s' % \
184 {'user':USER,
199 {'user':USER,
185 'pass':PASS,
200 'pass':PASS,
@@ -206,13 +221,13 b' def test_clone_anonymous():'
206 raise
221 raise
207
222
208
223
209 print 'checking if anonymous access is enabled'
224 print '\tchecking if anonymous access is enabled'
210 anonymous_access = get_anonymous_access()
225 anonymous_access = get_anonymous_access()
211 if not anonymous_access:
226 if not anonymous_access:
212 print 'not enabled, enabling it '
227 print '\tnot enabled, enabling it '
213 set_anonymous_access(enable=True)
228 set_anonymous_access(enable=True)
214 time.sleep(1)
229 time.sleep(1)
215
230
216 clone_url = 'http://%(host)s/%(cloned_repo)s %(dest)s' % \
231 clone_url = 'http://%(host)s/%(cloned_repo)s %(dest)s' % \
217 {'user':USER,
232 {'user':USER,
218 'pass':PASS,
233 'pass':PASS,
@@ -227,7 +242,7 b' def test_clone_anonymous():'
227
242
228 #disable if it was enabled
243 #disable if it was enabled
229 if not anonymous_access:
244 if not anonymous_access:
230 print 'disabling anonymous access'
245 print '\tdisabling anonymous access'
231 set_anonymous_access(enable=False)
246 set_anonymous_access(enable=False)
232
247
233 @test_wrapp
248 @test_wrapp
@@ -241,12 +256,12 b' def test_clone_wrong_credentials():'
241 except OSError:
256 except OSError:
242 raise
257 raise
243
258
244 print 'checking if anonymous access is enabled'
259 print '\tchecking if anonymous access is enabled'
245 anonymous_access = get_anonymous_access()
260 anonymous_access = get_anonymous_access()
246 if anonymous_access:
261 if anonymous_access:
247 print 'enabled, disabling it '
262 print '\tenabled, disabling it '
248 set_anonymous_access(enable=False)
263 set_anonymous_access(enable=False)
249
264
250 clone_url = 'http://%(user)s:%(pass)s@%(host)s/%(cloned_repo)s %(dest)s' % \
265 clone_url = 'http://%(user)s:%(pass)s@%(host)s/%(cloned_repo)s %(dest)s' % \
251 {'user':USER + 'error',
266 {'user':USER + 'error',
252 'pass':PASS,
267 'pass':PASS,
@@ -257,7 +272,7 b' def test_clone_wrong_credentials():'
257 stdout, stderr = Command(cwd).execute('hg clone', clone_url)
272 stdout, stderr = Command(cwd).execute('hg clone', clone_url)
258
273
259 if not """abort: authorization failed""" in stderr:
274 if not """abort: authorization failed""" in stderr:
260 raise Exception('Failure')
275 raise Exception('Failure')
261
276
262 @test_wrapp
277 @test_wrapp
263 def test_pull():
278 def test_pull():
@@ -335,7 +350,7 b' def test_push_wrong_path():'
335 try:
350 try:
336 shutil.rmtree(path, ignore_errors=True)
351 shutil.rmtree(path, ignore_errors=True)
337 os.makedirs(path)
352 os.makedirs(path)
338 print 'made dirs %s' % jn(path)
353 print '\tmade dirs %s' % jn(path)
339 except OSError:
354 except OSError:
340 raise
355 raise
341
356
@@ -361,19 +376,37 b' def test_push_wrong_path():'
361 if not """abort: HTTP Error 403: Forbidden""" in stderr:
376 if not """abort: HTTP Error 403: Forbidden""" in stderr:
362 raise Exception('Failure')
377 raise Exception('Failure')
363
378
379 @test_wrapp
380 def get_logs():
381 sa = get_session()
382 return len(sa.query(UserLog).all())
383
384 @test_wrapp
385 def test_logs(initial):
386 sa = get_session()
387 logs = sa.query(UserLog).all()
388 operations = 7
389 if initial + operations != len(logs):
390 raise Exception("missing number of logs %s vs %s" % (initial, len(logs)))
391
364
392
365 if __name__ == '__main__':
393 if __name__ == '__main__':
366 create_test_user(force=False)
394 create_test_user(force=False)
367 create_test_repo()
395 create_test_repo()
368
396
397 initial_logs = get_logs()
398
369 # test_push_modify_file()
399 # test_push_modify_file()
370 test_clone_with_credentials()
400 test_clone_with_credentials()
371 test_clone_wrong_credentials()
401 test_clone_wrong_credentials()
372
402
373
403
374 test_push_new_file(commits=2, with_clone=True)
404 test_push_new_file(commits=2, with_clone=True)
375 #
405
406 test_clone_anonymous()
376 test_push_wrong_path()
407 test_push_wrong_path()
377
408
378 test_clone_anonymous()
409
379 test_push_wrong_credentials() No newline at end of file
410 test_push_wrong_credentials()
411
412 test_logs(initial_logs)
@@ -7,9 +7,21 b''
7 Package for testing various lib/helper functions in rhodecode
7 Package for testing various lib/helper functions in rhodecode
8
8
9 :created_on: Jun 9, 2011
9 :created_on: Jun 9, 2011
10 :copyright: (c) 2011 by marcink.
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 :license: LICENSE_NAME, see LICENSE_FILE for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
17 #
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
22 #
23 # You should have received a copy of the GNU General Public License
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
13
25
14
26
15
27
@@ -1,2 +1,153 b''
1 import os
1 import unittest
2 import unittest
2 from rhodecode.tests import *
3 from rhodecode.tests import *
4
5 from rhodecode.model.repos_group import ReposGroupModel
6 from rhodecode.model.repo import RepoModel
7 from rhodecode.model.db import Group, User
8 from sqlalchemy.exc import IntegrityError
9
10 class TestReposGroups(unittest.TestCase):
11
12 def setUp(self):
13 self.g1 = self.__make_group('test1', skip_if_exists=True)
14 self.g2 = self.__make_group('test2', skip_if_exists=True)
15 self.g3 = self.__make_group('test3', skip_if_exists=True)
16
17 def tearDown(self):
18 print 'out'
19
20 def __check_path(self, *path):
21 path = [TESTS_TMP_PATH] + list(path)
22 path = os.path.join(*path)
23 return os.path.isdir(path)
24
25 def _check_folders(self):
26 print os.listdir(TESTS_TMP_PATH)
27
28 def __make_group(self, path, desc='desc', parent_id=None,
29 skip_if_exists=False):
30
31 gr = Group.get_by_group_name(path)
32 if gr and skip_if_exists:
33 return gr
34
35 form_data = dict(group_name=path,
36 group_description=desc,
37 group_parent_id=parent_id)
38 gr = ReposGroupModel().create(form_data)
39 return gr
40
41 def __delete_group(self, id_):
42 ReposGroupModel().delete(id_)
43
44
45 def __update_group(self, id_, path, desc='desc', parent_id=None):
46 form_data = dict(group_name=path,
47 group_description=desc,
48 group_parent_id=parent_id)
49
50 gr = ReposGroupModel().update(id_, form_data)
51 return gr
52
53 def test_create_group(self):
54 g = self.__make_group('newGroup')
55 self.assertEqual(g.full_path, 'newGroup')
56
57 self.assertTrue(self.__check_path('newGroup'))
58
59
60 def test_create_same_name_group(self):
61 self.assertRaises(IntegrityError, lambda:self.__make_group('newGroup'))
62
63
64 def test_same_subgroup(self):
65 sg1 = self.__make_group('sub1', parent_id=self.g1.group_id)
66 self.assertEqual(sg1.parent_group, self.g1)
67 self.assertEqual(sg1.full_path, 'test1/sub1')
68 self.assertTrue(self.__check_path('test1', 'sub1'))
69
70 ssg1 = self.__make_group('subsub1', parent_id=sg1.group_id)
71 self.assertEqual(ssg1.parent_group, sg1)
72 self.assertEqual(ssg1.full_path, 'test1/sub1/subsub1')
73 self.assertTrue(self.__check_path('test1', 'sub1', 'subsub1'))
74
75
76 def test_remove_group(self):
77 sg1 = self.__make_group('deleteme')
78 self.__delete_group(sg1.group_id)
79
80 self.assertEqual(Group.get(sg1.group_id), None)
81 self.assertFalse(self.__check_path('deteteme'))
82
83 sg1 = self.__make_group('deleteme', parent_id=self.g1.group_id)
84 self.__delete_group(sg1.group_id)
85
86 self.assertEqual(Group.get(sg1.group_id), None)
87 self.assertFalse(self.__check_path('test1', 'deteteme'))
88
89
90 def test_rename_single_group(self):
91 sg1 = self.__make_group('initial')
92
93 new_sg1 = self.__update_group(sg1.group_id, 'after')
94 self.assertTrue(self.__check_path('after'))
95 self.assertEqual(Group.get_by_group_name('initial'), None)
96
97
98 def test_update_group_parent(self):
99
100 sg1 = self.__make_group('initial', parent_id=self.g1.group_id)
101
102 new_sg1 = self.__update_group(sg1.group_id, 'after', parent_id=self.g1.group_id)
103 self.assertTrue(self.__check_path('test1', 'after'))
104 self.assertEqual(Group.get_by_group_name('test1/initial'), None)
105
106
107 new_sg1 = self.__update_group(sg1.group_id, 'after', parent_id=self.g3.group_id)
108 self.assertTrue(self.__check_path('test3', 'after'))
109 self.assertEqual(Group.get_by_group_name('test3/initial'), None)
110
111
112 new_sg1 = self.__update_group(sg1.group_id, 'hello')
113 self.assertTrue(self.__check_path('hello'))
114
115 self.assertEqual(Group.get_by_group_name('hello'), new_sg1)
116
117
118
119 def test_subgrouping_with_repo(self):
120
121 g1 = self.__make_group('g1')
122 g2 = self.__make_group('g2')
123
124 # create new repo
125 form_data = dict(repo_name='john',
126 repo_name_full='john',
127 fork_name=None,
128 description=None,
129 repo_group=None,
130 private=False,
131 repo_type='hg',
132 clone_uri=None)
133 cur_user = User.get_by_username(TEST_USER_ADMIN_LOGIN)
134 r = RepoModel().create(form_data, cur_user)
135
136 self.assertEqual(r.repo_name, 'john')
137
138 # put repo into group
139 form_data = form_data
140 form_data['repo_group'] = g1.group_id
141 form_data['perms_new'] = []
142 form_data['perms_updates'] = []
143 RepoModel().update(r.repo_name, form_data)
144 self.assertEqual(r.repo_name, 'g1/john')
145
146
147 self.__update_group(g1.group_id, 'g1', parent_id=g2.group_id)
148 self.assertTrue(self.__check_path('g2', 'g1'))
149
150 # test repo
151 self.assertEqual(r.repo_name, os.path.join('g2', 'g1', r.just_name))
152
153
@@ -11,10 +11,11 b' if py_version < (2, 5):'
11
11
12 requirements = [
12 requirements = [
13 "Pylons==1.0.0",
13 "Pylons==1.0.0",
14 "Beaker==1.5.4",
14 "WebHelpers>=1.2",
15 "WebHelpers>=1.2",
15 "formencode==1.2.4",
16 "formencode==1.2.4",
16 "SQLAlchemy>=0.7.2,<0.8",
17 "SQLAlchemy==0.7.3",
17 "Mako>=0.4.2",
18 "Mako==0.5.0",
18 "pygments>=1.4",
19 "pygments>=1.4",
19 "mercurial>=1.9.3,<2.0",
20 "mercurial>=1.9.3,<2.0",
20 "whoosh<1.8",
21 "whoosh<1.8",
@@ -22,7 +23,7 b' requirements = ['
22 "babel",
23 "babel",
23 "python-dateutil>=1.5.0,<2.0.0",
24 "python-dateutil>=1.5.0,<2.0.0",
24 "dulwich>=0.8.0",
25 "dulwich>=0.8.0",
25 "vcs>=0.2.1",
26 "vcs==0.2.2",
26 "webob==1.0.8"
27 "webob==1.0.8"
27 ]
28 ]
28
29
@@ -33,7 +34,7 b" classifiers = ['Development Status :: 5 "
33 'Environment :: Web Environment',
34 'Environment :: Web Environment',
34 'Framework :: Pylons',
35 'Framework :: Pylons',
35 'Intended Audience :: Developers',
36 'Intended Audience :: Developers',
36 'License :: OSI Approved :: BSD License',
37 'License :: OSI Approved :: GNU General Public License (GPL)',
37 'Operating System :: OS Independent',
38 'Operating System :: OS Independent',
38 'Programming Language :: Python',
39 'Programming Language :: Python',
39 'Programming Language :: Python :: 2.5',
40 'Programming Language :: Python :: 2.5',
@@ -23,6 +23,7 b' pdebug = false'
23 #smtp_password =
23 #smtp_password =
24 #smtp_port =
24 #smtp_port =
25 #smtp_use_tls = false
25 #smtp_use_tls = false
26 #smtp_use_ssl = true
26
27
27 [server:main]
28 [server:main]
28 ##nr of threads to spawn
29 ##nr of threads to spawn
@@ -49,6 +50,7 b' app_instance_uuid = develop-test'
49 cut_off_limit = 256000
50 cut_off_limit = 256000
50 force_https = false
51 force_https = false
51 commit_parse_limit = 25
52 commit_parse_limit = 25
53 use_gravatar = true
52
54
53 ####################################
55 ####################################
54 ### CELERY CONFIG ####
56 ### CELERY CONFIG ####
@@ -93,7 +95,6 b' beaker.cache.short_term.expire=60'
93 beaker.cache.long_term.type=memory
95 beaker.cache.long_term.type=memory
94 beaker.cache.long_term.expire=36000
96 beaker.cache.long_term.expire=36000
95
97
96
97 beaker.cache.sql_cache_short.type=memory
98 beaker.cache.sql_cache_short.type=memory
98 beaker.cache.sql_cache_short.expire=10
99 beaker.cache.sql_cache_short.expire=10
99
100
@@ -150,13 +151,13 b' sqlalchemy.convert_unicode = true'
150 ### LOGGING CONFIGURATION ####
151 ### LOGGING CONFIGURATION ####
151 ################################
152 ################################
152 [loggers]
153 [loggers]
153 keys = root, routes, rhodecode, sqlalchemy,beaker,templates
154 keys = root, routes, rhodecode, sqlalchemy, beaker, templates
154
155
155 [handlers]
156 [handlers]
156 keys = console
157 keys = console
157
158
158 [formatters]
159 [formatters]
159 keys = generic,color_formatter
160 keys = generic, color_formatter
160
161
161 #############
162 #############
162 ## LOGGERS ##
163 ## LOGGERS ##
@@ -167,9 +168,10 b' handlers = console'
167
168
168 [logger_routes]
169 [logger_routes]
169 level = ERROR
170 level = ERROR
170 handlers = console
171 handlers =
171 qualname = routes.middleware
172 qualname = routes.middleware
172 # "level = DEBUG" logs the route matched and routing variables.
173 # "level = DEBUG" logs the route matched and routing variables.
174 propagate = 1
173
175
174 [logger_beaker]
176 [logger_beaker]
175 level = DEBUG
177 level = DEBUG
@@ -185,9 +187,9 b' propagate = 1'
185
187
186 [logger_rhodecode]
188 [logger_rhodecode]
187 level = ERROR
189 level = ERROR
188 handlers = console
190 handlers =
189 qualname = rhodecode
191 qualname = rhodecode
190 propagate = 0
192 propagate = 1
191
193
192 [logger_sqlalchemy]
194 [logger_sqlalchemy]
193 level = ERROR
195 level = ERROR
@@ -203,7 +205,7 b' propagate = 0'
203 class = StreamHandler
205 class = StreamHandler
204 args = (sys.stderr,)
206 args = (sys.stderr,)
205 level = NOTSET
207 level = NOTSET
206 formatter = color_formatter
208 formatter = generic
207
209
208 ################
210 ################
209 ## FORMATTERS ##
211 ## FORMATTERS ##
General Comments 0
You need to be logged in to leave comments. Login now