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