##// 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 12 Augosto Hermann <augusto.herrmann@planejamento.gov.br>
13 13 Ankit Solanki <ankit.solanki@gmail.com>
14 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 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 30 1.2.1 (**2011-10-08**)
7 31 ======================
8 32
@@ -17,11 +41,9 b' fixes'
17 41 - gui fixes
18 42 - fixed logger
19 43
20
21 44 1.2.0 (**2011-10-07**)
22 45 ======================
23 46
24
25 47 news
26 48 ----
27 49
@@ -443,8 +443,8 b' in the production.ini file::'
443 443 In order to not have the statics served by the application. This improves speed.
444 444
445 445
446 Apache virtual host example
447 ---------------------------
446 Apache virtual host reverse proxy example
447 -----------------------------------------
448 448
449 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 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 532 Example wsgi dispatch script::
508 533
@@ -512,6 +537,9 b' Example wsgi dispatch script::'
512 537
513 538 # sometimes it's needed to set the curent dir
514 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 544 from paste.deploy import loadapp
517 545 from paste.script.util.logging_config import fileConfig
@@ -519,6 +547,10 b' Example wsgi dispatch script::'
519 547 fileConfig('/home/web/rhodecode/production.ini')
520 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 555 Other configuration files
524 556 -------------------------
@@ -25,7 +25,7 b''
25 25 # along with this program. If not, see <http://www.gnu.org/licenses/>.
26 26 import platform
27 27
28 VERSION = (1, 2, 1)
28 VERSION = (1, 2, 2)
29 29 __version__ = '.'.join((str(each) for each in VERSION[:4]))
30 30 __dbversion__ = 3 #defines current db version for migrations
31 31 __platform__ = platform.system()
@@ -35,7 +35,7 b" PLATFORM_WIN = ('Windows')"
35 35 PLATFORM_OTHERS = ('Linux', 'Darwin', 'FreeBSD', 'OpenBSD', 'SunOS')
36 36
37 37 try:
38 from rhodecode.lib.utils import get_current_revision
38 from rhodecode.lib import get_current_revision
39 39 _rev = get_current_revision()
40 40 except ImportError:
41 41 #this is needed when doing some setup.py operations
@@ -19,10 +19,10 b' def make_map(config):'
19 19 always_scan=config['debug'])
20 20 rmap.minimization = False
21 21 rmap.explicit = False
22
22
23 23 from rhodecode.lib.utils import is_valid_repo
24 24 from rhodecode.lib.utils import is_valid_repos_group
25
25
26 26 def check_repo(environ, match_dict):
27 27 """
28 28 check for valid repository for proper 404 handling
@@ -30,7 +30,7 b' def make_map(config):'
30 30 :param environ:
31 31 :param match_dict:
32 32 """
33
33
34 34 repo_name = match_dict.get('repo_name')
35 35 return is_valid_repo(repo_name, config['base_path'])
36 36
@@ -42,7 +42,7 b' def make_map(config):'
42 42 :param match_dict:
43 43 """
44 44 repos_group_name = match_dict.get('group_name')
45
45
46 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 333 # REPOSITORY ROUTES
334 334 #==========================================================================
335 335 rmap.connect('summary_home', '/{repo_name:.*}',
336 controller='summary',
336 controller='summary',
337 337 conditions=dict(function=check_repo))
338
339 # rmap.connect('repo_group_home', '/{group_name:.*}',
340 # controller='admin/repos_groups',action="show_by_name",
341 # conditions=dict(function=check_group))
342
338
339 rmap.connect('repos_group_home', '/{group_name:.*}',
340 controller='admin/repos_groups', action="show_by_name",
341 conditions=dict(function=check_group))
342
343 343 rmap.connect('changeset_home', '/{repo_name:.*}/changeset/{revision}',
344 344 controller='changeset', revision='tip',
345 345 conditions=dict(function=check_repo))
@@ -144,7 +144,7 b' class PermissionsController(BaseControll'
144 144 c.create_choices = self.create_choices
145 145
146 146 if id == 'default':
147 default_user = User.by_username('default')
147 default_user = User.get_by_username('default')
148 148 defaults = {'_method': 'put',
149 149 'anonymous': default_user.active}
150 150
@@ -64,20 +64,10 b' class ReposController(BaseController):'
64 64 super(ReposController, self).__before__()
65 65
66 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 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 71 c.users_array = repo_model.get_users_js()
82 72 c.users_groups_array = repo_model.get_users_groups_js()
83 73
@@ -89,8 +79,8 b' class ReposController(BaseController):'
89 79 """
90 80 self.__load_defaults()
91 81
92 c.repo_info = db_repo = Repository.by_repo_name(repo_name)
93 repo = scm_repo = db_repo.scm_instance
82 c.repo_info = db_repo = Repository.get_by_repo_name(repo_name)
83 repo = db_repo.scm_instance
94 84
95 85 if c.repo_info is None:
96 86 h.flash(_('%s repository is not mapped to db perhaps'
@@ -101,7 +91,7 b' class ReposController(BaseController):'
101 91
102 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 95 c.in_public_journal = self.sa.query(UserFollowing)\
106 96 .filter(UserFollowing.user_id == c.default_user_id)\
107 97 .filter(UserFollowing.follows_repository == c.repo_info).scalar()
@@ -234,11 +224,11 b' class ReposController(BaseController):'
234 224 repo_groups=c.repo_groups_choices)()
235 225 try:
236 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 228 invalidate_cache('get_repo_cached_%s' % repo_name)
239 229 h.flash(_('Repository %s updated successfully' % repo_name),
240 230 category='success')
241 changed_name = form_result['repo_name_full']
231 changed_name = repo.repo_name
242 232 action_logger(self.rhodecode_user, 'admin_updated_repo',
243 233 changed_name, '', self.sa)
244 234
@@ -381,8 +371,8 b' class ReposController(BaseController):'
381 371 token = get_token()
382 372 if cur_token == token:
383 373 try:
384 repo_id = Repository.by_repo_name(repo_name).repo_id
385 user_id = User.by_username('default').user_id
374 repo_id = Repository.get_by_repo_name(repo_name).repo_id
375 user_id = User.get_by_username('default').user_id
386 376 self.scm_model.toggle_following_repo(repo_id, user_id)
387 377 h.flash(_('Updated repository visibility in public journal'),
388 378 category='success')
@@ -9,9 +9,10 b' from pylons import request, response, se'
9 9 from pylons.controllers.util import abort, redirect
10 10 from pylons.i18n.translation import _
11 11
12 from sqlalchemy.exc import IntegrityError
13
12 14 from rhodecode.lib import helpers as h
13 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator, \
14 HasPermissionAnyDecorator
15 from rhodecode.lib.auth import LoginRequired, HasPermissionAnyDecorator
15 16 from rhodecode.lib.base import BaseController, render
16 17 from rhodecode.model.db import Group
17 18 from rhodecode.model.repos_group import ReposGroupModel
@@ -31,19 +32,7 b' class ReposGroupsController(BaseControll'
31 32 super(ReposGroupsController, self).__before__()
32 33
33 34 def __load_defaults(self):
34
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])
35 c.repo_groups = Group.groups_choices()
47 36 c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups)
48 37
49 38 def __load_data(self, group_id):
@@ -58,6 +47,8 b' class ReposGroupsController(BaseControll'
58 47
59 48 data = repo_group.get_dict()
60 49
50 data['group_name'] = repo_group.name
51
61 52 return data
62 53
63 54 @HasPermissionAnyDecorator('hg.admin')
@@ -169,13 +160,28 b' class ReposGroupsController(BaseControll'
169 160 repos_group_model.delete(id)
170 161 h.flash(_('removed repos group %s' % gr.group_name), category='success')
171 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 174 except Exception:
173 175 log.error(traceback.format_exc())
174 h.flash(_('error occurred during deletion of repos group %s' % gr.group_name),
175 category='error')
176 h.flash(_('error occurred during deletion of repos '
177 'group %s' % gr.group_name), category='error')
176 178
177 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 185 def show(self, id, format='html'):
180 186 """GET /repos_groups/id: Show a specific item"""
181 187 # url('repos_group', id=ID)
@@ -366,17 +366,7 b' class SettingsController(BaseController)'
366 366 def create_repository(self):
367 367 """GET /_admin/create_repository: Form to create a new item"""
368 368
369 c.repo_groups = [('', '')]
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])
369 c.repo_groups = Group.groups_choices()
380 370 c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups)
381 371
382 372 new_repo = request.GET.get('repo', '')
@@ -64,8 +64,7 b' class LoginController(BaseController):'
64 64 c.form_result = login_form.to_python(dict(request.POST))
65 65 #form checks for username/password, now we're authenticated
66 66 username = c.form_result['username']
67 user = User.by_username(username,
68 case_insensitive=True)
67 user = User.get_by_username(username, case_insensitive=True)
69 68 auth_user = AuthUser(user.user_id)
70 69 auth_user.set_authenticated()
71 70 session['rhodecode_user'] = auth_user
@@ -95,8 +94,7 b' class LoginController(BaseController):'
95 94 def register(self):
96 95 user_model = UserModel()
97 96 c.auto_active = False
98 for perm in user_model.get_by_username('default',
99 cache=False).user_perms:
97 for perm in User.get_by_username('default').user_perms:
100 98 if perm.permission.permission_name == 'hg.register.auto_activate':
101 99 c.auto_active = True
102 100 break
@@ -23,6 +23,8 b''
23 23 # You should have received a copy of the GNU General Public License
24 24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 25
26 import os
27
26 28 def __get_lem():
27 29 from pygments import lexers
28 30 from string import lower
@@ -379,3 +381,24 b' def get_changeset_safe(repo, rev):'
379 381 from rhodecode.lib.utils import EmptyChangeset
380 382 cs = EmptyChangeset(requested_revision=rev)
381 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 6 authentication and permission libraries
7 7
8 8 :created_on: Apr 4, 2010
9 :copyright: (c) 2010 by marcink.
10 :license: LICENSE_NAME, see LICENSE_FILE for more details.
9 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
10 :license: GPLv3, see COPYING for more details.
11 11 """
12 12 # This program is free software: you can redistribute it and/or modify
13 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 49 from rhodecode.model import meta
50 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 53 log = logging.getLogger(__name__)
54 54
@@ -151,7 +151,7 b' def authenticate(username, password):'
151 151 """
152 152
153 153 user_model = UserModel()
154 user = user_model.get_by_username(username, cache=False)
154 user = User.get_by_username(username)
155 155
156 156 log.debug('Authenticating user using RhodeCode account')
157 157 if user is not None and not user.ldap_dn:
@@ -170,8 +170,7 b' def authenticate(username, password):'
170 170
171 171 else:
172 172 log.debug('Regular authentication failed')
173 user_obj = user_model.get_by_username(username, cache=False,
174 case_insensitive=True)
173 user_obj = User.get_by_username(username, case_insensitive=True)
175 174
176 175 if user_obj is not None and not user_obj.ldap_dn:
177 176 log.debug('this user already exists as non ldap')
@@ -252,7 +251,7 b' class AuthUser(object):'
252 251
253 252 def propagate_data(self):
254 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 255 if self._api_key and self._api_key != self.anonymous_user.api_key:
257 256 #try go get user by api key
258 257 log.debug('Auth User lookup by API KEY %s', self._api_key)
@@ -7,8 +7,8 b''
7 7 repositories and send it to backup server using RSA key via ssh.
8 8
9 9 :created_on: Feb 28, 2010
10 :copyright: (c) 2010 by marcink.
11 :license: LICENSE_NAME, see LICENSE_FILE for more details.
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
12 12 """
13 13 # This program is free software: you can redistribute it and/or modify
14 14 # it under the terms of the GNU General Public License as published by
@@ -67,7 +67,7 b' class BaseRepoController(BaseController)'
67 67 super(BaseRepoController, self).__before__()
68 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 71 c.rhodecode_repo = c.rhodecode_db_repo.scm_instance
72 72
73 73 if c.rhodecode_repo is None:
@@ -94,11 +94,11 b' def __get_lockkey(func, *fargs, **fkwarg'
94 94 def locked_task(func):
95 95 def __wrapper(func, *fargs, **fkwargs):
96 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 99 log.info('running task with lockkey %s', lockkey)
100 100 try:
101 l = DaemonLock(jn(lockkey_path, lockkey))
101 l = DaemonLock(file_=jn(lockkey_path, lockkey))
102 102 ret = func(*fargs, **fkwargs)
103 103 l.release()
104 104 return ret
@@ -97,10 +97,11 b' def get_commits_stats(repo_name, ts_min_'
97 97
98 98 lockkey = __get_lockkey('get_commits_stats', repo_name, ts_min_y,
99 99 ts_max_y)
100 lockkey_path = dn(dn(dn(dn(os.path.abspath(__file__)))))
100 lockkey_path = config['here']
101
101 102 log.info('running task with lockkey %s', lockkey)
102 103 try:
103 lock = l = DaemonLock(jn(lockkey_path, lockkey))
104 lock = l = DaemonLock(file_=jn(lockkey_path, lockkey))
104 105
105 106 #for js data compatibilty cleans the key for person from '
106 107 akc = lambda k: person(k).replace('"', "")
@@ -24,6 +24,9 b''
24 24 # You should have received a copy of the GNU General Public License
25 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 31 # json
29 32 #==============================================================================
@@ -358,3 +361,19 b' class OrderedDict(_odict, dict):'
358 361 # OrderedSet
359 362 #==============================================================================
360 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 6 Set of custom exceptions used in RhodeCode
7 7
8 8 :created_on: Nov 17, 2010
9 :copyright: (c) 2010 by marcink.
10 :license: LICENSE_NAME, see LICENSE_FILE for more details.
9 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
10 :license: GPLv3, see COPYING for more details.
11 11 """
12 12 # This program is free software: you can redistribute it and/or modify
13 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 598 return repo_name
599 599 else:
600 600 def make_link(group):
601 return link_to(group.group_name, url('repos_group',
602 id=group.group_id))
601 return link_to(group.name, url('repos_group_home',
602 group_name=group.group_name))
603 603 return literal(' &raquo; '.join(map(make_link, groups)) + \
604 604 " &raquo; " + repo_name)
605 605
606
607 606 def fancy_file_stats(stats):
608 607 """
609 608 Displays a fancy two colored bar for number of added/deleted
@@ -101,7 +101,7 b' class MakeIndex(BasePasterCommand):'
101 101 from rhodecode.lib.pidlock import LockHeld, DaemonLock
102 102 from rhodecode.lib.indexers.daemon import WhooshIndexingDaemon
103 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 105 WhooshIndexingDaemon(index_location=index_location,
106 106 repo_location=repo_location,
107 107 repo_list=repo_list)\
@@ -262,7 +262,7 b' class SimpleGit(object):'
262 262 return repo_name
263 263
264 264 def __get_user(self, username):
265 return User.by_username(username)
265 return User.get_by_username(username)
266 266
267 267 def __get_action(self, environ):
268 268 """Maps git request commands into a pull or push command.
@@ -76,7 +76,7 b' class SimpleHg(object):'
76 76
77 77 # skip passing error to error controller
78 78 environ['pylons.status_code_redirect'] = True
79
79
80 80 #======================================================================
81 81 # EXTRACT REPOSITORY NAME FROM ENV
82 82 #======================================================================
@@ -90,12 +90,13 b' class SimpleHg(object):'
90 90 # GET ACTION PULL or PUSH
91 91 #======================================================================
92 92 action = self.__get_action(environ)
93
93
94 94 #======================================================================
95 95 # CHECK ANONYMOUS PERMISSION
96 96 #======================================================================
97 97 if action in ['pull', 'push']:
98 98 anonymous_user = self.__get_user('default')
99
99 100 username = anonymous_user.username
100 101 anonymous_perm = self.__check_permission(action,
101 102 anonymous_user,
@@ -152,13 +153,13 b' class SimpleHg(object):'
152 153 #======================================================================
153 154 # MERCURIAL REQUEST HANDLING
154 155 #======================================================================
155
156
156 157 repo_path = safe_str(os.path.join(self.basepath, repo_name))
157 158 log.debug('Repository path is %s' % repo_path)
158
159
159 160 baseui = make_ui('db')
160 161 self.__inject_extras(repo_path, baseui, extras)
161
162
162 163
163 164 # quick check if that dir exists...
164 165 if is_valid_repo(repo_name, self.basepath) is False:
@@ -228,7 +229,7 b' class SimpleHg(object):'
228 229 return repo_name
229 230
230 231 def __get_user(self, username):
231 return User.by_username(username)
232 return User.get_by_username(username)
232 233
233 234 def __get_action(self, environ):
234 235 """
@@ -257,7 +258,7 b' class SimpleHg(object):'
257 258 push requests"""
258 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 263 Injects some extra params into baseui instance
263 264
@@ -6,20 +6,7 b' import errno'
6 6 from warnings import warn
7 7 from multiprocessing.util import Finalize
8 8
9 from rhodecode import __platform__, PLATFORM_WIN
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
9 from rhodecode.lib.compat import kill
23 10
24 11 class LockHeld(Exception):
25 12 pass
@@ -29,17 +16,17 b' class DaemonLock(object):'
29 16 """daemon locking
30 17 USAGE:
31 18 try:
32 l = DaemonLock(desc='test lock')
19 l = DaemonLock(file_='/path/tolockfile',desc='test lock')
33 20 main()
34 21 l.release()
35 22 except LockHeld:
36 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 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 30 os.path.dirname(__file__),
44 31 'running.lock')
45 32 self.callbackfn = callbackfn
@@ -6,9 +6,21 b''
6 6 Simple smtp mailer used in RhodeCode
7 7
8 8 :created_on: Sep 13, 2010
9 :copyright: (c) 2011 by marcink.
10 :license: LICENSE_NAME, see LICENSE_FILE for more details.
9 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
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 25 import logging
14 26 import smtplib
@@ -112,7 +112,7 b' def action_logger(user, action, repo, ip'
112 112 if hasattr(user, 'user_id'):
113 113 user_obj = user
114 114 elif isinstance(user, basestring):
115 user_obj = User.by_username(user)
115 user_obj = User.get_by_username(user)
116 116 else:
117 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 361 parent = None
362 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 369 group = sa.query(Group).filter(Group.group_name == group_name).scalar()
365 370
366 371 if group is None:
367 372 group = Group(group_name, parent)
368 373 sa.add(group)
369 374 sa.commit()
370
371 375 parent = group
372
373 376 return group
374 377
375 378
@@ -386,8 +389,13 b' def repo2db_mapper(initial_repo_list, re'
386 389 rm = RepoModel()
387 390 user = sa.query(User).filter(User.admin == True).first()
388 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 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 399 if not rm.get_by_repo_name(name, cache=False):
392 400 log.info('repository %s not found creating default', name)
393 401 added.append(name)
@@ -442,26 +450,6 b' def add_cache(settings):'
442 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 454 # TEST FUNCTIONS AND CREATORS
467 455 #==============================================================================
@@ -483,7 +471,7 b' def create_test_index(repo_location, con'
483 471 os.makedirs(index_location)
484 472
485 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 475 WhooshIndexingDaemon(index_location=index_location,
488 476 repo_location=repo_location)\
489 477 .run(full_index=full_index)
@@ -31,9 +31,10 b' from datetime import date'
31 31
32 32 from sqlalchemy import *
33 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 37 from sqlalchemy.orm.interfaces import MapperExtension
36
37 38 from beaker.cache import cache_region, region_invalidate
38 39
39 40 from vcs import get_backend
@@ -42,13 +43,14 b' from vcs.exceptions import VCSError'
42 43 from vcs.utils.lazy import LazyProperty
43 44
44 45 from rhodecode.lib import str2bool, safe_str, get_changeset_safe, \
45 generate_api_key
46 generate_api_key, safe_unicode
46 47 from rhodecode.lib.exceptions import UsersGroupsAssignedException
47 48 from rhodecode.lib.compat import json
48 49
49 50 from rhodecode.model.meta import Base, Session
50 51 from rhodecode.model.caching_query import FromCache
51 52
53
52 54 log = logging.getLogger(__name__)
53 55
54 56 #==============================================================================
@@ -126,7 +128,8 b' class BaseModel(object):'
126 128
127 129 @classmethod
128 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 134 @classmethod
132 135 def delete(cls, id_):
@@ -140,12 +143,34 b' class RhodeCodeSettings(Base, BaseModel)'
140 143 __table_args__ = (UniqueConstraint('app_settings_name'), {'extend_existing':True})
141 144 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
142 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 148 def __init__(self, k='', v=''):
146 149 self.app_settings_name = k
147 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 174 def __repr__(self):
150 175 return "<%s('%s:%s')>" % (self.__class__.__name__,
151 176 self.app_settings_name, self.app_settings_value)
@@ -176,14 +201,11 b' class RhodeCodeSettings(Base, BaseModel)'
176 201 @classmethod
177 202 def get_ldap_settings(cls, cache=False):
178 203 ret = Session.query(cls)\
179 .filter(cls.app_settings_name.startswith('ldap_'))\
180 .all()
204 .filter(cls.app_settings_name.startswith('ldap_')).all()
181 205 fd = {}
182 206 for row in ret:
183 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 209 return fd
188 210
189 211
@@ -281,17 +303,16 b' class User(Base, BaseModel):'
281 303 return self.__class__.__name__
282 304
283 305 @classmethod
284 def by_username(cls, username, case_insensitive=False):
306 def get_by_username(cls, username, case_insensitive=False):
285 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 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 312 @classmethod
291 313 def get_by_api_key(cls, api_key):
292 314 return Session.query(cls).filter(cls.api_key == api_key).one()
293 315
294
295 316 def update_lastlogin(self):
296 317 """Update user lastlogin"""
297 318
@@ -487,12 +508,16 b' class Repository(Base, BaseModel):'
487 508 self.repo_id, self.repo_name)
488 509
489 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 516 q = Session.query(cls).filter(cls.repo_name == repo_name)
492 517
493 518 q = q.options(joinedload(Repository.fork))\
494 .options(joinedload(Repository.user))\
495 .options(joinedload(Repository.group))\
519 .options(joinedload(Repository.user))\
520 .options(joinedload(Repository.group))\
496 521
497 522 return q.one()
498 523
@@ -507,13 +532,14 b' class Repository(Base, BaseModel):'
507 532
508 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 537 q.options(FromCache("sql_cache_short", "repository_repo_path"))
512 538 return q.one().ui_value
513 539
514 540 @property
515 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 544 @property
519 545 def groups_with_parents(self):
@@ -542,7 +568,8 b' class Repository(Base, BaseModel):'
542 568 Returns base full path for that repository means where it actually
543 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 573 q.options(FromCache("sql_cache_short", "repository_repo_path"))
547 574 return q.one().ui_value
548 575
@@ -552,9 +579,18 b' class Repository(Base, BaseModel):'
552 579 # we need to split the name by / since this is how we store the
553 580 # names in the database, but that eventually needs to be converted
554 581 # into a valid system path
555 p += self.repo_name.split('/')
582 p += self.repo_name.split(Repository.url_sep())
556 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 594 @property
559 595 def _ui(self):
560 596 """
@@ -719,9 +755,26 b' class Group(Base, BaseModel):'
719 755 self.group_name)
720 756
721 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 771 def url_sep(cls):
723 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 778 @property
726 779 def parents(self):
727 780 parents_recursion_limit = 5
@@ -751,9 +804,16 b' class Group(Base, BaseModel):'
751 804 return Session.query(Group).filter(Group.parent_group == self)
752 805
753 806 @property
807 def name(self):
808 return self.group_name.split(Group.url_sep())[-1]
809
810 @property
754 811 def full_path(self):
755 return Group.url_sep().join([g.group_name for g in self.parents] +
756 [self.group_name])
812 return self.group_name
813
814 @property
815 def full_path_splitted(self):
816 return self.group_name.split(Group.url_sep())
757 817
758 818 @property
759 819 def repositories(self):
@@ -772,6 +832,17 b' class Group(Base, BaseModel):'
772 832
773 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 846 class Permission(Base, BaseModel):
776 847 __tablename__ = 'permissions'
777 848 __table_args__ = {'extend_existing':True}
@@ -32,6 +32,7 b' from formencode.validators import Unicod'
32 32 from pylons.i18n.translation import _
33 33 from webhelpers.pylonslib.secure_form import authentication_token
34 34
35 from rhodecode.config.routing import ADMIN_PREFIX
35 36 from rhodecode.lib.utils import repo_name_slug
36 37 from rhodecode.lib.auth import authenticate, get_crypt_password
37 38 from rhodecode.lib.exceptions import LdapImportError
@@ -70,8 +71,7 b' def ValidUsername(edit, old_data):'
70 71 old_un = UserModel().get(old_data.get('user_id')).username
71 72
72 73 if old_un != value or not edit:
73 if UserModel().get_by_username(value, cache=False,
74 case_insensitive=True):
74 if User.get_by_username(value, case_insensitive=True):
75 75 raise formencode.Invalid(_('This username already '
76 76 'exists') , value, state)
77 77
@@ -122,7 +122,7 b' def ValidReposGroup(edit, old_data):'
122 122 def validate_python(self, value, state):
123 123 #TODO WRITE VALIDATIONS
124 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 127 # slugify repo group just in case :)
128 128 slug = repo_name_slug(group_name)
@@ -206,7 +206,7 b' class ValidAuth(formencode.validators.Fa'
206 206 def validate_python(self, value, state):
207 207 password = value['password']
208 208 username = value['username']
209 user = UserModel().get_by_username(username)
209 user = User.get_by_username(username)
210 210
211 211 if authenticate(username, password):
212 212 return value
@@ -241,7 +241,7 b' def ValidRepoName(edit, old_data):'
241 241 repo_name = value.get('repo_name')
242 242
243 243 slug = repo_name_slug(repo_name)
244 if slug in ['_admin', '']:
244 if slug in [ADMIN_PREFIX, '']:
245 245 e_dict = {'repo_name': _('This repository name is disallowed')}
246 246 raise formencode.Invalid('', value, state, error_dict=e_dict)
247 247
@@ -250,7 +250,7 b' def ValidRepoName(edit, old_data):'
250 250 gr = Group.get(value.get('repo_group'))
251 251 group_path = gr.full_path
252 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 254 # database
255 255 repo_name_full = group_path + Group.url_sep() + repo_name
256 256 else:
@@ -259,25 +259,32 b' def ValidRepoName(edit, old_data):'
259 259
260 260
261 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 266 if group_path != '':
265 267 if RepoModel().get_by_repo_name(repo_name_full,):
266 268 e_dict = {'repo_name':_('This repository already '
267 'exists in group "%s"') %
269 'exists in a group "%s"') %
268 270 gr.group_name}
269 271 raise formencode.Invalid('', value, state,
270 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:
273 if RepoModel().get_by_repo_name(repo_name_full):
280 elif RepoModel().get_by_repo_name(repo_name_full):
274 281 e_dict = {'repo_name':_('This repository '
275 282 'already exists')}
276 283 raise formencode.Invalid('', value, state,
277 284 error_dict=e_dict)
285
278 286 return value
279 287
280
281 288 return _ValidRepoName
282 289
283 290 def ValidForkName():
@@ -287,7 +294,7 b' def ValidForkName():'
287 294 repo_name = value.get('fork_name')
288 295
289 296 slug = repo_name_slug(repo_name)
290 if slug in ['_admin', '']:
297 if slug in [ADMIN_PREFIX, '']:
291 298 e_dict = {'repo_name': _('This repository name is disallowed')}
292 299 raise formencode.Invalid('', value, state, error_dict=e_dict)
293 300
@@ -98,11 +98,11 b' class RepoModel(BaseModel):'
98 98 try:
99 99 cur_repo = self.get_by_repo_name(repo_name, cache=False)
100 100
101 #update permissions
101 # update permissions
102 102 for member, perm, member_type in form_data['perms_updates']:
103 103 if member_type == 'user':
104 104 r2p = self.sa.query(RepoToPerm)\
105 .filter(RepoToPerm.user == User.by_username(member))\
105 .filter(RepoToPerm.user == User.get_by_username(member))\
106 106 .filter(RepoToPerm.repository == cur_repo)\
107 107 .one()
108 108
@@ -122,12 +122,12 b' class RepoModel(BaseModel):'
122 122 perm).scalar()
123 123 self.sa.add(g2p)
124 124
125 #set new permissions
125 # set new permissions
126 126 for member, perm, member_type in form_data['perms_new']:
127 127 if member_type == 'user':
128 128 r2p = RepoToPerm()
129 129 r2p.repository = cur_repo
130 r2p.user = User.by_username(member)
130 r2p.user = User.get_by_username(member)
131 131
132 132 r2p.permission = self.sa.query(Permission)\
133 133 .filter(Permission.
@@ -144,26 +144,29 b' class RepoModel(BaseModel):'
144 144 .scalar()
145 145 self.sa.add(g2p)
146 146
147 #update current repo
147 # update current repo
148 148 for k, v in form_data.items():
149 149 if k == 'user':
150 cur_repo.user = User.by_username(v)
150 cur_repo.user = User.get_by_username(v)
151 151 elif k == 'repo_name':
152 cur_repo.repo_name = form_data['repo_name_full']
152 pass
153 153 elif k == 'repo_group':
154 154 cur_repo.group_id = v
155 155
156 156 else:
157 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 162 self.sa.add(cur_repo)
160 163
161 if repo_name != form_data['repo_name_full']:
164 if repo_name != new_name:
162 165 # rename repository
163 self.__rename_repo(old=repo_name,
164 new=form_data['repo_name_full'])
166 self.__rename_repo(old=repo_name, new=new_name)
165 167
166 168 self.sa.commit()
169 return cur_repo
167 170 except:
168 171 log.error(traceback.format_exc())
169 172 self.sa.rollback()
@@ -208,8 +211,7 b' class RepoModel(BaseModel):'
208 211 #create default permission
209 212 repo_to_perm = RepoToPerm()
210 213 default = 'repository.read'
211 for p in UserModel(self.sa).get_by_username('default',
212 cache=False).user_perms:
214 for p in User.get_by_username('default').user_perms:
213 215 if p.permission.permission_name.startswith('repository.'):
214 216 default = p.permission.permission_name
215 217 break
@@ -221,8 +223,7 b' class RepoModel(BaseModel):'
221 223 .one().permission_id
222 224
223 225 repo_to_perm.repository = new_repo
224 repo_to_perm.user_id = UserModel(self.sa)\
225 .get_by_username('default', cache=False).user_id
226 repo_to_perm.user_id = User.get_by_username('default').user_id
226 227
227 228 self.sa.add(repo_to_perm)
228 229
@@ -237,7 +238,7 b' class RepoModel(BaseModel):'
237 238 from rhodecode.model.scm import ScmModel
238 239 ScmModel(self.sa).toggle_following_repo(new_repo.repo_id,
239 240 cur_user.user_id)
240
241 return new_repo
241 242 except:
242 243 log.error(traceback.format_exc())
243 244 self.sa.rollback()
@@ -304,7 +305,7 b' class RepoModel(BaseModel):'
304 305 :param parent_id:
305 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 310 if new_parent_id:
310 311 paths = Group.get(new_parent_id).full_path.split(Group.url_sep())
@@ -315,12 +316,20 b' class RepoModel(BaseModel):'
315 316 repo_path = os.path.join(*map(lambda x:safe_str(x),
316 317 [self.repos_path, new_parent_path, repo_name]))
317 318
318 if is_valid_repo(repo_path, self.repos_path) is False:
319 log.info('creating repo %s in %s @ %s', repo_name, repo_path,
320 clone_uri)
321 backend = get_backend(alias)
319
320 # check if this path is not a repository
321 if is_valid_repo(repo_path, self.repos_path):
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 335 def __rename_repo(self, old, new):
@@ -50,7 +50,7 b' class ReposGroupModel(BaseModel):'
50 50 q = RhodeCodeUi.get_by_key('/').one()
51 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 55 makes repositories group on filesystem
56 56
@@ -58,44 +58,30 b' class ReposGroupModel(BaseModel):'
58 58 :param parent_id:
59 59 """
60 60
61 if parent_id:
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)
61 create_path = os.path.join(self.repos_path, group_name)
68 62 log.debug('creating new group in %s', create_path)
69 63
70 64 if os.path.isdir(create_path):
71 65 raise Exception('That directory already exists !')
72 66
73
74 67 os.makedirs(create_path)
75 68
76
77 def __rename_group(self, old, old_parent_id, new, new_parent_id):
69 def __rename_group(self, old, new):
78 70 """
79 71 Renames a group on filesystem
80 72
81 73 :param group_name:
82 74 """
75
76 if old == new:
77 log.debug('skipping group rename')
78 return
79
83 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:
92 paths = Group.get(old_parent_id).full_path.split(Group.url_sep())
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)
83 old_path = os.path.join(self.repos_path, old)
84 new_path = os.path.join(self.repos_path, new)
99 85
100 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 100 paths = os.sep.join(paths)
115 101
116 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 107 def create(self, form_data):
120 108 try:
121 109 new_repos_group = Group()
122 new_repos_group.group_name = form_data['group_name']
123 new_repos_group.group_description = \
124 form_data['group_description']
125 new_repos_group.group_parent_id = form_data['group_parent_id']
110 new_repos_group.group_description = form_data['group_description']
111 new_repos_group.parent_group = Group.get(form_data['group_parent_id'])
112 new_repos_group.group_name = new_repos_group.get_new_name(form_data['group_name'])
126 113
127 114 self.sa.add(new_repos_group)
128 115
129 self.__create_group(form_data['group_name'],
130 form_data['group_parent_id'])
116 self.__create_group(new_repos_group.group_name)
131 117
132 118 self.sa.commit()
119 return new_repos_group
133 120 except:
134 121 log.error(traceback.format_exc())
135 122 self.sa.rollback()
@@ -139,23 +126,27 b' class ReposGroupModel(BaseModel):'
139 126
140 127 try:
141 128 repos_group = Group.get(repos_group_id)
142 old_name = repos_group.group_name
143 old_parent_id = repos_group.group_parent_id
129 old_path = repos_group.full_path
144 130
145 repos_group.group_name = form_data['group_name']
146 repos_group.group_description = \
147 form_data['group_description']
148 repos_group.group_parent_id = form_data['group_parent_id']
131 #change properties
132 repos_group.group_description = form_data['group_description']
133 repos_group.parent_group = Group.get(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 138 self.sa.add(repos_group)
151 139
152 if old_name != form_data['group_name'] or (old_parent_id !=
153 form_data['group_parent_id']):
154 self.__rename_group(old=old_name, old_parent_id=old_parent_id,
155 new=form_data['group_name'],
156 new_parent_id=form_data['group_parent_id'])
140 self.__rename_group(old_path, new_path)
141
142 # we need to get all repositories from this new group and
143 # rename them accordingly to new group path
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 148 self.sa.commit()
149 return repos_group
159 150 except:
160 151 log.error(traceback.format_exc())
161 152 self.sa.rollback()
@@ -41,9 +41,8 b' from rhodecode.lib.auth import HasRepoPe'
41 41 from rhodecode.lib.utils import get_repos as get_filesystem_repos, make_ui, \
42 42 action_logger, EmptyChangeset
43 43 from rhodecode.model import BaseModel
44 from rhodecode.model.user import UserModel
45 44 from rhodecode.model.db import Repository, RhodeCodeUi, CacheInvalidation, \
46 UserFollowing, UserLog
45 UserFollowing, UserLog, User
47 46
48 47 log = logging.getLogger(__name__)
49 48
@@ -148,10 +147,15 b' class ScmModel(BaseModel):'
148 147 repos_list = {}
149 148
150 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 155 try:
152 156 if name in repos_list:
153 157 raise RepositoryError('Duplicate repository name %s '
154 'found in %s' % (name, path))
158 'found in %s' % (name, path))
155 159 else:
156 160
157 161 klass = get_backend(path[0])
@@ -283,7 +287,7 b' class ScmModel(BaseModel):'
283 287 return f is not None
284 288
285 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 292 f = self.sa.query(UserFollowing)\
289 293 .filter(UserFollowing.follows_user == u)\
@@ -293,24 +297,24 b' class ScmModel(BaseModel):'
293 297
294 298 def get_followers(self, repo_id):
295 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 302 return self.sa.query(UserFollowing)\
299 303 .filter(UserFollowing.follows_repo_id == repo_id).count()
300 304
301 305 def get_forks(self, repo_id):
302 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 309 return self.sa.query(Repository)\
306 310 .filter(Repository.fork_id == repo_id).count()
307 311
308 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 314 clone_uri = dbrepo.clone_uri
311 315 if not clone_uri:
312 316 raise Exception("This repository doesn't have a clone uri")
313
317
314 318 repo = dbrepo.scm_instance
315 319 try:
316 320 extras = {'ip': '',
@@ -363,12 +367,12 b' class ScmModel(BaseModel):'
363 367 from vcs.backends.git import GitInMemoryChangeset as IMC
364 368 # decoding here will force that we have proper encoded values
365 369 # in any other case this will throw exceptions and deny commit
366
367 if isinstance(content,(basestring,)):
370
371 if isinstance(content, (basestring,)):
368 372 content = safe_str(content)
369 elif isinstance(content,file):
373 elif isinstance(content, file):
370 374 content = content.read()
371
375
372 376 message = safe_str(message)
373 377 path = safe_str(f_path)
374 378 author = safe_str(author)
@@ -246,12 +246,13 b' color:#FFF;'
246 246 }
247 247
248 248 #header #header-inner {
249 height:40px;
249 min-height:40px;
250 250 clear:both;
251 251 position:relative;
252 252 background:#003367 url("../images/header_inner.png") repeat-x;
253 253 margin:0;
254 254 padding:0;
255 display:block;
255 256 box-shadow: 0 2px 2px rgba(0, 0, 0, 0.6);
256 257 -webkit-border-radius: 4px 4px 4px 4px;
257 258 -khtml-border-radius: 4px 4px 4px 4px;
@@ -272,7 +273,10 b' padding:0;'
272 273 #header #header-inner #home a:hover {
273 274 background-position:0 -40px;
274 275 }
275
276 #header #header-inner #logo {
277 float: left;
278 position: absolute;
279 }
276 280 #header #header-inner #logo h1 {
277 281 color:#FFF;
278 282 font-size:18px;
@@ -348,7 +352,7 b' top:0;'
348 352 left:0;
349 353 border-left:none;
350 354 border-right:1px solid #2e5c89;
351 padding:8px 8px 4px;
355 padding:8px 6px 4px;
352 356 }
353 357
354 358 #header #header-inner #quick li span.icon_short {
@@ -356,7 +360,10 b' top:0;'
356 360 left:0;
357 361 border-left:none;
358 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 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 582 .quick_repo_menu{
568 583 background: #FFF url("../images/vertical-indicator.png") 8px 50% no-repeat !important;
569 584 cursor: pointer;
@@ -1895,19 +1910,6 b' div.browserblock .add_node{'
1895 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 1913 div.browserblock .search_activate a:hover,div.browserblock .add_node a:hover{
1912 1914 text-decoration: none !important;
1913 1915 }
@@ -2371,8 +2373,10 b' border-left:1px solid #316293;'
2371 2373 border:1px solid #316293;
2372 2374 }
2373 2375
2374
2375 input.ui-button-small {
2376 .ui-button-small a:hover {
2377
2378 }
2379 input.ui-button-small,.ui-button-small {
2376 2380 background:#e5e3e3 url("../images/button.png") repeat-x !important;
2377 2381 border-top:1px solid #DDD !important;
2378 2382 border-left:1px solid #c6c6c6 !important;
@@ -2387,17 +2391,19 b' margin:0 !important;'
2387 2391 border-radius: 4px 4px 4px 4px !important;
2388 2392 box-shadow: 0 1px 0 #ececec !important;
2389 2393 cursor: pointer !important;
2390 }
2391
2392 input.ui-button-small:hover {
2394 padding:0px 2px 1px 2px;
2395 }
2396
2397 input.ui-button-small:hover,.ui-button-small:hover {
2393 2398 background:#b4b4b4 url("../images/button_selected.png") repeat-x !important;
2394 2399 border-top:1px solid #ccc !important;
2395 2400 border-left:1px solid #bebebe !important;
2396 2401 border-right:1px solid #b1b1b1 !important;
2397 border-bottom:1px solid #afafaf !important;
2398 }
2399
2400 input.ui-button-small-blue {
2402 border-bottom:1px solid #afafaf !important;
2403 text-decoration: none;
2404 }
2405
2406 input.ui-button-small-blue,.ui-button-small-blue {
2401 2407 background:#4e85bb url("../images/button_highlight.png") repeat-x;
2402 2408 border-top:1px solid #5c91a4;
2403 2409 border-left:1px solid #2a6f89;
@@ -2410,6 +2416,7 b' color:#fff;'
2410 2416 border-radius: 4px 4px 4px 4px;
2411 2417 box-shadow: 0 1px 0 #ececec;
2412 2418 cursor: pointer;
2419 padding:0px 2px 1px 2px;
2413 2420 }
2414 2421
2415 2422 input.ui-button-small-blue:hover {
@@ -5,12 +5,14 b''
5 5 </%def>
6 6
7 7 <%def name="breadcrumbs()">
8 <span class="groups_breadcrumbs">
8 9 ${_('Groups')}
9 10 %if c.group.parent_group:
10 &raquo; ${h.link_to(c.group.parent_group.group_name,
11 h.url('repos_group',id=c.group.parent_group.group_id))}
11 &raquo; ${h.link_to(c.group.parent_group.name,
12 h.url('repos_group_home',group_name=c.group.parent_group.group_name))}
12 13 %endif
13 &raquo; "${c.group.group_name}" ${_('with')}
14 &raquo; "${c.group.name}" ${_('with')}
15 </span>
14 16 </%def>
15 17
16 18 <%def name="page_nav()">
@@ -2,14 +2,14 b''
2 2 <%inherit file="/base/base.html"/>
3 3
4 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 6 </%def>
7 7 <%def name="breadcrumbs_links()">
8 8 ${h.link_to(_('Admin'),h.url('admin_home'))}
9 9 &raquo;
10 10 ${h.link_to(_('Repos groups'),h.url('repos_groups'))}
11 11 &raquo;
12 ${_('edit repos group')} "${c.repos_group.group_name}"
12 ${_('edit repos group')} "${c.repos_group.name}"
13 13 </%def>
14 14
15 15 <%def name="page_nav()">
@@ -44,14 +44,14 b''
44 44 <td>
45 45 <div style="white-space: nowrap">
46 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 48 </div>
49 49 </td>
50 50 <td>${gr.group_description}</td>
51 51 <td><b>${gr.repositories.count()}</b></td>
52 52 <td>
53 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 55 ${h.end_form()}
56 56 </td>
57 57 </tr>
@@ -11,9 +11,9 b''
11 11 ${h.form(h.url.current())}
12 12 <div class="info_box">
13 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 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 17 ## ${h.submit('view',_('view'),class_="ui-button-small")}
18 18 </div>
19 19 ${h.end_form()}
@@ -24,11 +24,11 b''
24 24 </div>
25 25 <div class="browser-search">
26 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 28 </div>
29 29 % if h.HasRepoPermissionAny('repository.write','repository.admin')(c.repo_name):
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>
30 <div id="add_node_id" class="add_node">
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 32 </div>
33 33 % endif
34 34 <div>
@@ -35,7 +35,7 b''
35 35 <td>
36 36 <div style="white-space: nowrap">
37 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 39 </div>
40 40 </td>
41 41 <td>${gr.group_description}</td>
@@ -45,17 +45,17 b''
45 45
46 46 ##REPO TYPE
47 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 49 %endif
50 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 52 %endif
53 53
54 54 ##PUBLIC/PRIVATE
55 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 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 59 %endif
60 60
61 61 ##REPO NAME
@@ -67,7 +67,7 b''
67 67 <a href="${h.url('summary_home',repo_name=c.dbrepo.fork.repo_name)}">
68 68 <img class="icon" alt="${_('public')}"
69 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 71 ${_('Fork of')} ${c.dbrepo.fork.repo_name}
72 72 </a>
73 73 </div>
@@ -78,7 +78,7 b''
78 78 <a href="${h.url(str(h.hide_credentials(c.dbrepo.clone_uri)))}">
79 79 <img class="icon" alt="${_('remote clone')}"
80 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 82 ${_('Clone from')} ${h.hide_credentials(c.dbrepo.clone_uri)}
83 83 </a>
84 84 </div>
@@ -151,7 +151,7 b''
151 151 %elif c.enable_downloads is False:
152 152 ${_('Downloads are disabled for this repository')}
153 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 155 %endif
156 156 %else:
157 157 ${h.select('download_options',c.rhodecode_repo.get_changeset().raw_id,c.download_options)}
@@ -317,7 +317,7 b''
317 317 %if c.no_data:
318 318 ${c.no_data_msg}
319 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 321 %endif
322 322
323 323 %else:
@@ -13,7 +13,7 b' class TestChangelogController(TestContro'
13 13 """name="5e204e7583b9" type="checkbox" value="1" />"""
14 14 in response.body)
15 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 17 self.assertTrue("""Small update at simplevcs app""" in response.body)
18 18
19 19
@@ -43,7 +43,7 b' class TestChangelogController(TestContro'
43 43 """name="46ad32a4f974" type="checkbox" value="1" />"""
44 44 in response.body)
45 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 48 self.assertTrue("""<span id="46ad32a4f974e45472a898c6b0acb600320"""
49 49 """579b1" class="changed_total tooltip" """
@@ -145,8 +145,8 b' class TestLoginController(TestController'
145 145 'lastname':'test'})
146 146
147 147 self.assertEqual(response.status , '200 OK')
148 assert 'An email address must contain a single @' in response.body
149 assert 'This username already exists' in response.body
148 self.assertTrue('An email address must contain a single @' 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 160 'lastname':'test'})
161 161
162 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 166 def test_register_password_mismatch(self):
@@ -246,7 +246,7 b' class TestLoginController(TestController'
246 246
247 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 251 response = self.app.get(url(controller='login',
252 252 action='password_reset_confirmation',
@@ -41,7 +41,7 b' class TestSummaryController(TestControll'
41 41
42 42
43 43 def _enable_stats(self):
44 r = Repository.by_repo_name(HG_REPO)
44 r = Repository.get_by_repo_name(HG_REPO)
45 45 r.enable_statistics = True
46 46 self.sa.add(r)
47 47 self.sa.commit()
@@ -6,9 +6,21 b''
6 6 Test suite for making push/pull operations
7 7
8 8 :created_on: Dec 30, 2010
9 :copyright: (c) 2010 by marcink.
10 :license: LICENSE_NAME, see LICENSE_FILE for more details.
9 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
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 25 import os
14 26 import time
@@ -29,7 +41,7 b' from sqlalchemy import engine_from_confi'
29 41 from rhodecode.lib.utils import add_cache
30 42 from rhodecode.model import init_model
31 43 from rhodecode.model import meta
32 from rhodecode.model.db import User, Repository
44 from rhodecode.model.db import User, Repository, UserLog
33 45 from rhodecode.lib.auth import get_crypt_password
34 46
35 47 from rhodecode.tests import TESTS_TMP_PATH, NEW_HG_REPO, HG_REPO
@@ -44,8 +56,8 b' add_cache(conf)'
44 56 USER = 'test_admin'
45 57 PASS = 'test12'
46 58 HOST = '127.0.0.1:5000'
47 DEBUG = bool(int(sys.argv[1]))
48 print 'DEBUG:',DEBUG
59 DEBUG = True if sys.argv[1:] else False
60 print 'DEBUG:', DEBUG
49 61 log = logging.getLogger(__name__)
50 62
51 63
@@ -70,15 +82,17 b' class Command(object):'
70 82
71 83
72 84 def test_wrapp(func):
73
74 def __wrapp(*args,**kwargs):
75 print '###%s###' %func.__name__
85
86 def __wrapp(*args, **kwargs):
87 print '>>>%s' % func.__name__
76 88 try:
77 res = func(*args,**kwargs)
78 except:
79 print '--%s failed--' % func.__name__
80 return
81 print 'ok'
89 res = func(*args, **kwargs)
90 except Exception, e:
91 print ('###############\n-'
92 '--%s failed %s--\n'
93 '###############\n' % (func.__name__, e))
94 sys.exit()
95 print '++OK++'
82 96 return res
83 97 return __wrapp
84 98
@@ -90,20 +104,20 b' def get_session():'
90 104
91 105
92 106 def create_test_user(force=True):
93 print 'creating test user'
107 print '\tcreating test user'
94 108 sa = get_session()
95 109
96 110 user = sa.query(User).filter(User.username == USER).scalar()
97 111
98 112 if force and user is not None:
99 print 'removing current user'
113 print '\tremoving current user'
100 114 for repo in sa.query(Repository).filter(Repository.user == user).all():
101 115 sa.delete(repo)
102 116 sa.delete(user)
103 117 sa.commit()
104 118
105 119 if user is None or force:
106 print 'creating new one'
120 print '\tcreating new one'
107 121 new_usr = User()
108 122 new_usr.username = USER
109 123 new_usr.password = get_crypt_password(PASS)
@@ -115,7 +129,7 b' def create_test_user(force=True):'
115 129 sa.add(new_usr)
116 130 sa.commit()
117 131
118 print 'done'
132 print '\tdone'
119 133
120 134
121 135 def create_test_repo(force=True):
@@ -130,7 +144,7 b' def create_test_repo(force=True):'
130 144 repo = sa.query(Repository).filter(Repository.repo_name == HG_REPO).scalar()
131 145
132 146 if repo is None:
133 print 'repo not found creating'
147 print '\trepo not found creating'
134 148
135 149 form_data = {'repo_name':HG_REPO,
136 150 'repo_type':'hg',
@@ -144,12 +158,13 b' def create_test_repo(force=True):'
144 158 def set_anonymous_access(enable=True):
145 159 sa = get_session()
146 160 user = sa.query(User).filter(User.username == 'default').one()
161 sa.expire(user)
147 162 user.active = enable
148 163 sa.add(user)
149 164 sa.commit()
150 165 sa.remove()
151
152 print 'anonymous access is now:',enable
166 import time;time.sleep(3)
167 print '\tanonymous access is now:', enable
153 168
154 169
155 170 def get_anonymous_access():
@@ -173,13 +188,13 b' def test_clone_with_credentials(no_error'
173 188 except OSError:
174 189 raise
175 190
176 print 'checking if anonymous access is enabled'
191 print '\tchecking if anonymous access is enabled'
177 192 anonymous_access = get_anonymous_access()
178 193 if anonymous_access:
179 print 'enabled, disabling it '
194 print '\tenabled, disabling it '
180 195 set_anonymous_access(enable=False)
181 196 time.sleep(1)
182
197
183 198 clone_url = 'http://%(user)s:%(pass)s@%(host)s/%(cloned_repo)s %(dest)s' % \
184 199 {'user':USER,
185 200 'pass':PASS,
@@ -206,13 +221,13 b' def test_clone_anonymous():'
206 221 raise
207 222
208 223
209 print 'checking if anonymous access is enabled'
224 print '\tchecking if anonymous access is enabled'
210 225 anonymous_access = get_anonymous_access()
211 226 if not anonymous_access:
212 print 'not enabled, enabling it '
227 print '\tnot enabled, enabling it '
213 228 set_anonymous_access(enable=True)
214 229 time.sleep(1)
215
230
216 231 clone_url = 'http://%(host)s/%(cloned_repo)s %(dest)s' % \
217 232 {'user':USER,
218 233 'pass':PASS,
@@ -227,7 +242,7 b' def test_clone_anonymous():'
227 242
228 243 #disable if it was enabled
229 244 if not anonymous_access:
230 print 'disabling anonymous access'
245 print '\tdisabling anonymous access'
231 246 set_anonymous_access(enable=False)
232 247
233 248 @test_wrapp
@@ -241,12 +256,12 b' def test_clone_wrong_credentials():'
241 256 except OSError:
242 257 raise
243 258
244 print 'checking if anonymous access is enabled'
259 print '\tchecking if anonymous access is enabled'
245 260 anonymous_access = get_anonymous_access()
246 261 if anonymous_access:
247 print 'enabled, disabling it '
262 print '\tenabled, disabling it '
248 263 set_anonymous_access(enable=False)
249
264
250 265 clone_url = 'http://%(user)s:%(pass)s@%(host)s/%(cloned_repo)s %(dest)s' % \
251 266 {'user':USER + 'error',
252 267 'pass':PASS,
@@ -257,7 +272,7 b' def test_clone_wrong_credentials():'
257 272 stdout, stderr = Command(cwd).execute('hg clone', clone_url)
258 273
259 274 if not """abort: authorization failed""" in stderr:
260 raise Exception('Failure')
275 raise Exception('Failure')
261 276
262 277 @test_wrapp
263 278 def test_pull():
@@ -335,7 +350,7 b' def test_push_wrong_path():'
335 350 try:
336 351 shutil.rmtree(path, ignore_errors=True)
337 352 os.makedirs(path)
338 print 'made dirs %s' % jn(path)
353 print '\tmade dirs %s' % jn(path)
339 354 except OSError:
340 355 raise
341 356
@@ -361,19 +376,37 b' def test_push_wrong_path():'
361 376 if not """abort: HTTP Error 403: Forbidden""" in stderr:
362 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 393 if __name__ == '__main__':
366 394 create_test_user(force=False)
367 395 create_test_repo()
368
396
397 initial_logs = get_logs()
398
369 399 # test_push_modify_file()
370 400 test_clone_with_credentials()
371 401 test_clone_wrong_credentials()
372 402
373 403
374 404 test_push_new_file(commits=2, with_clone=True)
375 #
405
406 test_clone_anonymous()
376 407 test_push_wrong_path()
377
378 test_clone_anonymous()
379 test_push_wrong_credentials() No newline at end of file
408
409
410 test_push_wrong_credentials()
411
412 test_logs(initial_logs)
@@ -7,9 +7,21 b''
7 7 Package for testing various lib/helper functions in rhodecode
8 8
9 9 :created_on: Jun 9, 2011
10 :copyright: (c) 2011 by marcink.
11 :license: LICENSE_NAME, see LICENSE_FILE for more details.
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
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 2 import unittest
2 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 12 requirements = [
13 13 "Pylons==1.0.0",
14 "Beaker==1.5.4",
14 15 "WebHelpers>=1.2",
15 16 "formencode==1.2.4",
16 "SQLAlchemy>=0.7.2,<0.8",
17 "Mako>=0.4.2",
17 "SQLAlchemy==0.7.3",
18 "Mako==0.5.0",
18 19 "pygments>=1.4",
19 20 "mercurial>=1.9.3,<2.0",
20 21 "whoosh<1.8",
@@ -22,7 +23,7 b' requirements = ['
22 23 "babel",
23 24 "python-dateutil>=1.5.0,<2.0.0",
24 25 "dulwich>=0.8.0",
25 "vcs>=0.2.1",
26 "vcs==0.2.2",
26 27 "webob==1.0.8"
27 28 ]
28 29
@@ -33,7 +34,7 b" classifiers = ['Development Status :: 5 "
33 34 'Environment :: Web Environment',
34 35 'Framework :: Pylons',
35 36 'Intended Audience :: Developers',
36 'License :: OSI Approved :: BSD License',
37 'License :: OSI Approved :: GNU General Public License (GPL)',
37 38 'Operating System :: OS Independent',
38 39 'Programming Language :: Python',
39 40 'Programming Language :: Python :: 2.5',
@@ -23,6 +23,7 b' pdebug = false'
23 23 #smtp_password =
24 24 #smtp_port =
25 25 #smtp_use_tls = false
26 #smtp_use_ssl = true
26 27
27 28 [server:main]
28 29 ##nr of threads to spawn
@@ -49,6 +50,7 b' app_instance_uuid = develop-test'
49 50 cut_off_limit = 256000
50 51 force_https = false
51 52 commit_parse_limit = 25
53 use_gravatar = true
52 54
53 55 ####################################
54 56 ### CELERY CONFIG ####
@@ -93,7 +95,6 b' beaker.cache.short_term.expire=60'
93 95 beaker.cache.long_term.type=memory
94 96 beaker.cache.long_term.expire=36000
95 97
96
97 98 beaker.cache.sql_cache_short.type=memory
98 99 beaker.cache.sql_cache_short.expire=10
99 100
@@ -150,13 +151,13 b' sqlalchemy.convert_unicode = true'
150 151 ### LOGGING CONFIGURATION ####
151 152 ################################
152 153 [loggers]
153 keys = root, routes, rhodecode, sqlalchemy,beaker,templates
154 keys = root, routes, rhodecode, sqlalchemy, beaker, templates
154 155
155 156 [handlers]
156 157 keys = console
157 158
158 159 [formatters]
159 keys = generic,color_formatter
160 keys = generic, color_formatter
160 161
161 162 #############
162 163 ## LOGGERS ##
@@ -167,9 +168,10 b' handlers = console'
167 168
168 169 [logger_routes]
169 170 level = ERROR
170 handlers = console
171 handlers =
171 172 qualname = routes.middleware
172 173 # "level = DEBUG" logs the route matched and routing variables.
174 propagate = 1
173 175
174 176 [logger_beaker]
175 177 level = DEBUG
@@ -185,9 +187,9 b' propagate = 1'
185 187
186 188 [logger_rhodecode]
187 189 level = ERROR
188 handlers = console
190 handlers =
189 191 qualname = rhodecode
190 propagate = 0
192 propagate = 1
191 193
192 194 [logger_sqlalchemy]
193 195 level = ERROR
@@ -203,7 +205,7 b' propagate = 0'
203 205 class = StreamHandler
204 206 args = (sys.stderr,)
205 207 level = NOTSET
206 formatter = color_formatter
208 formatter = generic
207 209
208 210 ################
209 211 ## FORMATTERS ##
General Comments 0
You need to be logged in to leave comments. Login now