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. |
|
|
13 |
and donation ware and focuses more on providing a customized, |
|
|
14 |
interface for Mercurial(and soon GIT) repositories. |
|
|
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 |
|
|
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 |
|
|
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 |
|
|
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 |
|
|
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 |
|
|
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 |
|
|
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 |
|
|
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, |
|
|
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 = |
|
|
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 |
|
|
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-201 |
|
|
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 = |
|
|
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, |
|
|
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 |
|
|
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 |
|
|
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= |
|
|
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: |
|
|
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', ' |
|
|
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 HTTP |
|
|
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 |
|
|
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' |
|
|
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-201 |
|
|
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. |
|
|
84 | log.info("database exist and it's going to be destroyed") | |
|
85 | if self.tests: | |
|
86 | destroy = True | |
|
87 |
|
|
|
88 | destroy = ask_ok('Are you sure to destroy old database ? [y/n]') | |
|
89 |
|
|
|
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', ' |
|
|
326 |
'ldap_ |
|
|
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 |
|
|
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 |
|
|
34 |
from sqlalchemy.orm. |
|
|
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= |
|
|
77 |
app_settings_value = Column("app_settings_value", String(length= |
|
|
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 |
|
|
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= |
|
|
92 |
ui_key = Column("ui_key", String(length= |
|
|
93 |
ui_value = Column("ui_value", String(length= |
|
|
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 |
|
|
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= |
|
|
102 |
password = Column("password", String(length= |
|
|
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= |
|
|
106 |
lastname = Column("lastname", String(length= |
|
|
107 |
email = Column("email", String(length= |
|
|
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 |
|
|
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= |
|
|
150 |
user_ip = Column("user_ip", String(length= |
|
|
151 |
action = Column("action", |
|
|
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 |
|
|
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= |
|
|
166 |
repo_type = Column("repo_type", String(length= |
|
|
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= |
|
|
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 |
|
|
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= |
|
|
191 |
permission_longname = Column("permission_longname", String(length= |
|
|
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 |
|
|
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 |
|
|
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 |
|
|
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( |
|
|
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 |
|
|
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 |
|
|
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= |
|
|
252 |
cache_args = Column("cache_args", String(length= |
|
|
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 |
|
|
243 | class DbMigrateVersion(Base): | |
|
266 | 244 | __tablename__ = 'db_migrate_version' |
|
267 | 245 | __table_args__ = {'useexisting':True} |
|
268 |
repository_id = Column('repository_id', String(25 |
|
|
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. |
|
|
11 |
"vcs==0.1.1 |
|
|
12 |
"pygments==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. |
|
|
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 |
|
|
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