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, |
|
|
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 |
|
|
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 |
|
|
|
340 |
|
|
|
341 |
|
|
|
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('»'.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('»')[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 |
|
|
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 = |
|
|
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, HasPermissionA |
|
|
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('»'.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('»')[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 |
|
|
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('»'.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('»')[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 |
|
|
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: |
|
|
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 = |
|
|
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 = |
|
|
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 = |
|
|
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: |
|
|
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: |
|
|
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. |
|
|
602 |
|
|
|
601 | return link_to(group.name, url('repos_group_home', | |
|
602 | group_name=group.group_name)) | |
|
603 | 603 | return literal(' » '.join(map(make_link, groups)) + \ |
|
604 | 604 | " » " + 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: ( |
|
|
10 |
:license: |
|
|
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)). |
|
|
308 | return Session.query(cls).filter(cls.username.ilike(username)).scalar() | |
|
287 | 309 | else: |
|
288 |
return Session.query(cls).filter(cls.username == username). |
|
|
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 = ' » ' | |
|
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 User |
|
|
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 - |
|
|
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 = User |
|
|
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 [ |
|
|
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 actual |
|
|
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 |
|
|
|
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 [ |
|
|
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 != |
|
|
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 User |
|
|
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 = User |
|
|
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 |
|
|
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. |
|
|
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_n |
|
|
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( |
|
|
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_ |
|
|
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_ |
|
|
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 = User |
|
|
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 |
|
|
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: |
|
|
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 |
» ${h.link_to(c.group.parent_group. |
|
|
11 |
h.url('repos_group |
|
|
11 | » ${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 |
» "${c.group. |
|
|
14 | » "${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. |
|
|
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 | » |
|
10 | 10 | ${h.link_to(_('Repos groups'),h.url('repos_groups'))} |
|
11 | 11 | » |
|
12 |
${_('edit repos group')} "${c.repos_group. |
|
|
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(' » '.join([g. |
|
|
47 | ${h.link_to(h.literal(' » '.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. |
|
|
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=" |
|
|
14 | <a class="ui-button-small" href="${c.url_prev}" title="${_('previous revision')}">«</a> | |
|
15 | 15 | ${h.text('at_rev',value=c.changeset.revision,size=5)} |
|
16 |
<a class=" |
|
|
16 | <a class="ui-button-small" href="${c.url_next}" title="${_('next revision')}">»</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 |
|
|
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. |
|
|
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( |
|
|
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( |
|
|
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( |
|
|
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( |
|
|
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( |
|
|
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( |
|
|
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 |
|
|
|
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 |
|
|
|
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 |
"""0 |
|
|
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 |
""" 0 |
|
|
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 |
|
|
149 |
assert |
|
|
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 |
|
|
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: |
|
|
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 ' |
|
|
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: ( |
|
|
11 |
:license: |
|
|
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 |
|
|
17 |
"Mako |
|
|
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 |
|
|
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 :: |
|
|
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 = |
|
|
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 = |
|
|
190 | handlers = | |
|
189 | 191 | qualname = rhodecode |
|
190 |
propagate = |
|
|
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 = |
|
|
208 | formatter = generic | |
|
207 | 209 | |
|
208 | 210 | ################ |
|
209 | 211 | ## FORMATTERS ## |
General Comments 0
You need to be logged in to leave comments.
Login now