##// END OF EJS Templates
changes for release 1.1.5
marcink -
r1136:93b980eb default
parent child Browse files
Show More
@@ -8,19 +8,19 b' browser/management tool with a built in '
8 8 It works on http/https and has a built in permission/authentication system with
9 9 the ability to authenticate via LDAP.
10 10
11 RhodeCode is similar in some respects to github or bitbucket,
12 however RhodeCode can be run as standalone hosted application on your own server. It is open source
13 and donation ware and focuses more on providing a customized, self administered
14 interface for Mercurial(and soon GIT) repositories. RhodeCode is powered by a vcs_
15 library that Lukasz Balcerzak and I created to handle multiple different version
16 control systems.
11 RhodeCode is similar in some respects to github or bitbucket_,
12 however RhodeCode can be run as standalone hosted application on your own server.
13 It is open source and donation ware and focuses more on providing a customized,
14 self administered interface for Mercurial(and soon GIT) repositories.
15 RhodeCode is powered by a vcs_ library that Lukasz Balcerzak and I created to
16 handle multiple different version control systems.
17 17
18 18 RhodeCode uses `Semantic Versioning <http://semver.org/>`_
19 19
20 20 RhodeCode demo
21 21 --------------
22 22
23 http://hg.python-works.com
23 http://demo.rhodecode.org
24 24
25 25 The default access is anonymous but you can login to an administrative account
26 26 using the following credentials:
@@ -31,8 +31,8 b' using the following credentials:'
31 31 Source code
32 32 -----------
33 33
34 The latest source for RhodeCode can be obtained from my own RhodeCode instance
35 https://rhodecode.org
34 The latest source for RhodeCode can be obtained from official RhodeCode instance
35 https://hg.rhodecode.org
36 36
37 37 Rarely updated source code and issue tracker is available at bitbcuket
38 38 http://bitbucket.org/marcinkuzminski/rhodecode
@@ -123,6 +123,7 b' have sphinx installed you can install it'
123 123 .. _python: http://www.python.org/
124 124 .. _django: http://www.djangoproject.com/
125 125 .. _mercurial: http://mercurial.selenic.com/
126 .. _bitbucket: http://bitbucket.org/
126 127 .. _subversion: http://subversion.tigris.org/
127 128 .. _git: http://git-scm.com/
128 129 .. _celery: http://celeryproject.org/
@@ -3,6 +3,28 b''
3 3 Changelog
4 4 =========
5 5
6
7 1.1.5 (**2011-03-1X**)
8 ======================
9
10 news
11 ----
12
13 - basic windows support, by exchanging pybcrypt into sha256 for windows only
14 highly inspired by idea of mantis406
15
16 fixes
17 -----
18
19 - fixed sorting by author in main page
20 - fixed crashes with diffs on binary files
21 - fixed #131 problem with boolean values for LDAP
22 - fixed #122 mysql problems thanks to striker69
23 - fixed problem with errors on calling raw/raw_files/annotate functions
24 with unknown revisions
25 - fixed returned rawfiles attachment names with international character
26 - cleaned out docs, big thanks to Jason Harris
27
6 28 1.1.4 (**2011-02-19**)
7 29 ======================
8 30
@@ -7,13 +7,19 b' If you would like to contribute to Rhode'
7 7 greatly appreciated!
8 8
9 9 Could I request that you make your source contributions by first forking the
10 RhodeCode repository on bitbucket
10 RhodeCode repository on bitbucket_
11 11 https://bitbucket.org/marcinkuzminski/rhodecode and then make your changes to
12 your forked repository. Finally, when you are finished making a change, please
13 send me a pull request.
12 your forked repository. Please post all fixes into **BETA** branch since your
13 fix might be already fixed there and i try to merge all fixes from beta into
14 stable, and not the other way. Finally, when you are finished making a change,
15 please send me a pull request.
14 16
15 17 To run RhodeCode in a development version you always need to install the tip
16 18 version of RhodeCode and the VCS library.
17 19
18 20 | Thank you for any contributions!
19 | Marcin No newline at end of file
21 | Marcin
22
23
24
25 .. _bitbucket: http://bitbucket.org/
@@ -48,6 +48,7 b' Other topics'
48 48 .. _python: http://www.python.org/
49 49 .. _django: http://www.djangoproject.com/
50 50 .. _mercurial: http://mercurial.selenic.com/
51 .. _bitbucket: http://bitbucket.org/
51 52 .. _subversion: http://subversion.tigris.org/
52 53 .. _git: http://git-scm.com/
53 54 .. _celery: http://celeryproject.org/
@@ -9,12 +9,13 b' together with celery you have to install'
9 9 recommended one is rabbitmq_ to make the async tasks work.
10 10
11 11 Of course RhodeCode works in sync mode also and then you do not have to install
12 any third party applications. However, using Celery_ will give you a large speed improvement when using
13 many big repositories. If you plan to use RhodeCode for say 7 to 10 small repositories, RhodeCode
14 will perform perfectly well without celery running.
12 any third party applications. However, using Celery_ will give you a large
13 speed improvement when using many big repositories. If you plan to use
14 RhodeCode for say 7 to 10 small repositories, RhodeCode will perform perfectly
15 well without celery running.
15 16
16 If you make the decision to run RhodeCode with celery make sure you run celeryd using paster
17 and message broker together with the application.
17 If you make the decision to run RhodeCode with celery make sure you run
18 celeryd using paster and message broker together with the application.
18 19
19 20 Installing RhodeCode from Cheese Shop
20 21 -------------------------------------
@@ -348,19 +348,19 b' Troubleshooting'
348 348 double check the root path for your http setup. It should point to
349 349 for example:
350 350 /home/my-virtual-python/lib/python2.6/site-packages/rhodecode/public
351
352 |
351
352 |
353 353
354 354 :Q: **Can't install celery/rabbitmq**
355 355 :A: Don't worry RhodeCode works without them too. No extra setup is required.
356 356
357 357 |
358
358
359 359 :Q: **Long lasting push timeouts?**
360 360 :A: Make sure you set a longer timeouts in your proxy/fcgi settings, timeouts
361 361 are caused by https server and not RhodeCode.
362
363 |
362
363 |
364 364
365 365 :Q: **Large pushes timeouts?**
366 366 :A: Make sure you set a proper max_body_size for the http server.
@@ -7,7 +7,8 b' Upgrading from Cheese Shop'
7 7 --------------------------
8 8
9 9 .. note::
10 Firstly, it is recommended that you **always** perform a database backup before doing an upgrade.
10 Firstly, it is recommended that you **always** perform a database backup
11 before doing an upgrade.
11 12
12 13 The easiest way to upgrade ``rhodecode`` is to run::
13 14
@@ -24,15 +25,16 b' Then make sure you run the following com'
24 25
25 26 This will display any changes made by the new version of RhodeCode to your
26 27 current configuration. It will try to perform an automerge. It's always better
27 to make a backup of your configuration file before hand and recheck the content after the automerge.
28 to make a backup of your configuration file before hand and recheck the
29 content after the automerge.
28 30
29 31 .. note::
30 32 The next steps only apply to upgrading from non bugfix releases eg. from
31 33 any minor or major releases. Bugfix releases (eg. 1.1.2->1.1.3) will
32 34 not have any database schema changes or whoosh library updates.
33 35
34 It is also recommended that you rebuild the whoosh index after upgrading since the new whoosh
35 version could introduce some incompatible index changes.
36 It is also recommended that you rebuild the whoosh index after upgrading since
37 the new whoosh version could introduce some incompatible index changes.
36 38
37 39
38 40 The final step is to upgrade the database. To do this simply run::
@@ -40,8 +42,8 b' The final step is to upgrade the databas'
40 42 paster upgrade-db production.ini
41 43
42 44 This will upgrade the schema and update some of the defaults in the database,
43 and will always recheck the settings of the application, if there are no new options
44 that need to be set.
45 and will always recheck the settings of the application, if there are no new
46 options that need to be set.
45 47
46 48
47 49 .. _virtualenv: http://pypi.python.org/pypi/virtualenv
@@ -25,11 +25,12 b''
25 25 # along with this program; if not, write to the Free Software
26 26 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
27 27 # MA 02110-1301, USA.
28
28 import platform
29 29
30 VERSION = (1, 1, 4)
30 VERSION = (1, 1, 5)
31 31 __version__ = '.'.join((str(each) for each in VERSION[:4]))
32 32 __dbversion__ = 2 #defines current db version for migrations
33 __platform__ = platform.system()
33 34
34 35 try:
35 36 from rhodecode.lib.utils import get_current_revision
@@ -71,7 +71,7 b' celery.result.serialier = json'
71 71 celeryd.concurrency = 2
72 72 #celeryd.log.file = celeryd.log
73 73 celeryd.log.level = debug
74 celeryd.max.tasks.per.child = 3
74 celeryd.max.tasks.per.child = 1
75 75
76 76 #tasks will never be sent to the queue, but executed locally instead.
77 77 celery.always.eager = false
@@ -140,8 +140,8 b' class SettingsController(BaseController)'
140 140
141 141 except:
142 142 log.error(traceback.format_exc())
143 h.flash(_('error occurred during updating application settings'),
144 category='error')
143 h.flash(_('error occurred during updating'
144 ' application settings'), category='error')
145 145
146 146 self.sa.rollback()
147 147
@@ -7,7 +7,7 b''
7 7
8 8 :created_on: Apr 21, 2010
9 9 :author: marcink
10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 11 :license: GPLv3, see COPYING for more details.
12 12 """
13 13 # This program is free software; you can redistribute it and/or
@@ -55,9 +55,30 b' class FilesController(BaseController):'
55 55 super(FilesController, self).__before__()
56 56 c.cut_off_limit = self.cut_off_limit
57 57
58 def __get_cs_or_redirect(self, rev, repo_name):
59 """
60 Safe way to get changeset if error occur it redirects to tip with
61 proper message
62
63 :param rev: revision to fetch
64 :param repo_name: repo name to redirect after
65 """
66
67 _repo = ScmModel().get_repo(c.repo_name)
68 try:
69 return _repo.get_changeset(rev)
70 except EmptyRepositoryError, e:
71 h.flash(_('There are no files yet'), category='warning')
72 redirect(h.url('summary_home', repo_name=repo_name))
73
74 except RepositoryError, e:
75 h.flash(str(e), category='warning')
76 redirect(h.url('files_home', repo_name=repo_name, revision='tip'))
77
58 78 def index(self, repo_name, revision, f_path):
59 hg_model = ScmModel()
60 c.repo = hg_model.get_repo(c.repo_name)
79 cs = self.__get_cs_or_redirect(revision, repo_name)
80 c.repo = ScmModel().get_repo(c.repo_name)
81
61 82 revision = request.POST.get('at_rev', None) or revision
62 83
63 84 def get_next_rev(cur):
@@ -72,68 +93,64 b' class FilesController(BaseController):'
72 93 return r
73 94
74 95 c.f_path = f_path
96 c.changeset = cs
97 cur_rev = c.changeset.revision
98 prev_rev = c.repo.get_changeset(get_prev_rev(cur_rev)).raw_id
99 next_rev = c.repo.get_changeset(get_next_rev(cur_rev)).raw_id
75 100
101 c.url_prev = url('files_home', repo_name=c.repo_name,
102 revision=prev_rev, f_path=f_path)
103 c.url_next = url('files_home', repo_name=c.repo_name,
104 revision=next_rev, f_path=f_path)
76 105
77 106 try:
78 c.changeset = c.repo.get_changeset(revision)
79 cur_rev = c.changeset.revision
80 prev_rev = c.repo.get_changeset(get_prev_rev(cur_rev)).raw_id
81 next_rev = c.repo.get_changeset(get_next_rev(cur_rev)).raw_id
82
83 c.url_prev = url('files_home', repo_name=c.repo_name,
84 revision=prev_rev, f_path=f_path)
85 c.url_next = url('files_home', repo_name=c.repo_name,
86 revision=next_rev, f_path=f_path)
87
88 try:
89 c.files_list = c.changeset.get_node(f_path)
90 c.file_history = self._get_history(c.repo, c.files_list, f_path)
91 except RepositoryError, e:
92 h.flash(str(e), category='warning')
93 redirect(h.url('files_home', repo_name=repo_name, revision=revision))
94
95 except EmptyRepositoryError, e:
96 h.flash(_('There are no files yet'), category='warning')
97 redirect(h.url('summary_home', repo_name=repo_name))
98
107 c.files_list = c.changeset.get_node(f_path)
108 c.file_history = self._get_history(c.repo, c.files_list, f_path)
99 109 except RepositoryError, e:
100 110 h.flash(str(e), category='warning')
101 redirect(h.url('files_home', repo_name=repo_name, revision='tip'))
102
111 redirect(h.url('files_home', repo_name=repo_name,
112 revision=revision))
103 113
104 114
105 115 return render('files/files.html')
106 116
107 117 def rawfile(self, repo_name, revision, f_path):
108 hg_model = ScmModel()
109 c.repo = hg_model.get_repo(c.repo_name)
110 file_node = c.repo.get_changeset(revision).get_node(f_path)
118 cs = self.__get_cs_or_redirect(revision, repo_name)
119 try:
120 file_node = cs.get_node(f_path)
121 except RepositoryError, e:
122 h.flash(str(e), category='warning')
123 redirect(h.url('files_home', repo_name=repo_name,
124 revision=cs.raw_id))
125
126 fname = f_path.split('/')[-1].encode('utf8', 'replace')
127
128 response.content_disposition = 'attachment; filename=%s' % fname
111 129 response.content_type = file_node.mimetype
112 response.content_disposition = 'attachment; filename=%s' \
113 % f_path.split('/')[-1]
114 130 return file_node.content
115 131
116 132 def raw(self, repo_name, revision, f_path):
117 hg_model = ScmModel()
118 c.repo = hg_model.get_repo(c.repo_name)
119 file_node = c.repo.get_changeset(revision).get_node(f_path)
133 cs = self.__get_cs_or_redirect(revision, repo_name)
134 try:
135 file_node = cs.get_node(f_path)
136 except RepositoryError, e:
137 h.flash(str(e), category='warning')
138 redirect(h.url('files_home', repo_name=repo_name,
139 revision=cs.raw_id))
140
120 141 response.content_type = 'text/plain'
121
122 142 return file_node.content
123 143
124 144 def annotate(self, repo_name, revision, f_path):
125 hg_model = ScmModel()
126 c.repo = hg_model.get_repo(c.repo_name)
127
145 cs = self.__get_cs_or_redirect(revision, repo_name)
128 146 try:
129 c.cs = c.repo.get_changeset(revision)
130 c.file = c.cs.get_node(f_path)
147 c.file = cs.get_node(f_path)
131 148 except RepositoryError, e:
132 149 h.flash(str(e), category='warning')
133 redirect(h.url('files_home', repo_name=repo_name, revision=revision))
150 redirect(h.url('files_home', repo_name=repo_name, revision=cs.raw_id))
134 151
135 c.file_history = self._get_history(c.repo, c.file, f_path)
136
152 c.file_history = self._get_history(ScmModel().get_repo(c.repo_name), c.file, f_path)
153 c.cs = cs
137 154 c.f_path = f_path
138 155
139 156 return render('files/files_annotate.html')
@@ -201,25 +218,34 b' class FilesController(BaseController):'
201 218 response.content_type = 'text/plain'
202 219 response.content_disposition = 'attachment; filename=%s' \
203 220 % diff_name
221 if node1.is_binary or node2.is_binary:
222 return _('binary file changed')
204 223 return diff.raw_diff()
205 224
206 225 elif c.action == 'raw':
207 226 response.content_type = 'text/plain'
227 if node1.is_binary or node2.is_binary:
228 return _('binary file changed')
208 229 return diff.raw_diff()
209 230
210 231 elif c.action == 'diff':
211 232 if node1.size > self.cut_off_limit or node2.size > self.cut_off_limit:
212 233 c.cur_diff = _('Diff is to big to display')
234 elif node1.is_binary or node2.is_binary:
235 c.cur_diff = _('Binary file')
213 236 else:
214 237 c.cur_diff = diff.as_html()
215 238 else:
216 239 #default option
217 240 if node1.size > self.cut_off_limit or node2.size > self.cut_off_limit:
218 241 c.cur_diff = _('Diff is to big to display')
242 elif node1.is_binary or node2.is_binary:
243 c.cur_diff = _('Binary file')
219 244 else:
220 245 c.cur_diff = diff.as_html()
221 246
222 if not c.cur_diff: c.no_changes = True
247 if not c.cur_diff:
248 c.no_changes = True
223 249 return render('files/file_diff.html')
224 250
225 251 def _get_history(self, repo, node, f_path):
@@ -250,9 +276,3 b' class FilesController(BaseController):'
250 276 hist_l.append(tags_group)
251 277
252 278 return hist_l
253
254 # [
255 # ([("u1", "User1"), ("u2", "User2")], "Users"),
256 # ([("g1", "Group1"), ("g2", "Group2")], "Groups")
257 # ]
258
@@ -43,7 +43,7 b' class HomeController(BaseController):'
43 43 super(HomeController, self).__before__()
44 44
45 45 def index(self):
46 sortables = ['name', 'description', 'last_change', 'tip', 'contact']
46 sortables = ['name', 'description', 'last_change', 'tip', 'owner']
47 47 current_sort = request.GET.get('sort', 'name')
48 48 current_sort_slug = current_sort.replace('-', '')
49 49
@@ -26,9 +26,12 b''
26 26 # MA 02110-1301, USA.
27 27
28 28 import logging
29 from sqlalchemy import or_
29 import traceback
30 30
31 31 from pylons import request, response, session, tmpl_context as c, url
32 from paste.httpexceptions import HTTPInternalServerError, HTTPBadRequest
33
34 from sqlalchemy import or_
32 35
33 36 from rhodecode.lib.auth import LoginRequired, NotAnonymous
34 37 from rhodecode.lib.base import BaseController, render
@@ -36,8 +39,6 b' from rhodecode.lib.helpers import get_to'
36 39 from rhodecode.model.db import UserLog, UserFollowing
37 40 from rhodecode.model.scm import ScmModel
38 41
39 from paste.httpexceptions import HTTPInternalServerError
40
41 42 log = logging.getLogger(__name__)
42 43
43 44 class JournalController(BaseController):
@@ -81,6 +82,7 b' class JournalController(BaseController):'
81 82 c.rhodecode_user.user_id)
82 83 return 'ok'
83 84 except:
85 log.error(traceback.format_exc())
84 86 raise HTTPInternalServerError()
85 87
86 88 repo_id = request.POST.get('follows_repo_id')
@@ -90,8 +92,9 b' class JournalController(BaseController):'
90 92 c.rhodecode_user.user_id)
91 93 return 'ok'
92 94 except:
95 log.error(traceback.format_exc())
93 96 raise HTTPInternalServerError()
94 97
95 98
96 99
97 raise HTTPInternalServerError()
100 raise HTTPBadRequest()
@@ -26,4 +26,21 b''
26 26 # MA 02110-1301, USA.
27 27
28 28 def str2bool(v):
29 return v.lower() in ["yes", "true", "t", "1"] if v else None
29 if isinstance(v, (str, unicode)):
30 obj = v.strip().lower()
31 if obj in ['true', 'yes', 'on', 'y', 't', '1']:
32 return True
33 elif obj in ['false', 'no', 'off', 'n', 'f', '0']:
34 return False
35 else:
36 raise ValueError("String is not true/false: %r" % obj)
37 return bool(obj)
38
39 def generate_api_key(username, salt=None):
40 from tempfile import _RandomNameSequence
41 import hashlib
42
43 if salt is None:
44 salt = _RandomNameSequence().next()
45
46 return hashlib.sha1(username + salt).hexdigest()
@@ -1,8 +1,14 b''
1 #!/usr/bin/env python
2 # encoding: utf-8
3 # authentication and permission libraries
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5 #
1 # -*- coding: utf-8 -*-
2 """
3 rhodecode.lib.auth
4 ~~~~~~~~~~~~~~~~~~
5
6 authentication and permission libraries
7
8 :created_on: Apr 4, 2010
9 :copyright: (c) 2010 by marcink.
10 :license: LICENSE_NAME, see LICENSE_FILE for more details.
11 """
6 12 # This program is free software; you can redistribute it and/or
7 13 # modify it under the terms of the GNU General Public License
8 14 # as published by the Free Software Foundation; version 2
@@ -17,26 +23,34 b''
17 23 # along with this program; if not, write to the Free Software
18 24 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 25 # MA 02110-1301, USA.
20 """
21 Created on April 4, 2010
22 26
23 @author: marcink
24 """
27 import random
28 import logging
29 import traceback
30
31 from decorator import decorator
32
25 33 from pylons import config, session, url, request
26 34 from pylons.controllers.util import abort, redirect
27 from rhodecode.lib.exceptions import *
35 from pylons.i18n.translation import _
36
37 from rhodecode import __platform__
38
39 if __platform__ == 'Windows':
40 from hashlib import sha256
41 if __platform__ in ('Linux', 'Darwin'):
42 import bcrypt
43
44 from rhodecode.lib import str2bool
45 from rhodecode.lib.exceptions import LdapPasswordError, LdapUsernameError
28 46 from rhodecode.lib.utils import get_repo_slug
29 47 from rhodecode.lib.auth_ldap import AuthLdap
48
30 49 from rhodecode.model import meta
31 50 from rhodecode.model.user import UserModel
32 from rhodecode.model.caching_query import FromCache
33 from rhodecode.model.db import User, RepoToPerm, Repository, Permission, \
34 UserToPerm
35 import bcrypt
36 from decorator import decorator
37 import logging
38 import random
39 import traceback
51 from rhodecode.model.db import Permission, RepoToPerm, Repository, \
52 User, UserToPerm
53
40 54
41 55 log = logging.getLogger(__name__)
42 56
@@ -65,15 +79,46 b' class PasswordGenerator(object):'
65 79 self.passwd = ''.join([random.choice(type) for _ in xrange(len)])
66 80 return self.passwd
67 81
82 class RhodeCodeCrypto(object):
83
84 @classmethod
85 def hash_string(cls, str_):
86 """
87 Cryptographic function used for password hashing based on pybcrypt
88 or pycrypto in windows
89
90 :param password: password to hash
91 """
92 if __platform__ == 'Windows':
93 return sha256(str_).hexdigest()
94 elif __platform__ in ('Linux', 'Darwin'):
95 return bcrypt.hashpw(str_, bcrypt.gensalt(10))
96 else:
97 raise Exception('Unknown or unsupported platform %s' % __platform__)
98
99 @classmethod
100 def hash_check(cls, password, hashed):
101 """
102 Checks matching password with it's hashed value, runs different
103 implementation based on platform it runs on
104
105 :param password: password
106 :param hashed: password in hashed form
107 """
108
109 if __platform__ == 'Windows':
110 return sha256(password).hexdigest() == hashed
111 elif __platform__ in ('Linux', 'Darwin'):
112 return bcrypt.hashpw(password, hashed) == hashed
113 else:
114 raise Exception('Unknown or unsupported platform %s' % __platform__)
115
68 116
69 117 def get_crypt_password(password):
70 """Cryptographic function used for password hashing based on sha1
71 :param password: password to hash
72 """
73 return bcrypt.hashpw(password, bcrypt.gensalt(10))
118 return RhodeCodeCrypto.hash_string(password)
74 119
75 120 def check_password(password, hashed):
76 return bcrypt.hashpw(password, hashed) == hashed
121 return RhodeCodeCrypto.hash_check(password, hashed)
77 122
78 123 def authfunc(environ, username, password):
79 124 """
@@ -126,7 +171,7 b' def authenticate(username, password):'
126 171 #======================================================================
127 172 # FALLBACK TO LDAP AUTH IN ENABLE
128 173 #======================================================================
129 if ldap_settings.get('ldap_active', False):
174 if str2bool(ldap_settings.get('ldap_active')):
130 175 log.debug("Authenticating user using ldap")
131 176 kwargs = {
132 177 'server':ldap_settings.get('ldap_host', ''),
@@ -134,7 +179,7 b' def authenticate(username, password):'
134 179 'port':ldap_settings.get('ldap_port'),
135 180 'bind_dn':ldap_settings.get('ldap_dn_user'),
136 181 'bind_pass':ldap_settings.get('ldap_dn_pass'),
137 'use_ldaps':ldap_settings.get('ldap_ldaps'),
182 'use_ldaps':str2bool(ldap_settings.get('ldap_ldaps')),
138 183 'ldap_version':3,
139 184 }
140 185 log.debug('Checking for ldap authentication')
@@ -1,7 +1,7 b''
1 1 #!/usr/bin/env python
2 2 # encoding: utf-8
3 3 # ldap authentication lib
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
4 # Copyright (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
5 5 #
6 6 # This program is free software; you can redistribute it and/or
7 7 # modify it under the terms of the GNU General Public License
@@ -60,39 +60,19 b' class DbManage(object):'
60 60 init_model(engine)
61 61 self.sa = meta.Session()
62 62
63 def check_for_db(self, override):
64 db_path = jn(self.root, self.dbname)
65 if self.dburi.startswith('sqlite'):
66 log.info('checking for existing db in %s', db_path)
67 if os.path.isfile(db_path):
68
69 self.db_exists = True
70 if not override:
71 raise Exception('database already exists')
72 return 'sqlite'
73 if self.dburi.startswith('postgresql'):
74 self.db_exists = True
75 return 'postgresql'
76
77
78 63 def create_tables(self, override=False):
79 64 """Create a auth database
80 65 """
81 66
82 db_type = self.check_for_db(override)
83 if self.db_exists:
84 log.info("database exist and it's going to be destroyed")
85 if self.tests:
86 destroy = True
87 else:
88 destroy = ask_ok('Are you sure to destroy old database ? [y/n]')
89 if not destroy:
90 sys.exit()
91 if self.db_exists and destroy:
92 if db_type == 'sqlite':
93 os.remove(jn(self.root, self.dbname))
94 if db_type == 'postgresql':
95 meta.Base.metadata.drop_all()
67 log.info("Any existing database is going to be destroyed")
68 if self.tests:
69 destroy = True
70 else:
71 destroy = ask_ok('Are you sure to destroy old database ? [y/n]')
72 if not destroy:
73 sys.exit()
74 if destroy:
75 meta.Base.metadata.drop_all()
96 76
97 77 checkfirst = not override
98 78 meta.Base.metadata.create_all(checkfirst=checkfirst)
@@ -322,10 +302,14 b' class DbManage(object):'
322 302 """Creates ldap settings"""
323 303
324 304 try:
325 for k in ['ldap_active', 'ldap_host', 'ldap_port', 'ldap_ldaps',
326 'ldap_dn_user', 'ldap_dn_pass', 'ldap_base_dn']:
305 for k, v in [('ldap_active', 'false'),
306 ('ldap_host', ''),
307 ('ldap_port', '389'),
308 ('ldap_ldaps', 'false'),
309 ('ldap_dn_user', ''), ('ldap_dn_pass', ''),
310 ('ldap_base_dn', '')]:
327 311
328 setting = RhodeCodeSettings(k, '')
312 setting = RhodeCodeSettings(k, v)
329 313 self.sa.add(setting)
330 314 self.sa.commit()
331 315 except:
@@ -230,6 +230,8 b' tooltip = _ToolTip()'
230 230 class _FilesBreadCrumbs(object):
231 231
232 232 def __call__(self, repo_name, rev, paths):
233 if isinstance(paths, str):
234 paths = paths.decode('utf-8', 'replace')
233 235 url_l = [link_to(repo_name, url('files_home',
234 236 repo_name=repo_name,
235 237 revision=rev, f_path=''))]
@@ -483,7 +485,7 b' def action_parser_icon(user_log):'
483 485 if len(x) > 1:
484 486 action, action_params = x
485 487
486 tmpl = """<img src="%s/%s" alt="%s"/>"""
488 tmpl = """<img src="%s%s" alt="%s"/>"""
487 489 map = {'user_deleted_repo':'database_delete.png',
488 490 'user_created_repo':'database_add.png',
489 491 'user_forked_repo':'arrow_divide.png',
@@ -550,6 +552,6 b' def changed_tooltip(nodes):'
550 552 suf = ''
551 553 if len(nodes) > 30:
552 554 suf = '<br/>' + _(' and %s more') % (len(nodes) - 30)
553 return literal(pref + '<br/> '.join([x.path for x in nodes[:30]]) + suf)
555 return literal(pref + '<br/> '.join([x.path.decode('utf-8', 'replace') for x in nodes[:30]]) + suf)
554 556 else:
555 557 return ': ' + _('No Files')
@@ -30,51 +30,20 b' from datetime import date'
30 30
31 31 from sqlalchemy import *
32 32 from sqlalchemy.exc import DatabaseError
33 from sqlalchemy.orm import relationship, backref, class_mapper
34 from sqlalchemy.orm.session import Session
33 from sqlalchemy.orm import relationship, backref
34 from sqlalchemy.orm.interfaces import MapperExtension
35 35
36 from rhodecode.model.meta import Base
36 from rhodecode.model.meta import Base, Session
37 37
38 38 log = logging.getLogger(__name__)
39 39
40 class BaseModel(object):
41 40
42 @classmethod
43 def _get_keys(cls):
44 """return column names for this model """
45 return class_mapper(cls).c.keys()
46
47 def get_dict(self):
48 """return dict with keys and values corresponding
49 to this model data """
50
51 d = {}
52 for k in self._get_keys():
53 d[k] = getattr(self, k)
54 return d
55
56 def get_appstruct(self):
57 """return list with keys and values tupples corresponding
58 to this model data """
59
60 l = []
61 for k in self._get_keys():
62 l.append((k, getattr(self, k),))
63 return l
64
65 def populate_obj(self, populate_dict):
66 """populate model with data from given populate_dict"""
67
68 for k in self._get_keys():
69 if k in populate_dict:
70 setattr(self, k, populate_dict[k])
71
72 class RhodeCodeSettings(Base, BaseModel):
41 class RhodeCodeSettings(Base):
73 42 __tablename__ = 'rhodecode_settings'
74 43 __table_args__ = (UniqueConstraint('app_settings_name'), {'useexisting':True})
75 44 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
76 app_settings_name = Column("app_settings_name", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
77 app_settings_value = Column("app_settings_value", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
45 app_settings_name = Column("app_settings_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
46 app_settings_value = Column("app_settings_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
78 47
79 48 def __init__(self, k='', v=''):
80 49 self.app_settings_name = k
@@ -84,27 +53,27 b' class RhodeCodeSettings(Base, BaseModel)'
84 53 return "<%s('%s:%s')>" % (self.__class__.__name__,
85 54 self.app_settings_name, self.app_settings_value)
86 55
87 class RhodeCodeUi(Base, BaseModel):
56 class RhodeCodeUi(Base):
88 57 __tablename__ = 'rhodecode_ui'
89 58 __table_args__ = {'useexisting':True}
90 59 ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
91 ui_section = Column("ui_section", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
92 ui_key = Column("ui_key", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
93 ui_value = Column("ui_value", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
60 ui_section = Column("ui_section", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
61 ui_key = Column("ui_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
62 ui_value = Column("ui_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
94 63 ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
95 64
96 65
97 class User(Base, BaseModel):
66 class User(Base):
98 67 __tablename__ = 'users'
99 68 __table_args__ = (UniqueConstraint('username'), UniqueConstraint('email'), {'useexisting':True})
100 69 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
101 username = Column("username", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
102 password = Column("password", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
70 username = Column("username", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
71 password = Column("password", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
103 72 active = Column("active", Boolean(), nullable=True, unique=None, default=None)
104 73 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
105 name = Column("name", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
106 lastname = Column("lastname", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
107 email = Column("email", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
74 name = Column("name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
75 lastname = Column("lastname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
76 email = Column("email", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
108 77 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
109 78 is_ldap = Column("is_ldap", Boolean(), nullable=False, unique=None, default=False)
110 79
@@ -118,6 +87,10 b' class User(Base, BaseModel):'
118 87 def full_contact(self):
119 88 return '%s %s <%s>' % (self.name, self.lastname, self.email)
120 89
90 @property
91 def short_contact(self):
92 return '%s %s' % (self.name, self.lastname)
93
121 94
122 95 @property
123 96 def is_admin(self):
@@ -127,6 +100,11 b' class User(Base, BaseModel):'
127 100 return "<%s('id:%s:%s')>" % (self.__class__.__name__,
128 101 self.user_id, self.username)
129 102
103 @classmethod
104 def by_username(cls, username):
105 return Session.query(cls).filter(cls.username == username).one()
106
107
130 108 def update_lastlogin(self):
131 109 """Update user lastlogin"""
132 110
@@ -140,15 +118,15 b' class User(Base, BaseModel):'
140 118 session.rollback()
141 119
142 120
143 class UserLog(Base, BaseModel):
121 class UserLog(Base):
144 122 __tablename__ = 'user_logs'
145 123 __table_args__ = {'useexisting':True}
146 124 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
147 125 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
148 126 repository_id = Column("repository_id", Integer(length=None, convert_unicode=False, assert_unicode=None), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
149 repository_name = Column("repository_name", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
150 user_ip = Column("user_ip", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
151 action = Column("action", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
127 repository_name = Column("repository_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
128 user_ip = Column("user_ip", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
129 action = Column("action", UnicodeText(length=1200000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
152 130 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
153 131
154 132 @property
@@ -158,16 +136,16 b' class UserLog(Base, BaseModel):'
158 136 user = relationship('User')
159 137 repository = relationship('Repository')
160 138
161 class Repository(Base, BaseModel):
139 class Repository(Base):
162 140 __tablename__ = 'repositories'
163 141 __table_args__ = (UniqueConstraint('repo_name'), {'useexisting':True},)
164 142 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
165 repo_name = Column("repo_name", String(length=None, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
166 repo_type = Column("repo_type", String(length=None, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default='hg')
143 repo_name = Column("repo_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
144 repo_type = Column("repo_type", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default='hg')
167 145 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
168 146 private = Column("private", Boolean(), nullable=True, unique=None, default=None)
169 147 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
170 description = Column("description", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
148 description = Column("description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
171 149 fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
172 150
173 151 user = relationship('User')
@@ -178,23 +156,23 b' class Repository(Base, BaseModel):'
178 156 repo_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id', cascade='all')
179 157
180 158 logs = relationship('UserLog', cascade='all')
181
159
182 160 def __repr__(self):
183 161 return "<%s('%s:%s')>" % (self.__class__.__name__,
184 162 self.repo_id, self.repo_name)
185 163
186 class Permission(Base, BaseModel):
164 class Permission(Base):
187 165 __tablename__ = 'permissions'
188 166 __table_args__ = {'useexisting':True}
189 167 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
190 permission_name = Column("permission_name", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
191 permission_longname = Column("permission_longname", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
168 permission_name = Column("permission_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
169 permission_longname = Column("permission_longname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
192 170
193 171 def __repr__(self):
194 172 return "<%s('%s:%s')>" % (self.__class__.__name__,
195 173 self.permission_id, self.permission_name)
196 174
197 class RepoToPerm(Base, BaseModel):
175 class RepoToPerm(Base):
198 176 __tablename__ = 'repo_to_perm'
199 177 __table_args__ = (UniqueConstraint('user_id', 'repository_id'), {'useexisting':True})
200 178 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
@@ -206,7 +184,7 b' class RepoToPerm(Base, BaseModel):'
206 184 permission = relationship('Permission')
207 185 repository = relationship('Repository')
208 186
209 class UserToPerm(Base, BaseModel):
187 class UserToPerm(Base):
210 188 __tablename__ = 'user_to_perm'
211 189 __table_args__ = (UniqueConstraint('user_id', 'permission_id'), {'useexisting':True})
212 190 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
@@ -216,19 +194,19 b' class UserToPerm(Base, BaseModel):'
216 194 user = relationship('User')
217 195 permission = relationship('Permission')
218 196
219 class Statistics(Base, BaseModel):
197 class Statistics(Base):
220 198 __tablename__ = 'statistics'
221 199 __table_args__ = (UniqueConstraint('repository_id'), {'useexisting':True})
222 200 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
223 repository_id = Column("repository_id", Integer(), ForeignKey(u'repositories.repo_id'), nullable=False, unique=True, default=None)
201 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
224 202 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
225 commit_activity = Column("commit_activity", LargeBinary(), nullable=False)#JSON data
203 commit_activity = Column("commit_activity", LargeBinary(1000000), nullable=False)#JSON data
226 204 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
227 languages = Column("languages", LargeBinary(), nullable=False)#JSON data
205 languages = Column("languages", LargeBinary(1000000), nullable=False)#JSON data
228 206
229 207 repository = relationship('Repository', single_parent=True)
230 208
231 class UserFollowing(Base, BaseModel):
209 class UserFollowing(Base):
232 210 __tablename__ = 'user_followings'
233 211 __table_args__ = (UniqueConstraint('user_id', 'follows_repository_id'),
234 212 UniqueConstraint('user_id', 'follows_user_id')
@@ -244,12 +222,12 b' class UserFollowing(Base, BaseModel):'
244 222 follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
245 223 follows_repository = relationship('Repository', order_by='Repository.repo_name')
246 224
247 class CacheInvalidation(Base, BaseModel):
225 class CacheInvalidation(Base):
248 226 __tablename__ = 'cache_invalidation'
249 227 __table_args__ = (UniqueConstraint('cache_key'), {'useexisting':True})
250 228 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
251 cache_key = Column("cache_key", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
252 cache_args = Column("cache_args", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
229 cache_key = Column("cache_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
230 cache_args = Column("cache_args", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
253 231 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
254 232
255 233
@@ -262,10 +240,10 b' class CacheInvalidation(Base, BaseModel)'
262 240 return "<%s('%s:%s')>" % (self.__class__.__name__,
263 241 self.cache_id, self.cache_key)
264 242
265 class DbMigrateVersion(Base, BaseModel):
243 class DbMigrateVersion(Base):
266 244 __tablename__ = 'db_migrate_version'
267 245 __table_args__ = {'useexisting':True}
268 repository_id = Column('repository_id', String(250), primary_key=True)
246 repository_id = Column('repository_id', String(255), primary_key=True)
269 247 repository_path = Column('repository_path', Text)
270 248 version = Column('version', Integer)
271 249
@@ -1,8 +1,10 b''
1 1 """SQLAlchemy Metadata and Session object"""
2 2 from sqlalchemy.ext.declarative import declarative_base
3 from sqlalchemy.orm import scoped_session, sessionmaker
3 from sqlalchemy.orm import scoped_session, sessionmaker, class_mapper
4 from beaker import cache
5
4 6 from rhodecode.model import caching_query
5 from beaker import cache
7
6 8
7 9 # Beaker CacheManager. A home base for cache configurations.
8 10 cache_manager = cache.CacheManager()
@@ -17,10 +19,52 b' Session = scoped_session('
17 19 )
18 20 )
19 21
22 class BaseModel(object):
23 """Base Model for all classess
24
25 """
26
27 @classmethod
28 def _get_keys(cls):
29 """return column names for this model """
30 return class_mapper(cls).c.keys()
31
32 def get_dict(self):
33 """return dict with keys and values corresponding
34 to this model data """
35
36 d = {}
37 for k in self._get_keys():
38 d[k] = getattr(self, k)
39 return d
40
41 def get_appstruct(self):
42 """return list with keys and values tupples corresponding
43 to this model data """
44
45 l = []
46 for k in self._get_keys():
47 l.append((k, getattr(self, k),))
48 return l
49
50 def populate_obj(self, populate_dict):
51 """populate model with data from given populate_dict"""
52
53 for k in self._get_keys():
54 if k in populate_dict:
55 setattr(self, k, populate_dict[k])
56
57 @classmethod
58 def query(cls):
59 return Session.query(cls)
60
61 @classmethod
62 def get(cls, id_):
63 return Session.query(cls).get(id_)
64
65
20 66 # The declarative Base
21 Base = declarative_base()
22 #For another db...
23 #Base2 = declarative_base()
67 Base = declarative_base(cls=BaseModel)
24 68
25 69 #to use cache use this in query
26 70 #.options(FromCache("sqlalchemy_cache_type", "cachekey"))
@@ -146,6 +146,7 b' class ScmModel(BaseModel):'
146 146 tmp_d['rev'] = tip.revision
147 147 tmp_d['contact'] = repo.dbrepo.user.full_contact
148 148 tmp_d['contact_sort'] = tmp_d['contact']
149 tmp_d['owner_sort'] = tmp_d['contact']
149 150 tmp_d['repo_archives'] = list(repo._get_archives())
150 151 tmp_d['last_msg'] = tip.message
151 152 tmp_d['repo'] = repo
@@ -1,19 +1,19 b''
1 1 import sys
2 from rhodecode import get_version
3 from rhodecode import __platform__
4
2 5 py_version = sys.version_info
3 6
4 from rhodecode import get_version
5
6 7 requirements = [
7 8 "Pylons==1.0.0",
8 9 "WebHelpers==1.2",
9 10 "SQLAlchemy==0.6.6",
10 "Mako==0.3.6",
11 "vcs==0.1.10",
12 "pygments==1.3.1",
11 "Mako==0.4.0",
12 "vcs==0.1.11",
13 "pygments==1.4.0",
13 14 "mercurial==1.7.5",
14 15 "whoosh==1.3.4",
15 "celery==2.1.4",
16 "py-bcrypt",
16 "celery==2.2.4",
17 17 "babel",
18 18 ]
19 19
@@ -25,10 +25,14 b" classifiers = ['Development Status :: 5 "
25 25 'Operating System :: OS Independent',
26 26 'Programming Language :: Python', ]
27 27
28 if sys.version_info < (2, 6):
28 if py_version < (2, 6):
29 29 requirements.append("simplejson")
30 30 requirements.append("pysqlite")
31 31
32 if __platform__ in ('Linux', 'Darwin'):
33 requirements.append("py-bcrypt")
34
35
32 36 #additional files from project that goes somewhere in the filesystem
33 37 #relative to sys.prefix
34 38 data_files = []
@@ -38,6 +42,10 b" package_data = {'rhodecode': ['i18n/*/LC"
38 42
39 43 description = ('Mercurial repository browser/management with '
40 44 'build in push/pull server and full text search')
45 keywords = ' '.join (['rhodecode', 'rhodiumcode', 'mercurial', 'git',
46 'repository management', 'hgweb replacement'
47 'hgwebdir', 'gitweb replacement', 'serving hgweb',
48 ])
41 49 #long description
42 50 try:
43 51 readme_file = 'README.rst'
@@ -66,7 +74,7 b' setup('
66 74 version=get_version(),
67 75 description=description,
68 76 long_description=long_description,
69 keywords='rhodiumcode mercurial web hgwebdir gitweb git replacement serving hgweb rhodecode',
77 keywords=keywords,
70 78 license='BSD',
71 79 author='Marcin Kuzminski',
72 80 author_email='marcin@python-works.com',
General Comments 0
You need to be logged in to leave comments. Login now