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. |
|
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, |
|
13 | It is open source and donation ware and focuses more on providing a customized, | |
14 |
interface for Mercurial(and soon GIT) repositories. |
|
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 |
|
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 |
|
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 |
|
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 | ------------------------------------- |
@@ -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 |
|
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 |
|
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 |
|
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 |
|
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, |
|
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 = |
|
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 |
|
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-201 |
|
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 = |
|
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,10 +93,7 b' class FilesController(BaseController):' | |||||
72 | return r |
|
93 | return r | |
73 |
|
94 | |||
74 | c.f_path = f_path |
|
95 | c.f_path = f_path | |
75 |
|
96 | c.changeset = cs | ||
76 |
|
||||
77 | try: |
|
|||
78 | c.changeset = c.repo.get_changeset(revision) |
|
|||
79 |
|
|
97 | cur_rev = c.changeset.revision | |
80 |
|
|
98 | prev_rev = c.repo.get_changeset(get_prev_rev(cur_rev)).raw_id | |
81 |
|
|
99 | next_rev = c.repo.get_changeset(get_next_rev(cur_rev)).raw_id | |
@@ -90,50 +108,49 b' class FilesController(BaseController):' | |||||
90 |
|
|
108 | c.file_history = self._get_history(c.repo, c.files_list, f_path) | |
91 |
|
|
109 | except RepositoryError, e: | |
92 |
|
|
110 | h.flash(str(e), category='warning') | |
93 |
|
|
111 | redirect(h.url('files_home', repo_name=repo_name, | |
94 |
|
112 | revision=revision)) | ||
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: |
|
|||
100 | h.flash(str(e), category='warning') |
|
|||
101 | redirect(h.url('files_home', repo_name=repo_name, revision='tip')) |
|
|||
102 |
|
||||
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 |
|
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 |
|
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= |
|
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: |
|
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', ' |
|
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 HTTP |
|
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): | |||
68 |
|
83 | |||
69 | def get_crypt_password(password): |
|
84 | @classmethod | |
70 | """Cryptographic function used for password hashing based on sha1 |
|
85 | def hash_string(cls, str_): | |
|
86 | """ | |||
|
87 | Cryptographic function used for password hashing based on pybcrypt | |||
|
88 | or pycrypto in windows | |||
|
89 | ||||
71 | :param password: password to hash |
|
90 | :param password: password to hash | |
72 | """ |
|
91 | """ | |
73 | return bcrypt.hashpw(password, bcrypt.gensalt(10)) |
|
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 | ||||
|
116 | ||||
|
117 | def get_crypt_password(password): | |||
|
118 | return RhodeCodeCrypto.hash_string(password) | |||
74 |
|
119 | |||
75 | def check_password(password, hashed): |
|
120 | def check_password(password, hashed): | |
76 |
return |
|
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' |
|
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-201 |
|
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,38 +60,18 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: |
|
|||
84 | log.info("database exist and it's going to be destroyed") |
|
|||
85 |
|
|
68 | if self.tests: | |
86 |
|
|
69 | destroy = True | |
87 |
|
|
70 | else: | |
88 |
|
|
71 | destroy = ask_ok('Are you sure to destroy old database ? [y/n]') | |
89 |
|
|
72 | if not destroy: | |
90 |
|
|
73 | sys.exit() | |
91 |
|
|
74 | if destroy: | |
92 | if db_type == 'sqlite': |
|
|||
93 | os.remove(jn(self.root, self.dbname)) |
|
|||
94 | if db_type == 'postgresql': |
|
|||
95 |
|
|
75 | meta.Base.metadata.drop_all() | |
96 |
|
76 | |||
97 | checkfirst = not override |
|
77 | checkfirst = not override | |
@@ -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', ' |
|
305 | for k, v in [('ldap_active', 'false'), | |
326 |
'ldap_ |
|
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 |
|
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 |
|
33 | from sqlalchemy.orm import relationship, backref | |
34 |
from sqlalchemy.orm. |
|
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= |
|
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= |
|
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 |
|
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= |
|
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= |
|
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= |
|
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 |
|
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= |
|
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= |
|
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= |
|
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= |
|
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= |
|
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 |
|
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= |
|
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= |
|
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", |
|
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 |
|
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= |
|
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= |
|
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= |
|
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') | |
@@ -183,18 +161,18 b' class Repository(Base, BaseModel):' | |||||
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 |
|
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= |
|
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= |
|
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 |
|
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 |
|
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 |
|
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( |
|
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 |
|
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 |
|
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= |
|
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= |
|
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 |
|
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(25 |
|
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. |
|
11 | "Mako==0.4.0", | |
11 |
"vcs==0.1.1 |
|
12 | "vcs==0.1.11", | |
12 |
"pygments==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. |
|
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 |
|
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