##// END OF EJS Templates
invalidation: merge .invalidate and .set_valid as .test_and_set_valid...
Mads Kiilerich -
r3772:910ad1ff beta
parent child Browse files
Show More
@@ -1,181 +1,179 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.controllers.feed
3 rhodecode.controllers.feed
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 Feed controller for rhodecode
6 Feed controller for rhodecode
7
7
8 :created_on: Apr 23, 2010
8 :created_on: Apr 23, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2010-2012 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 modify
13 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 3 of the License, or
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25
25
26 import logging
26 import logging
27
27
28 from pylons import url, response, tmpl_context as c
28 from pylons import url, response, tmpl_context as c
29 from pylons.i18n.translation import _
29 from pylons.i18n.translation import _
30
30
31 from beaker.cache import cache_region, region_invalidate
31 from beaker.cache import cache_region, region_invalidate
32 from webhelpers.feedgenerator import Atom1Feed, Rss201rev2Feed
32 from webhelpers.feedgenerator import Atom1Feed, Rss201rev2Feed
33
33
34 from rhodecode.lib import helpers as h
34 from rhodecode.lib import helpers as h
35 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
35 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
36 from rhodecode.lib.base import BaseRepoController
36 from rhodecode.lib.base import BaseRepoController
37 from rhodecode.lib.diffs import DiffProcessor, LimitedDiffContainer
37 from rhodecode.lib.diffs import DiffProcessor, LimitedDiffContainer
38 from rhodecode.model.db import CacheInvalidation
38 from rhodecode.model.db import CacheInvalidation
39 from rhodecode.lib.utils2 import safe_int, str2bool, safe_unicode
39 from rhodecode.lib.utils2 import safe_int, str2bool, safe_unicode
40
40
41 log = logging.getLogger(__name__)
41 log = logging.getLogger(__name__)
42
42
43
43
44 class FeedController(BaseRepoController):
44 class FeedController(BaseRepoController):
45
45
46 @LoginRequired(api_access=True)
46 @LoginRequired(api_access=True)
47 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
47 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
48 'repository.admin')
48 'repository.admin')
49 def __before__(self):
49 def __before__(self):
50 super(FeedController, self).__before__()
50 super(FeedController, self).__before__()
51 #common values for feeds
51 #common values for feeds
52 self.description = _('Changes on %s repository')
52 self.description = _('Changes on %s repository')
53 self.title = self.title = _('%s %s feed') % (c.rhodecode_name, '%s')
53 self.title = self.title = _('%s %s feed') % (c.rhodecode_name, '%s')
54 self.language = 'en-us'
54 self.language = 'en-us'
55 self.ttl = "5"
55 self.ttl = "5"
56 import rhodecode
56 import rhodecode
57 CONF = rhodecode.CONFIG
57 CONF = rhodecode.CONFIG
58 self.include_diff = str2bool(CONF.get('rss_include_diff', False))
58 self.include_diff = str2bool(CONF.get('rss_include_diff', False))
59 self.feed_nr = safe_int(CONF.get('rss_items_per_page', 20))
59 self.feed_nr = safe_int(CONF.get('rss_items_per_page', 20))
60 # we need to protect from parsing huge diffs here other way
60 # we need to protect from parsing huge diffs here other way
61 # we can kill the server
61 # we can kill the server
62 self.feed_diff_limit = safe_int(CONF.get('rss_cut_off_limit', 32 * 1024))
62 self.feed_diff_limit = safe_int(CONF.get('rss_cut_off_limit', 32 * 1024))
63
63
64 def _get_title(self, cs):
64 def _get_title(self, cs):
65 return "%s" % (
65 return "%s" % (
66 h.shorter(cs.message, 160)
66 h.shorter(cs.message, 160)
67 )
67 )
68
68
69 def __changes(self, cs):
69 def __changes(self, cs):
70 changes = []
70 changes = []
71 diff_processor = DiffProcessor(cs.diff(),
71 diff_processor = DiffProcessor(cs.diff(),
72 diff_limit=self.feed_diff_limit)
72 diff_limit=self.feed_diff_limit)
73 _parsed = diff_processor.prepare(inline_diff=False)
73 _parsed = diff_processor.prepare(inline_diff=False)
74 limited_diff = False
74 limited_diff = False
75 if isinstance(_parsed, LimitedDiffContainer):
75 if isinstance(_parsed, LimitedDiffContainer):
76 limited_diff = True
76 limited_diff = True
77
77
78 for st in _parsed:
78 for st in _parsed:
79 st.update({'added': st['stats'][0],
79 st.update({'added': st['stats'][0],
80 'removed': st['stats'][1]})
80 'removed': st['stats'][1]})
81 changes.append('\n %(operation)s %(filename)s '
81 changes.append('\n %(operation)s %(filename)s '
82 '(%(added)s lines added, %(removed)s lines removed)'
82 '(%(added)s lines added, %(removed)s lines removed)'
83 % st)
83 % st)
84 if limited_diff:
84 if limited_diff:
85 changes = changes + ['\n ' +
85 changes = changes + ['\n ' +
86 _('Changeset was too big and was cut off...')]
86 _('Changeset was too big and was cut off...')]
87 return diff_processor, changes
87 return diff_processor, changes
88
88
89 def __get_desc(self, cs):
89 def __get_desc(self, cs):
90 desc_msg = []
90 desc_msg = []
91 desc_msg.append((_('%s committed on %s')
91 desc_msg.append((_('%s committed on %s')
92 % (h.person(cs.author), h.fmt_date(cs.date))) + '<br/>')
92 % (h.person(cs.author), h.fmt_date(cs.date))) + '<br/>')
93 #branches, tags, bookmarks
93 #branches, tags, bookmarks
94 if cs.branch:
94 if cs.branch:
95 desc_msg.append('branch: %s<br/>' % cs.branch)
95 desc_msg.append('branch: %s<br/>' % cs.branch)
96 if h.is_hg(c.rhodecode_repo):
96 if h.is_hg(c.rhodecode_repo):
97 for book in cs.bookmarks:
97 for book in cs.bookmarks:
98 desc_msg.append('bookmark: %s<br/>' % book)
98 desc_msg.append('bookmark: %s<br/>' % book)
99 for tag in cs.tags:
99 for tag in cs.tags:
100 desc_msg.append('tag: %s<br/>' % tag)
100 desc_msg.append('tag: %s<br/>' % tag)
101 diff_processor, changes = self.__changes(cs)
101 diff_processor, changes = self.__changes(cs)
102 # rev link
102 # rev link
103 _url = url('changeset_home', repo_name=cs.repository.name,
103 _url = url('changeset_home', repo_name=cs.repository.name,
104 revision=cs.raw_id, qualified=True)
104 revision=cs.raw_id, qualified=True)
105 desc_msg.append('changeset: <a href="%s">%s</a>' % (_url, cs.raw_id[:8]))
105 desc_msg.append('changeset: <a href="%s">%s</a>' % (_url, cs.raw_id[:8]))
106
106
107 desc_msg.append('<pre>')
107 desc_msg.append('<pre>')
108 desc_msg.append(cs.message)
108 desc_msg.append(cs.message)
109 desc_msg.append('\n')
109 desc_msg.append('\n')
110 desc_msg.extend(changes)
110 desc_msg.extend(changes)
111 if self.include_diff:
111 if self.include_diff:
112 desc_msg.append('\n\n')
112 desc_msg.append('\n\n')
113 desc_msg.append(diff_processor.as_raw())
113 desc_msg.append(diff_processor.as_raw())
114 desc_msg.append('</pre>')
114 desc_msg.append('</pre>')
115 return map(safe_unicode, desc_msg)
115 return map(safe_unicode, desc_msg)
116
116
117 def atom(self, repo_name):
117 def atom(self, repo_name):
118 """Produce an atom-1.0 feed via feedgenerator module"""
118 """Produce an atom-1.0 feed via feedgenerator module"""
119
119
120 @cache_region('long_term')
120 @cache_region('long_term')
121 def _get_feed_from_cache(key):
121 def _get_feed_from_cache(key):
122 feed = Atom1Feed(
122 feed = Atom1Feed(
123 title=self.title % repo_name,
123 title=self.title % repo_name,
124 link=url('summary_home', repo_name=repo_name,
124 link=url('summary_home', repo_name=repo_name,
125 qualified=True),
125 qualified=True),
126 description=self.description % repo_name,
126 description=self.description % repo_name,
127 language=self.language,
127 language=self.language,
128 ttl=self.ttl
128 ttl=self.ttl
129 )
129 )
130
130
131 for cs in reversed(list(c.rhodecode_repo[-self.feed_nr:])):
131 for cs in reversed(list(c.rhodecode_repo[-self.feed_nr:])):
132 feed.add_item(title=self._get_title(cs),
132 feed.add_item(title=self._get_title(cs),
133 link=url('changeset_home', repo_name=repo_name,
133 link=url('changeset_home', repo_name=repo_name,
134 revision=cs.raw_id, qualified=True),
134 revision=cs.raw_id, qualified=True),
135 author_name=cs.author,
135 author_name=cs.author,
136 description=''.join(self.__get_desc(cs)),
136 description=''.join(self.__get_desc(cs)),
137 pubdate=cs.date,
137 pubdate=cs.date,
138 )
138 )
139
139
140 response.content_type = feed.mime_type
140 response.content_type = feed.mime_type
141 return feed.writeString('utf-8')
141 return feed.writeString('utf-8')
142
142
143 key = repo_name + '_ATOM'
143 key = repo_name + '_ATOM'
144 inv = CacheInvalidation.invalidate(key)
144 valid = CacheInvalidation.test_and_set_valid(key)
145 if inv is not None:
145 if not valid:
146 region_invalidate(_get_feed_from_cache, None, key)
146 region_invalidate(_get_feed_from_cache, None, key)
147 CacheInvalidation.set_valid(inv.cache_key)
148 return _get_feed_from_cache(key)
147 return _get_feed_from_cache(key)
149
148
150 def rss(self, repo_name):
149 def rss(self, repo_name):
151 """Produce an rss2 feed via feedgenerator module"""
150 """Produce an rss2 feed via feedgenerator module"""
152
151
153 @cache_region('long_term')
152 @cache_region('long_term')
154 def _get_feed_from_cache(key):
153 def _get_feed_from_cache(key):
155 feed = Rss201rev2Feed(
154 feed = Rss201rev2Feed(
156 title=self.title % repo_name,
155 title=self.title % repo_name,
157 link=url('summary_home', repo_name=repo_name,
156 link=url('summary_home', repo_name=repo_name,
158 qualified=True),
157 qualified=True),
159 description=self.description % repo_name,
158 description=self.description % repo_name,
160 language=self.language,
159 language=self.language,
161 ttl=self.ttl
160 ttl=self.ttl
162 )
161 )
163
162
164 for cs in reversed(list(c.rhodecode_repo[-self.feed_nr:])):
163 for cs in reversed(list(c.rhodecode_repo[-self.feed_nr:])):
165 feed.add_item(title=self._get_title(cs),
164 feed.add_item(title=self._get_title(cs),
166 link=url('changeset_home', repo_name=repo_name,
165 link=url('changeset_home', repo_name=repo_name,
167 revision=cs.raw_id, qualified=True),
166 revision=cs.raw_id, qualified=True),
168 author_name=cs.author,
167 author_name=cs.author,
169 description=''.join(self.__get_desc(cs)),
168 description=''.join(self.__get_desc(cs)),
170 pubdate=cs.date,
169 pubdate=cs.date,
171 )
170 )
172
171
173 response.content_type = feed.mime_type
172 response.content_type = feed.mime_type
174 return feed.writeString('utf-8')
173 return feed.writeString('utf-8')
175
174
176 key = repo_name + '_RSS'
175 key = repo_name + '_RSS'
177 inv = CacheInvalidation.invalidate(key)
176 valid = CacheInvalidation.test_and_set_valid(key)
178 if inv is not None:
177 if not valid:
179 region_invalidate(_get_feed_from_cache, None, key)
178 region_invalidate(_get_feed_from_cache, None, key)
180 CacheInvalidation.set_valid(inv.cache_key)
181 return _get_feed_from_cache(key)
179 return _get_feed_from_cache(key)
@@ -1,244 +1,242 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.controllers.summary
3 rhodecode.controllers.summary
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 Summary controller for Rhodecode
6 Summary controller for Rhodecode
7
7
8 :created_on: Apr 18, 2010
8 :created_on: Apr 18, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2010-2012 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 modify
13 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 3 of the License, or
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25
25
26 import traceback
26 import traceback
27 import calendar
27 import calendar
28 import logging
28 import logging
29 import urllib
29 import urllib
30 from time import mktime
30 from time import mktime
31 from datetime import timedelta, date
31 from datetime import timedelta, date
32 from urlparse import urlparse
32 from urlparse import urlparse
33
33
34 from pylons import tmpl_context as c, request, url, config
34 from pylons import tmpl_context as c, request, url, config
35 from pylons.i18n.translation import _
35 from pylons.i18n.translation import _
36 from webob.exc import HTTPBadRequest
36 from webob.exc import HTTPBadRequest
37
37
38 from beaker.cache import cache_region, region_invalidate
38 from beaker.cache import cache_region, region_invalidate
39
39
40 from rhodecode.lib import helpers as h
40 from rhodecode.lib import helpers as h
41 from rhodecode.lib.compat import product
41 from rhodecode.lib.compat import product
42 from rhodecode.lib.vcs.exceptions import ChangesetError, EmptyRepositoryError, \
42 from rhodecode.lib.vcs.exceptions import ChangesetError, EmptyRepositoryError, \
43 NodeDoesNotExistError
43 NodeDoesNotExistError
44 from rhodecode.config.conf import ALL_READMES, ALL_EXTS, LANGUAGES_EXTENSIONS_MAP
44 from rhodecode.config.conf import ALL_READMES, ALL_EXTS, LANGUAGES_EXTENSIONS_MAP
45 from rhodecode.model.db import Statistics, CacheInvalidation
45 from rhodecode.model.db import Statistics, CacheInvalidation
46 from rhodecode.lib.utils import jsonify
46 from rhodecode.lib.utils import jsonify
47 from rhodecode.lib.utils2 import safe_unicode, safe_str
47 from rhodecode.lib.utils2 import safe_unicode, safe_str
48 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator,\
48 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator,\
49 NotAnonymous
49 NotAnonymous
50 from rhodecode.lib.base import BaseRepoController, render
50 from rhodecode.lib.base import BaseRepoController, render
51 from rhodecode.lib.vcs.backends.base import EmptyChangeset
51 from rhodecode.lib.vcs.backends.base import EmptyChangeset
52 from rhodecode.lib.markup_renderer import MarkupRenderer
52 from rhodecode.lib.markup_renderer import MarkupRenderer
53 from rhodecode.lib.celerylib import run_task
53 from rhodecode.lib.celerylib import run_task
54 from rhodecode.lib.celerylib.tasks import get_commits_stats
54 from rhodecode.lib.celerylib.tasks import get_commits_stats
55 from rhodecode.lib.helpers import RepoPage
55 from rhodecode.lib.helpers import RepoPage
56 from rhodecode.lib.compat import json, OrderedDict
56 from rhodecode.lib.compat import json, OrderedDict
57 from rhodecode.lib.vcs.nodes import FileNode
57 from rhodecode.lib.vcs.nodes import FileNode
58 from rhodecode.controllers.changelog import _load_changelog_summary
58 from rhodecode.controllers.changelog import _load_changelog_summary
59
59
60 log = logging.getLogger(__name__)
60 log = logging.getLogger(__name__)
61
61
62 README_FILES = [''.join([x[0][0], x[1][0]]) for x in
62 README_FILES = [''.join([x[0][0], x[1][0]]) for x in
63 sorted(list(product(ALL_READMES, ALL_EXTS)),
63 sorted(list(product(ALL_READMES, ALL_EXTS)),
64 key=lambda y:y[0][1] + y[1][1])]
64 key=lambda y:y[0][1] + y[1][1])]
65
65
66
66
67 class SummaryController(BaseRepoController):
67 class SummaryController(BaseRepoController):
68
68
69 def __before__(self):
69 def __before__(self):
70 super(SummaryController, self).__before__()
70 super(SummaryController, self).__before__()
71
71
72 def _get_download_links(self, repo):
72 def _get_download_links(self, repo):
73
73
74 download_l = []
74 download_l = []
75
75
76 branches_group = ([], _("Branches"))
76 branches_group = ([], _("Branches"))
77 tags_group = ([], _("Tags"))
77 tags_group = ([], _("Tags"))
78
78
79 for name, chs in c.rhodecode_repo.branches.items():
79 for name, chs in c.rhodecode_repo.branches.items():
80 #chs = chs.split(':')[-1]
80 #chs = chs.split(':')[-1]
81 branches_group[0].append((chs, name),)
81 branches_group[0].append((chs, name),)
82 download_l.append(branches_group)
82 download_l.append(branches_group)
83
83
84 for name, chs in c.rhodecode_repo.tags.items():
84 for name, chs in c.rhodecode_repo.tags.items():
85 #chs = chs.split(':')[-1]
85 #chs = chs.split(':')[-1]
86 tags_group[0].append((chs, name),)
86 tags_group[0].append((chs, name),)
87 download_l.append(tags_group)
87 download_l.append(tags_group)
88
88
89 return download_l
89 return download_l
90
90
91
92 def __get_readme_data(self, db_repo):
91 def __get_readme_data(self, db_repo):
93 repo_name = db_repo.repo_name
92 repo_name = db_repo.repo_name
94
93
95 @cache_region('long_term')
94 @cache_region('long_term')
96 def _get_readme_from_cache(key):
95 def _get_readme_from_cache(key):
97 readme_data = None
96 readme_data = None
98 readme_file = None
97 readme_file = None
99 log.debug('Looking for README file')
98 log.debug('Looking for README file')
100 try:
99 try:
101 # get's the landing revision! or tip if fails
100 # get's the landing revision! or tip if fails
102 cs = db_repo.get_landing_changeset()
101 cs = db_repo.get_landing_changeset()
103 if isinstance(cs, EmptyChangeset):
102 if isinstance(cs, EmptyChangeset):
104 raise EmptyRepositoryError()
103 raise EmptyRepositoryError()
105 renderer = MarkupRenderer()
104 renderer = MarkupRenderer()
106 for f in README_FILES:
105 for f in README_FILES:
107 try:
106 try:
108 readme = cs.get_node(f)
107 readme = cs.get_node(f)
109 if not isinstance(readme, FileNode):
108 if not isinstance(readme, FileNode):
110 continue
109 continue
111 readme_file = f
110 readme_file = f
112 log.debug('Found README file `%s` rendering...' %
111 log.debug('Found README file `%s` rendering...' %
113 readme_file)
112 readme_file)
114 readme_data = renderer.render(readme.content, f)
113 readme_data = renderer.render(readme.content, f)
115 break
114 break
116 except NodeDoesNotExistError:
115 except NodeDoesNotExistError:
117 continue
116 continue
118 except ChangesetError:
117 except ChangesetError:
119 log.error(traceback.format_exc())
118 log.error(traceback.format_exc())
120 pass
119 pass
121 except EmptyRepositoryError:
120 except EmptyRepositoryError:
122 pass
121 pass
123 except Exception:
122 except Exception:
124 log.error(traceback.format_exc())
123 log.error(traceback.format_exc())
125
124
126 return readme_data, readme_file
125 return readme_data, readme_file
127
126
128 key = repo_name + '_README'
127 key = repo_name + '_README'
129 inv = CacheInvalidation.invalidate(key)
128 valid = CacheInvalidation.test_and_set_valid(key)
130 if inv is not None:
129 if not valid:
131 region_invalidate(_get_readme_from_cache, None, key)
130 region_invalidate(_get_readme_from_cache, None, key)
132 CacheInvalidation.set_valid(inv.cache_key)
133 return _get_readme_from_cache(key)
131 return _get_readme_from_cache(key)
134
132
135 @LoginRequired()
133 @LoginRequired()
136 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
134 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
137 'repository.admin')
135 'repository.admin')
138 def index(self, repo_name):
136 def index(self, repo_name):
139 c.dbrepo = dbrepo = c.rhodecode_db_repo
137 c.dbrepo = dbrepo = c.rhodecode_db_repo
140 _load_changelog_summary()
138 _load_changelog_summary()
141 if self.rhodecode_user.username == 'default':
139 if self.rhodecode_user.username == 'default':
142 # for default(anonymous) user we don't need to pass credentials
140 # for default(anonymous) user we don't need to pass credentials
143 username = ''
141 username = ''
144 password = ''
142 password = ''
145 else:
143 else:
146 username = str(self.rhodecode_user.username)
144 username = str(self.rhodecode_user.username)
147 password = '@'
145 password = '@'
148
146
149 parsed_url = urlparse(url.current(qualified=True))
147 parsed_url = urlparse(url.current(qualified=True))
150
148
151 default_clone_uri = '{scheme}://{user}{pass}{netloc}{path}'
149 default_clone_uri = '{scheme}://{user}{pass}{netloc}{path}'
152
150
153 uri_tmpl = config.get('clone_uri', default_clone_uri)
151 uri_tmpl = config.get('clone_uri', default_clone_uri)
154 uri_tmpl = uri_tmpl.replace('{', '%(').replace('}', ')s')
152 uri_tmpl = uri_tmpl.replace('{', '%(').replace('}', ')s')
155 decoded_path = safe_unicode(urllib.unquote(parsed_url.path))
153 decoded_path = safe_unicode(urllib.unquote(parsed_url.path))
156 uri_dict = {
154 uri_dict = {
157 'user': urllib.quote(username),
155 'user': urllib.quote(username),
158 'pass': password,
156 'pass': password,
159 'scheme': parsed_url.scheme,
157 'scheme': parsed_url.scheme,
160 'netloc': parsed_url.netloc,
158 'netloc': parsed_url.netloc,
161 'path': urllib.quote(safe_str(decoded_path))
159 'path': urllib.quote(safe_str(decoded_path))
162 }
160 }
163
161
164 uri = (uri_tmpl % uri_dict)
162 uri = (uri_tmpl % uri_dict)
165 # generate another clone url by id
163 # generate another clone url by id
166 uri_dict.update(
164 uri_dict.update(
167 {'path': decoded_path.replace(repo_name, '_%s' % c.dbrepo.repo_id)}
165 {'path': decoded_path.replace(repo_name, '_%s' % c.dbrepo.repo_id)}
168 )
166 )
169 uri_id = uri_tmpl % uri_dict
167 uri_id = uri_tmpl % uri_dict
170
168
171 c.clone_repo_url = uri
169 c.clone_repo_url = uri
172 c.clone_repo_url_id = uri_id
170 c.clone_repo_url_id = uri_id
173
171
174 td = date.today() + timedelta(days=1)
172 td = date.today() + timedelta(days=1)
175 td_1m = td - timedelta(days=calendar.mdays[td.month])
173 td_1m = td - timedelta(days=calendar.mdays[td.month])
176 td_1y = td - timedelta(days=365)
174 td_1y = td - timedelta(days=365)
177
175
178 ts_min_m = mktime(td_1m.timetuple())
176 ts_min_m = mktime(td_1m.timetuple())
179 ts_min_y = mktime(td_1y.timetuple())
177 ts_min_y = mktime(td_1y.timetuple())
180 ts_max_y = mktime(td.timetuple())
178 ts_max_y = mktime(td.timetuple())
181
179
182 if dbrepo.enable_statistics:
180 if dbrepo.enable_statistics:
183 c.show_stats = True
181 c.show_stats = True
184 c.no_data_msg = _('No data loaded yet')
182 c.no_data_msg = _('No data loaded yet')
185 recurse_limit = 500 # don't recurse more than 500 times when parsing
183 recurse_limit = 500 # don't recurse more than 500 times when parsing
186 run_task(get_commits_stats, c.dbrepo.repo_name, ts_min_y,
184 run_task(get_commits_stats, c.dbrepo.repo_name, ts_min_y,
187 ts_max_y, recurse_limit)
185 ts_max_y, recurse_limit)
188 else:
186 else:
189 c.show_stats = False
187 c.show_stats = False
190 c.no_data_msg = _('Statistics are disabled for this repository')
188 c.no_data_msg = _('Statistics are disabled for this repository')
191 c.ts_min = ts_min_m
189 c.ts_min = ts_min_m
192 c.ts_max = ts_max_y
190 c.ts_max = ts_max_y
193
191
194 stats = self.sa.query(Statistics)\
192 stats = self.sa.query(Statistics)\
195 .filter(Statistics.repository == dbrepo)\
193 .filter(Statistics.repository == dbrepo)\
196 .scalar()
194 .scalar()
197
195
198 c.stats_percentage = 0
196 c.stats_percentage = 0
199
197
200 if stats and stats.languages:
198 if stats and stats.languages:
201 c.no_data = False is dbrepo.enable_statistics
199 c.no_data = False is dbrepo.enable_statistics
202 lang_stats_d = json.loads(stats.languages)
200 lang_stats_d = json.loads(stats.languages)
203 c.commit_data = stats.commit_activity
201 c.commit_data = stats.commit_activity
204 c.overview_data = stats.commit_activity_combined
202 c.overview_data = stats.commit_activity_combined
205
203
206 lang_stats = ((x, {"count": y,
204 lang_stats = ((x, {"count": y,
207 "desc": LANGUAGES_EXTENSIONS_MAP.get(x)})
205 "desc": LANGUAGES_EXTENSIONS_MAP.get(x)})
208 for x, y in lang_stats_d.items())
206 for x, y in lang_stats_d.items())
209
207
210 c.trending_languages = json.dumps(
208 c.trending_languages = json.dumps(
211 sorted(lang_stats, reverse=True, key=lambda k: k[1])[:10]
209 sorted(lang_stats, reverse=True, key=lambda k: k[1])[:10]
212 )
210 )
213 last_rev = stats.stat_on_revision + 1
211 last_rev = stats.stat_on_revision + 1
214 c.repo_last_rev = c.rhodecode_repo.count()\
212 c.repo_last_rev = c.rhodecode_repo.count()\
215 if c.rhodecode_repo.revisions else 0
213 if c.rhodecode_repo.revisions else 0
216 if last_rev == 0 or c.repo_last_rev == 0:
214 if last_rev == 0 or c.repo_last_rev == 0:
217 pass
215 pass
218 else:
216 else:
219 c.stats_percentage = '%.2f' % ((float((last_rev)) /
217 c.stats_percentage = '%.2f' % ((float((last_rev)) /
220 c.repo_last_rev) * 100)
218 c.repo_last_rev) * 100)
221 else:
219 else:
222 c.commit_data = json.dumps({})
220 c.commit_data = json.dumps({})
223 c.overview_data = json.dumps([[ts_min_y, 0], [ts_max_y, 10]])
221 c.overview_data = json.dumps([[ts_min_y, 0], [ts_max_y, 10]])
224 c.trending_languages = json.dumps({})
222 c.trending_languages = json.dumps({})
225 c.no_data = True
223 c.no_data = True
226
224
227 c.enable_downloads = dbrepo.enable_downloads
225 c.enable_downloads = dbrepo.enable_downloads
228 if c.enable_downloads:
226 if c.enable_downloads:
229 c.download_options = self._get_download_links(c.rhodecode_repo)
227 c.download_options = self._get_download_links(c.rhodecode_repo)
230
228
231 c.readme_data, c.readme_file = \
229 c.readme_data, c.readme_file = \
232 self.__get_readme_data(c.rhodecode_db_repo)
230 self.__get_readme_data(c.rhodecode_db_repo)
233 return render('summary/summary.html')
231 return render('summary/summary.html')
234
232
235 @LoginRequired()
233 @LoginRequired()
236 @NotAnonymous()
234 @NotAnonymous()
237 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
235 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
238 'repository.admin')
236 'repository.admin')
239 @jsonify
237 @jsonify
240 def repo_size(self, repo_name):
238 def repo_size(self, repo_name):
241 if request.is_xhr:
239 if request.is_xhr:
242 return c.rhodecode_db_repo._repo_size()
240 return c.rhodecode_db_repo._repo_size()
243 else:
241 else:
244 raise HTTPBadRequest()
242 raise HTTPBadRequest()
@@ -1,799 +1,799 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.lib.utils
3 rhodecode.lib.utils
4 ~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~
5
5
6 Utilities library for RhodeCode
6 Utilities library for RhodeCode
7
7
8 :created_on: Apr 18, 2010
8 :created_on: Apr 18, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2010-2012 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 modify
13 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 3 of the License, or
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25
25
26 import os
26 import os
27 import re
27 import re
28 import logging
28 import logging
29 import datetime
29 import datetime
30 import traceback
30 import traceback
31 import paste
31 import paste
32 import beaker
32 import beaker
33 import tarfile
33 import tarfile
34 import shutil
34 import shutil
35 import decorator
35 import decorator
36 import warnings
36 import warnings
37 from os.path import abspath
37 from os.path import abspath
38 from os.path import dirname as dn, join as jn
38 from os.path import dirname as dn, join as jn
39
39
40 from paste.script.command import Command, BadCommand
40 from paste.script.command import Command, BadCommand
41
41
42 from mercurial import ui, config
42 from mercurial import ui, config
43
43
44 from webhelpers.text import collapse, remove_formatting, strip_tags
44 from webhelpers.text import collapse, remove_formatting, strip_tags
45
45
46 from rhodecode.lib.vcs import get_backend
46 from rhodecode.lib.vcs import get_backend
47 from rhodecode.lib.vcs.backends.base import BaseChangeset
47 from rhodecode.lib.vcs.backends.base import BaseChangeset
48 from rhodecode.lib.vcs.utils.lazy import LazyProperty
48 from rhodecode.lib.vcs.utils.lazy import LazyProperty
49 from rhodecode.lib.vcs.utils.helpers import get_scm
49 from rhodecode.lib.vcs.utils.helpers import get_scm
50 from rhodecode.lib.vcs.exceptions import VCSError
50 from rhodecode.lib.vcs.exceptions import VCSError
51
51
52 from rhodecode.lib.caching_query import FromCache
52 from rhodecode.lib.caching_query import FromCache
53
53
54 from rhodecode.model import meta
54 from rhodecode.model import meta
55 from rhodecode.model.db import Repository, User, RhodeCodeUi, \
55 from rhodecode.model.db import Repository, User, RhodeCodeUi, \
56 UserLog, RepoGroup, RhodeCodeSetting, CacheInvalidation, UserGroup
56 UserLog, RepoGroup, RhodeCodeSetting, CacheInvalidation, UserGroup
57 from rhodecode.model.meta import Session
57 from rhodecode.model.meta import Session
58 from rhodecode.model.repos_group import ReposGroupModel
58 from rhodecode.model.repos_group import ReposGroupModel
59 from rhodecode.lib.utils2 import safe_str, safe_unicode
59 from rhodecode.lib.utils2 import safe_str, safe_unicode
60 from rhodecode.lib.vcs.utils.fakemod import create_module
60 from rhodecode.lib.vcs.utils.fakemod import create_module
61 from rhodecode.model.users_group import UserGroupModel
61 from rhodecode.model.users_group import UserGroupModel
62
62
63 log = logging.getLogger(__name__)
63 log = logging.getLogger(__name__)
64
64
65 REMOVED_REPO_PAT = re.compile(r'rm__\d{8}_\d{6}_\d{6}__.*')
65 REMOVED_REPO_PAT = re.compile(r'rm__\d{8}_\d{6}_\d{6}__.*')
66
66
67
67
68 def recursive_replace(str_, replace=' '):
68 def recursive_replace(str_, replace=' '):
69 """
69 """
70 Recursive replace of given sign to just one instance
70 Recursive replace of given sign to just one instance
71
71
72 :param str_: given string
72 :param str_: given string
73 :param replace: char to find and replace multiple instances
73 :param replace: char to find and replace multiple instances
74
74
75 Examples::
75 Examples::
76 >>> recursive_replace("Mighty---Mighty-Bo--sstones",'-')
76 >>> recursive_replace("Mighty---Mighty-Bo--sstones",'-')
77 'Mighty-Mighty-Bo-sstones'
77 'Mighty-Mighty-Bo-sstones'
78 """
78 """
79
79
80 if str_.find(replace * 2) == -1:
80 if str_.find(replace * 2) == -1:
81 return str_
81 return str_
82 else:
82 else:
83 str_ = str_.replace(replace * 2, replace)
83 str_ = str_.replace(replace * 2, replace)
84 return recursive_replace(str_, replace)
84 return recursive_replace(str_, replace)
85
85
86
86
87 def repo_name_slug(value):
87 def repo_name_slug(value):
88 """
88 """
89 Return slug of name of repository
89 Return slug of name of repository
90 This function is called on each creation/modification
90 This function is called on each creation/modification
91 of repository to prevent bad names in repo
91 of repository to prevent bad names in repo
92 """
92 """
93
93
94 slug = remove_formatting(value)
94 slug = remove_formatting(value)
95 slug = strip_tags(slug)
95 slug = strip_tags(slug)
96
96
97 for c in """`?=[]\;'"<>,/~!@#$%^&*()+{}|: """:
97 for c in """`?=[]\;'"<>,/~!@#$%^&*()+{}|: """:
98 slug = slug.replace(c, '-')
98 slug = slug.replace(c, '-')
99 slug = recursive_replace(slug, '-')
99 slug = recursive_replace(slug, '-')
100 slug = collapse(slug, '-')
100 slug = collapse(slug, '-')
101 return slug
101 return slug
102
102
103
103
104 #==============================================================================
104 #==============================================================================
105 # PERM DECORATOR HELPERS FOR EXTRACTING NAMES FOR PERM CHECKS
105 # PERM DECORATOR HELPERS FOR EXTRACTING NAMES FOR PERM CHECKS
106 #==============================================================================
106 #==============================================================================
107 def get_repo_slug(request):
107 def get_repo_slug(request):
108 _repo = request.environ['pylons.routes_dict'].get('repo_name')
108 _repo = request.environ['pylons.routes_dict'].get('repo_name')
109 if _repo:
109 if _repo:
110 _repo = _repo.rstrip('/')
110 _repo = _repo.rstrip('/')
111 return _repo
111 return _repo
112
112
113
113
114 def get_repos_group_slug(request):
114 def get_repos_group_slug(request):
115 _group = request.environ['pylons.routes_dict'].get('group_name')
115 _group = request.environ['pylons.routes_dict'].get('group_name')
116 if _group:
116 if _group:
117 _group = _group.rstrip('/')
117 _group = _group.rstrip('/')
118 return _group
118 return _group
119
119
120
120
121 def get_user_group_slug(request):
121 def get_user_group_slug(request):
122 _group = request.environ['pylons.routes_dict'].get('id')
122 _group = request.environ['pylons.routes_dict'].get('id')
123 try:
123 try:
124 _group = UserGroup.get(_group)
124 _group = UserGroup.get(_group)
125 if _group:
125 if _group:
126 _group = _group.users_group_name
126 _group = _group.users_group_name
127 except Exception:
127 except Exception:
128 log.debug(traceback.format_exc())
128 log.debug(traceback.format_exc())
129 #catch all failures here
129 #catch all failures here
130 pass
130 pass
131
131
132 return _group
132 return _group
133
133
134
134
135 def action_logger(user, action, repo, ipaddr='', sa=None, commit=False):
135 def action_logger(user, action, repo, ipaddr='', sa=None, commit=False):
136 """
136 """
137 Action logger for various actions made by users
137 Action logger for various actions made by users
138
138
139 :param user: user that made this action, can be a unique username string or
139 :param user: user that made this action, can be a unique username string or
140 object containing user_id attribute
140 object containing user_id attribute
141 :param action: action to log, should be on of predefined unique actions for
141 :param action: action to log, should be on of predefined unique actions for
142 easy translations
142 easy translations
143 :param repo: string name of repository or object containing repo_id,
143 :param repo: string name of repository or object containing repo_id,
144 that action was made on
144 that action was made on
145 :param ipaddr: optional ip address from what the action was made
145 :param ipaddr: optional ip address from what the action was made
146 :param sa: optional sqlalchemy session
146 :param sa: optional sqlalchemy session
147
147
148 """
148 """
149
149
150 if not sa:
150 if not sa:
151 sa = meta.Session()
151 sa = meta.Session()
152
152
153 try:
153 try:
154 if hasattr(user, 'user_id'):
154 if hasattr(user, 'user_id'):
155 user_obj = User.get(user.user_id)
155 user_obj = User.get(user.user_id)
156 elif isinstance(user, basestring):
156 elif isinstance(user, basestring):
157 user_obj = User.get_by_username(user)
157 user_obj = User.get_by_username(user)
158 else:
158 else:
159 raise Exception('You have to provide a user object or a username')
159 raise Exception('You have to provide a user object or a username')
160
160
161 if hasattr(repo, 'repo_id'):
161 if hasattr(repo, 'repo_id'):
162 repo_obj = Repository.get(repo.repo_id)
162 repo_obj = Repository.get(repo.repo_id)
163 repo_name = repo_obj.repo_name
163 repo_name = repo_obj.repo_name
164 elif isinstance(repo, basestring):
164 elif isinstance(repo, basestring):
165 repo_name = repo.lstrip('/')
165 repo_name = repo.lstrip('/')
166 repo_obj = Repository.get_by_repo_name(repo_name)
166 repo_obj = Repository.get_by_repo_name(repo_name)
167 else:
167 else:
168 repo_obj = None
168 repo_obj = None
169 repo_name = ''
169 repo_name = ''
170
170
171 user_log = UserLog()
171 user_log = UserLog()
172 user_log.user_id = user_obj.user_id
172 user_log.user_id = user_obj.user_id
173 user_log.username = user_obj.username
173 user_log.username = user_obj.username
174 user_log.action = safe_unicode(action)
174 user_log.action = safe_unicode(action)
175
175
176 user_log.repository = repo_obj
176 user_log.repository = repo_obj
177 user_log.repository_name = repo_name
177 user_log.repository_name = repo_name
178
178
179 user_log.action_date = datetime.datetime.now()
179 user_log.action_date = datetime.datetime.now()
180 user_log.user_ip = ipaddr
180 user_log.user_ip = ipaddr
181 sa.add(user_log)
181 sa.add(user_log)
182
182
183 log.info('Logging action:%s on %s by user:%s ip:%s' %
183 log.info('Logging action:%s on %s by user:%s ip:%s' %
184 (action, safe_unicode(repo), user_obj, ipaddr))
184 (action, safe_unicode(repo), user_obj, ipaddr))
185 if commit:
185 if commit:
186 sa.commit()
186 sa.commit()
187 except Exception:
187 except Exception:
188 log.error(traceback.format_exc())
188 log.error(traceback.format_exc())
189 raise
189 raise
190
190
191
191
192 def get_filesystem_repos(path, recursive=False, skip_removed_repos=True):
192 def get_filesystem_repos(path, recursive=False, skip_removed_repos=True):
193 """
193 """
194 Scans given path for repos and return (name,(type,path)) tuple
194 Scans given path for repos and return (name,(type,path)) tuple
195
195
196 :param path: path to scan for repositories
196 :param path: path to scan for repositories
197 :param recursive: recursive search and return names with subdirs in front
197 :param recursive: recursive search and return names with subdirs in front
198 """
198 """
199
199
200 # remove ending slash for better results
200 # remove ending slash for better results
201 path = path.rstrip(os.sep)
201 path = path.rstrip(os.sep)
202 log.debug('now scanning in %s location recursive:%s...' % (path, recursive))
202 log.debug('now scanning in %s location recursive:%s...' % (path, recursive))
203
203
204 def _get_repos(p):
204 def _get_repos(p):
205 if not os.access(p, os.W_OK):
205 if not os.access(p, os.W_OK):
206 log.warn('ignoring repo path without write access: %s', p)
206 log.warn('ignoring repo path without write access: %s', p)
207 return
207 return
208 for dirpath in os.listdir(p):
208 for dirpath in os.listdir(p):
209 if os.path.isfile(os.path.join(p, dirpath)):
209 if os.path.isfile(os.path.join(p, dirpath)):
210 continue
210 continue
211 cur_path = os.path.join(p, dirpath)
211 cur_path = os.path.join(p, dirpath)
212
212
213 # skip removed repos
213 # skip removed repos
214 if skip_removed_repos and REMOVED_REPO_PAT.match(dirpath):
214 if skip_removed_repos and REMOVED_REPO_PAT.match(dirpath):
215 continue
215 continue
216
216
217 #skip .<somethin> dirs
217 #skip .<somethin> dirs
218 if dirpath.startswith('.'):
218 if dirpath.startswith('.'):
219 continue
219 continue
220
220
221 try:
221 try:
222 scm_info = get_scm(cur_path)
222 scm_info = get_scm(cur_path)
223 yield scm_info[1].split(path, 1)[-1].lstrip(os.sep), scm_info
223 yield scm_info[1].split(path, 1)[-1].lstrip(os.sep), scm_info
224 except VCSError:
224 except VCSError:
225 if not recursive:
225 if not recursive:
226 continue
226 continue
227 #check if this dir containts other repos for recursive scan
227 #check if this dir containts other repos for recursive scan
228 rec_path = os.path.join(p, dirpath)
228 rec_path = os.path.join(p, dirpath)
229 if os.path.isdir(rec_path):
229 if os.path.isdir(rec_path):
230 for inner_scm in _get_repos(rec_path):
230 for inner_scm in _get_repos(rec_path):
231 yield inner_scm
231 yield inner_scm
232
232
233 return _get_repos(path)
233 return _get_repos(path)
234
234
235
235
236 def is_valid_repo(repo_name, base_path, scm=None):
236 def is_valid_repo(repo_name, base_path, scm=None):
237 """
237 """
238 Returns True if given path is a valid repository False otherwise.
238 Returns True if given path is a valid repository False otherwise.
239 If scm param is given also compare if given scm is the same as expected
239 If scm param is given also compare if given scm is the same as expected
240 from scm parameter
240 from scm parameter
241
241
242 :param repo_name:
242 :param repo_name:
243 :param base_path:
243 :param base_path:
244 :param scm:
244 :param scm:
245
245
246 :return True: if given path is a valid repository
246 :return True: if given path is a valid repository
247 """
247 """
248 full_path = os.path.join(safe_str(base_path), safe_str(repo_name))
248 full_path = os.path.join(safe_str(base_path), safe_str(repo_name))
249
249
250 try:
250 try:
251 scm_ = get_scm(full_path)
251 scm_ = get_scm(full_path)
252 if scm:
252 if scm:
253 return scm_[0] == scm
253 return scm_[0] == scm
254 return True
254 return True
255 except VCSError:
255 except VCSError:
256 return False
256 return False
257
257
258
258
259 def is_valid_repos_group(repos_group_name, base_path, skip_path_check=False):
259 def is_valid_repos_group(repos_group_name, base_path, skip_path_check=False):
260 """
260 """
261 Returns True if given path is a repository group False otherwise
261 Returns True if given path is a repository group False otherwise
262
262
263 :param repo_name:
263 :param repo_name:
264 :param base_path:
264 :param base_path:
265 """
265 """
266 full_path = os.path.join(safe_str(base_path), safe_str(repos_group_name))
266 full_path = os.path.join(safe_str(base_path), safe_str(repos_group_name))
267
267
268 # check if it's not a repo
268 # check if it's not a repo
269 if is_valid_repo(repos_group_name, base_path):
269 if is_valid_repo(repos_group_name, base_path):
270 return False
270 return False
271
271
272 try:
272 try:
273 # we need to check bare git repos at higher level
273 # we need to check bare git repos at higher level
274 # since we might match branches/hooks/info/objects or possible
274 # since we might match branches/hooks/info/objects or possible
275 # other things inside bare git repo
275 # other things inside bare git repo
276 get_scm(os.path.dirname(full_path))
276 get_scm(os.path.dirname(full_path))
277 return False
277 return False
278 except VCSError:
278 except VCSError:
279 pass
279 pass
280
280
281 # check if it's a valid path
281 # check if it's a valid path
282 if skip_path_check or os.path.isdir(full_path):
282 if skip_path_check or os.path.isdir(full_path):
283 return True
283 return True
284
284
285 return False
285 return False
286
286
287
287
288 def ask_ok(prompt, retries=4, complaint='Yes or no please!'):
288 def ask_ok(prompt, retries=4, complaint='Yes or no please!'):
289 while True:
289 while True:
290 ok = raw_input(prompt)
290 ok = raw_input(prompt)
291 if ok in ('y', 'ye', 'yes'):
291 if ok in ('y', 'ye', 'yes'):
292 return True
292 return True
293 if ok in ('n', 'no', 'nop', 'nope'):
293 if ok in ('n', 'no', 'nop', 'nope'):
294 return False
294 return False
295 retries = retries - 1
295 retries = retries - 1
296 if retries < 0:
296 if retries < 0:
297 raise IOError
297 raise IOError
298 print complaint
298 print complaint
299
299
300 #propagated from mercurial documentation
300 #propagated from mercurial documentation
301 ui_sections = ['alias', 'auth',
301 ui_sections = ['alias', 'auth',
302 'decode/encode', 'defaults',
302 'decode/encode', 'defaults',
303 'diff', 'email',
303 'diff', 'email',
304 'extensions', 'format',
304 'extensions', 'format',
305 'merge-patterns', 'merge-tools',
305 'merge-patterns', 'merge-tools',
306 'hooks', 'http_proxy',
306 'hooks', 'http_proxy',
307 'smtp', 'patch',
307 'smtp', 'patch',
308 'paths', 'profiling',
308 'paths', 'profiling',
309 'server', 'trusted',
309 'server', 'trusted',
310 'ui', 'web', ]
310 'ui', 'web', ]
311
311
312
312
313 def make_ui(read_from='file', path=None, checkpaths=True, clear_session=True):
313 def make_ui(read_from='file', path=None, checkpaths=True, clear_session=True):
314 """
314 """
315 A function that will read python rc files or database
315 A function that will read python rc files or database
316 and make an mercurial ui object from read options
316 and make an mercurial ui object from read options
317
317
318 :param path: path to mercurial config file
318 :param path: path to mercurial config file
319 :param checkpaths: check the path
319 :param checkpaths: check the path
320 :param read_from: read from 'file' or 'db'
320 :param read_from: read from 'file' or 'db'
321 """
321 """
322
322
323 baseui = ui.ui()
323 baseui = ui.ui()
324
324
325 # clean the baseui object
325 # clean the baseui object
326 baseui._ocfg = config.config()
326 baseui._ocfg = config.config()
327 baseui._ucfg = config.config()
327 baseui._ucfg = config.config()
328 baseui._tcfg = config.config()
328 baseui._tcfg = config.config()
329
329
330 if read_from == 'file':
330 if read_from == 'file':
331 if not os.path.isfile(path):
331 if not os.path.isfile(path):
332 log.debug('hgrc file is not present at %s, skipping...' % path)
332 log.debug('hgrc file is not present at %s, skipping...' % path)
333 return False
333 return False
334 log.debug('reading hgrc from %s' % path)
334 log.debug('reading hgrc from %s' % path)
335 cfg = config.config()
335 cfg = config.config()
336 cfg.read(path)
336 cfg.read(path)
337 for section in ui_sections:
337 for section in ui_sections:
338 for k, v in cfg.items(section):
338 for k, v in cfg.items(section):
339 log.debug('settings ui from file: [%s] %s=%s' % (section, k, v))
339 log.debug('settings ui from file: [%s] %s=%s' % (section, k, v))
340 baseui.setconfig(safe_str(section), safe_str(k), safe_str(v))
340 baseui.setconfig(safe_str(section), safe_str(k), safe_str(v))
341
341
342 elif read_from == 'db':
342 elif read_from == 'db':
343 sa = meta.Session()
343 sa = meta.Session()
344 ret = sa.query(RhodeCodeUi)\
344 ret = sa.query(RhodeCodeUi)\
345 .options(FromCache("sql_cache_short", "get_hg_ui_settings"))\
345 .options(FromCache("sql_cache_short", "get_hg_ui_settings"))\
346 .all()
346 .all()
347
347
348 hg_ui = ret
348 hg_ui = ret
349 for ui_ in hg_ui:
349 for ui_ in hg_ui:
350 if ui_.ui_active:
350 if ui_.ui_active:
351 log.debug('settings ui from db: [%s] %s=%s', ui_.ui_section,
351 log.debug('settings ui from db: [%s] %s=%s', ui_.ui_section,
352 ui_.ui_key, ui_.ui_value)
352 ui_.ui_key, ui_.ui_value)
353 baseui.setconfig(safe_str(ui_.ui_section), safe_str(ui_.ui_key),
353 baseui.setconfig(safe_str(ui_.ui_section), safe_str(ui_.ui_key),
354 safe_str(ui_.ui_value))
354 safe_str(ui_.ui_value))
355 if ui_.ui_key == 'push_ssl':
355 if ui_.ui_key == 'push_ssl':
356 # force set push_ssl requirement to False, rhodecode
356 # force set push_ssl requirement to False, rhodecode
357 # handles that
357 # handles that
358 baseui.setconfig(safe_str(ui_.ui_section), safe_str(ui_.ui_key),
358 baseui.setconfig(safe_str(ui_.ui_section), safe_str(ui_.ui_key),
359 False)
359 False)
360 if clear_session:
360 if clear_session:
361 meta.Session.remove()
361 meta.Session.remove()
362 return baseui
362 return baseui
363
363
364
364
365 def set_rhodecode_config(config):
365 def set_rhodecode_config(config):
366 """
366 """
367 Updates pylons config with new settings from database
367 Updates pylons config with new settings from database
368
368
369 :param config:
369 :param config:
370 """
370 """
371 hgsettings = RhodeCodeSetting.get_app_settings()
371 hgsettings = RhodeCodeSetting.get_app_settings()
372
372
373 for k, v in hgsettings.items():
373 for k, v in hgsettings.items():
374 config[k] = v
374 config[k] = v
375
375
376
376
377 def map_groups(path):
377 def map_groups(path):
378 """
378 """
379 Given a full path to a repository, create all nested groups that this
379 Given a full path to a repository, create all nested groups that this
380 repo is inside. This function creates parent-child relationships between
380 repo is inside. This function creates parent-child relationships between
381 groups and creates default perms for all new groups.
381 groups and creates default perms for all new groups.
382
382
383 :param paths: full path to repository
383 :param paths: full path to repository
384 """
384 """
385 sa = meta.Session()
385 sa = meta.Session()
386 groups = path.split(Repository.url_sep())
386 groups = path.split(Repository.url_sep())
387 parent = None
387 parent = None
388 group = None
388 group = None
389
389
390 # last element is repo in nested groups structure
390 # last element is repo in nested groups structure
391 groups = groups[:-1]
391 groups = groups[:-1]
392 rgm = ReposGroupModel(sa)
392 rgm = ReposGroupModel(sa)
393 owner = User.get_first_admin()
393 owner = User.get_first_admin()
394 for lvl, group_name in enumerate(groups):
394 for lvl, group_name in enumerate(groups):
395 group_name = '/'.join(groups[:lvl] + [group_name])
395 group_name = '/'.join(groups[:lvl] + [group_name])
396 group = RepoGroup.get_by_group_name(group_name)
396 group = RepoGroup.get_by_group_name(group_name)
397 desc = '%s group' % group_name
397 desc = '%s group' % group_name
398
398
399 # skip folders that are now removed repos
399 # skip folders that are now removed repos
400 if REMOVED_REPO_PAT.match(group_name):
400 if REMOVED_REPO_PAT.match(group_name):
401 break
401 break
402
402
403 if group is None:
403 if group is None:
404 log.debug('creating group level: %s group_name: %s'
404 log.debug('creating group level: %s group_name: %s'
405 % (lvl, group_name))
405 % (lvl, group_name))
406 group = RepoGroup(group_name, parent)
406 group = RepoGroup(group_name, parent)
407 group.group_description = desc
407 group.group_description = desc
408 group.user = owner
408 group.user = owner
409 sa.add(group)
409 sa.add(group)
410 perm_obj = rgm._create_default_perms(group)
410 perm_obj = rgm._create_default_perms(group)
411 sa.add(perm_obj)
411 sa.add(perm_obj)
412 sa.flush()
412 sa.flush()
413
413
414 parent = group
414 parent = group
415 return group
415 return group
416
416
417
417
418 def repo2db_mapper(initial_repo_list, remove_obsolete=False,
418 def repo2db_mapper(initial_repo_list, remove_obsolete=False,
419 install_git_hook=False):
419 install_git_hook=False):
420 """
420 """
421 maps all repos given in initial_repo_list, non existing repositories
421 maps all repos given in initial_repo_list, non existing repositories
422 are created, if remove_obsolete is True it also check for db entries
422 are created, if remove_obsolete is True it also check for db entries
423 that are not in initial_repo_list and removes them.
423 that are not in initial_repo_list and removes them.
424
424
425 :param initial_repo_list: list of repositories found by scanning methods
425 :param initial_repo_list: list of repositories found by scanning methods
426 :param remove_obsolete: check for obsolete entries in database
426 :param remove_obsolete: check for obsolete entries in database
427 :param install_git_hook: if this is True, also check and install githook
427 :param install_git_hook: if this is True, also check and install githook
428 for a repo if missing
428 for a repo if missing
429 """
429 """
430 from rhodecode.model.repo import RepoModel
430 from rhodecode.model.repo import RepoModel
431 from rhodecode.model.scm import ScmModel
431 from rhodecode.model.scm import ScmModel
432 sa = meta.Session()
432 sa = meta.Session()
433 rm = RepoModel()
433 rm = RepoModel()
434 user = User.get_first_admin()
434 user = User.get_first_admin()
435 added = []
435 added = []
436
436
437 ##creation defaults
437 ##creation defaults
438 defs = RhodeCodeSetting.get_default_repo_settings(strip_prefix=True)
438 defs = RhodeCodeSetting.get_default_repo_settings(strip_prefix=True)
439 enable_statistics = defs.get('repo_enable_statistics')
439 enable_statistics = defs.get('repo_enable_statistics')
440 enable_locking = defs.get('repo_enable_locking')
440 enable_locking = defs.get('repo_enable_locking')
441 enable_downloads = defs.get('repo_enable_downloads')
441 enable_downloads = defs.get('repo_enable_downloads')
442 private = defs.get('repo_private')
442 private = defs.get('repo_private')
443
443
444 for name, repo in initial_repo_list.items():
444 for name, repo in initial_repo_list.items():
445 group = map_groups(name)
445 group = map_groups(name)
446 db_repo = rm.get_by_repo_name(name)
446 db_repo = rm.get_by_repo_name(name)
447 # found repo that is on filesystem not in RhodeCode database
447 # found repo that is on filesystem not in RhodeCode database
448 if not db_repo:
448 if not db_repo:
449 log.info('repository %s not found, creating now' % name)
449 log.info('repository %s not found, creating now' % name)
450 added.append(name)
450 added.append(name)
451 desc = (repo.description
451 desc = (repo.description
452 if repo.description != 'unknown'
452 if repo.description != 'unknown'
453 else '%s repository' % name)
453 else '%s repository' % name)
454
454
455 new_repo = rm.create_repo(
455 new_repo = rm.create_repo(
456 repo_name=name,
456 repo_name=name,
457 repo_type=repo.alias,
457 repo_type=repo.alias,
458 description=desc,
458 description=desc,
459 repos_group=getattr(group, 'group_id', None),
459 repos_group=getattr(group, 'group_id', None),
460 owner=user,
460 owner=user,
461 just_db=True,
461 just_db=True,
462 enable_locking=enable_locking,
462 enable_locking=enable_locking,
463 enable_downloads=enable_downloads,
463 enable_downloads=enable_downloads,
464 enable_statistics=enable_statistics,
464 enable_statistics=enable_statistics,
465 private=private
465 private=private
466 )
466 )
467 # we added that repo just now, and make sure it has githook
467 # we added that repo just now, and make sure it has githook
468 # installed
468 # installed
469 if new_repo.repo_type == 'git':
469 if new_repo.repo_type == 'git':
470 ScmModel().install_git_hook(new_repo.scm_instance)
470 ScmModel().install_git_hook(new_repo.scm_instance)
471 new_repo.update_changeset_cache()
471 new_repo.update_changeset_cache()
472 elif install_git_hook:
472 elif install_git_hook:
473 if db_repo.repo_type == 'git':
473 if db_repo.repo_type == 'git':
474 ScmModel().install_git_hook(db_repo.scm_instance)
474 ScmModel().install_git_hook(db_repo.scm_instance)
475 # during starting install all cache keys for all repositories in the
475 # during starting install all cache keys for all repositories in the
476 # system, this will register all repos and multiple instances
476 # system, this will register all repos and multiple instances
477 cache_key = CacheInvalidation._get_cache_key(name)
477 cache_key = CacheInvalidation._get_cache_key(name)
478 log.debug("Creating invalidation cache key for %s: %s", name, cache_key)
478 log.debug("Creating invalidation cache key for %s: %s", name, cache_key)
479 CacheInvalidation.invalidate(name)
479 CacheInvalidation.test_and_set_valid(name)
480
480
481 sa.commit()
481 sa.commit()
482 removed = []
482 removed = []
483 if remove_obsolete:
483 if remove_obsolete:
484 # remove from database those repositories that are not in the filesystem
484 # remove from database those repositories that are not in the filesystem
485 for repo in sa.query(Repository).all():
485 for repo in sa.query(Repository).all():
486 if repo.repo_name not in initial_repo_list.keys():
486 if repo.repo_name not in initial_repo_list.keys():
487 log.debug("Removing non-existing repository found in db `%s`" %
487 log.debug("Removing non-existing repository found in db `%s`" %
488 repo.repo_name)
488 repo.repo_name)
489 try:
489 try:
490 removed.append(repo.repo_name)
490 removed.append(repo.repo_name)
491 RepoModel(sa).delete(repo, forks='detach', fs_remove=False)
491 RepoModel(sa).delete(repo, forks='detach', fs_remove=False)
492 sa.commit()
492 sa.commit()
493 except Exception:
493 except Exception:
494 #don't hold further removals on error
494 #don't hold further removals on error
495 log.error(traceback.format_exc())
495 log.error(traceback.format_exc())
496 sa.rollback()
496 sa.rollback()
497 return added, removed
497 return added, removed
498
498
499
499
500 # set cache regions for beaker so celery can utilise it
500 # set cache regions for beaker so celery can utilise it
501 def add_cache(settings):
501 def add_cache(settings):
502 cache_settings = {'regions': None}
502 cache_settings = {'regions': None}
503 for key in settings.keys():
503 for key in settings.keys():
504 for prefix in ['beaker.cache.', 'cache.']:
504 for prefix in ['beaker.cache.', 'cache.']:
505 if key.startswith(prefix):
505 if key.startswith(prefix):
506 name = key.split(prefix)[1].strip()
506 name = key.split(prefix)[1].strip()
507 cache_settings[name] = settings[key].strip()
507 cache_settings[name] = settings[key].strip()
508 if cache_settings['regions']:
508 if cache_settings['regions']:
509 for region in cache_settings['regions'].split(','):
509 for region in cache_settings['regions'].split(','):
510 region = region.strip()
510 region = region.strip()
511 region_settings = {}
511 region_settings = {}
512 for key, value in cache_settings.items():
512 for key, value in cache_settings.items():
513 if key.startswith(region):
513 if key.startswith(region):
514 region_settings[key.split('.')[1]] = value
514 region_settings[key.split('.')[1]] = value
515 region_settings['expire'] = int(region_settings.get('expire',
515 region_settings['expire'] = int(region_settings.get('expire',
516 60))
516 60))
517 region_settings.setdefault('lock_dir',
517 region_settings.setdefault('lock_dir',
518 cache_settings.get('lock_dir'))
518 cache_settings.get('lock_dir'))
519 region_settings.setdefault('data_dir',
519 region_settings.setdefault('data_dir',
520 cache_settings.get('data_dir'))
520 cache_settings.get('data_dir'))
521
521
522 if 'type' not in region_settings:
522 if 'type' not in region_settings:
523 region_settings['type'] = cache_settings.get('type',
523 region_settings['type'] = cache_settings.get('type',
524 'memory')
524 'memory')
525 beaker.cache.cache_regions[region] = region_settings
525 beaker.cache.cache_regions[region] = region_settings
526
526
527
527
528 def load_rcextensions(root_path):
528 def load_rcextensions(root_path):
529 import rhodecode
529 import rhodecode
530 from rhodecode.config import conf
530 from rhodecode.config import conf
531
531
532 path = os.path.join(root_path, 'rcextensions', '__init__.py')
532 path = os.path.join(root_path, 'rcextensions', '__init__.py')
533 if os.path.isfile(path):
533 if os.path.isfile(path):
534 rcext = create_module('rc', path)
534 rcext = create_module('rc', path)
535 EXT = rhodecode.EXTENSIONS = rcext
535 EXT = rhodecode.EXTENSIONS = rcext
536 log.debug('Found rcextensions now loading %s...' % rcext)
536 log.debug('Found rcextensions now loading %s...' % rcext)
537
537
538 # Additional mappings that are not present in the pygments lexers
538 # Additional mappings that are not present in the pygments lexers
539 conf.LANGUAGES_EXTENSIONS_MAP.update(getattr(EXT, 'EXTRA_MAPPINGS', {}))
539 conf.LANGUAGES_EXTENSIONS_MAP.update(getattr(EXT, 'EXTRA_MAPPINGS', {}))
540
540
541 #OVERRIDE OUR EXTENSIONS FROM RC-EXTENSIONS (if present)
541 #OVERRIDE OUR EXTENSIONS FROM RC-EXTENSIONS (if present)
542
542
543 if getattr(EXT, 'INDEX_EXTENSIONS', []) != []:
543 if getattr(EXT, 'INDEX_EXTENSIONS', []) != []:
544 log.debug('settings custom INDEX_EXTENSIONS')
544 log.debug('settings custom INDEX_EXTENSIONS')
545 conf.INDEX_EXTENSIONS = getattr(EXT, 'INDEX_EXTENSIONS', [])
545 conf.INDEX_EXTENSIONS = getattr(EXT, 'INDEX_EXTENSIONS', [])
546
546
547 #ADDITIONAL MAPPINGS
547 #ADDITIONAL MAPPINGS
548 log.debug('adding extra into INDEX_EXTENSIONS')
548 log.debug('adding extra into INDEX_EXTENSIONS')
549 conf.INDEX_EXTENSIONS.extend(getattr(EXT, 'EXTRA_INDEX_EXTENSIONS', []))
549 conf.INDEX_EXTENSIONS.extend(getattr(EXT, 'EXTRA_INDEX_EXTENSIONS', []))
550
550
551 # auto check if the module is not missing any data, set to default if is
551 # auto check if the module is not missing any data, set to default if is
552 # this will help autoupdate new feature of rcext module
552 # this will help autoupdate new feature of rcext module
553 from rhodecode.config import rcextensions
553 from rhodecode.config import rcextensions
554 for k in dir(rcextensions):
554 for k in dir(rcextensions):
555 if not k.startswith('_') and not hasattr(EXT, k):
555 if not k.startswith('_') and not hasattr(EXT, k):
556 setattr(EXT, k, getattr(rcextensions, k))
556 setattr(EXT, k, getattr(rcextensions, k))
557
557
558
558
559 def get_custom_lexer(extension):
559 def get_custom_lexer(extension):
560 """
560 """
561 returns a custom lexer if it's defined in rcextensions module, or None
561 returns a custom lexer if it's defined in rcextensions module, or None
562 if there's no custom lexer defined
562 if there's no custom lexer defined
563 """
563 """
564 import rhodecode
564 import rhodecode
565 from pygments import lexers
565 from pygments import lexers
566 #check if we didn't define this extension as other lexer
566 #check if we didn't define this extension as other lexer
567 if rhodecode.EXTENSIONS and extension in rhodecode.EXTENSIONS.EXTRA_LEXERS:
567 if rhodecode.EXTENSIONS and extension in rhodecode.EXTENSIONS.EXTRA_LEXERS:
568 _lexer_name = rhodecode.EXTENSIONS.EXTRA_LEXERS[extension]
568 _lexer_name = rhodecode.EXTENSIONS.EXTRA_LEXERS[extension]
569 return lexers.get_lexer_by_name(_lexer_name)
569 return lexers.get_lexer_by_name(_lexer_name)
570
570
571
571
572 #==============================================================================
572 #==============================================================================
573 # TEST FUNCTIONS AND CREATORS
573 # TEST FUNCTIONS AND CREATORS
574 #==============================================================================
574 #==============================================================================
575 def create_test_index(repo_location, config, full_index):
575 def create_test_index(repo_location, config, full_index):
576 """
576 """
577 Makes default test index
577 Makes default test index
578
578
579 :param config: test config
579 :param config: test config
580 :param full_index:
580 :param full_index:
581 """
581 """
582
582
583 from rhodecode.lib.indexers.daemon import WhooshIndexingDaemon
583 from rhodecode.lib.indexers.daemon import WhooshIndexingDaemon
584 from rhodecode.lib.pidlock import DaemonLock, LockHeld
584 from rhodecode.lib.pidlock import DaemonLock, LockHeld
585
585
586 repo_location = repo_location
586 repo_location = repo_location
587
587
588 index_location = os.path.join(config['app_conf']['index_dir'])
588 index_location = os.path.join(config['app_conf']['index_dir'])
589 if not os.path.exists(index_location):
589 if not os.path.exists(index_location):
590 os.makedirs(index_location)
590 os.makedirs(index_location)
591
591
592 try:
592 try:
593 l = DaemonLock(file_=jn(dn(index_location), 'make_index.lock'))
593 l = DaemonLock(file_=jn(dn(index_location), 'make_index.lock'))
594 WhooshIndexingDaemon(index_location=index_location,
594 WhooshIndexingDaemon(index_location=index_location,
595 repo_location=repo_location)\
595 repo_location=repo_location)\
596 .run(full_index=full_index)
596 .run(full_index=full_index)
597 l.release()
597 l.release()
598 except LockHeld:
598 except LockHeld:
599 pass
599 pass
600
600
601
601
602 def create_test_env(repos_test_path, config):
602 def create_test_env(repos_test_path, config):
603 """
603 """
604 Makes a fresh database and
604 Makes a fresh database and
605 install test repository into tmp dir
605 install test repository into tmp dir
606 """
606 """
607 from rhodecode.lib.db_manage import DbManage
607 from rhodecode.lib.db_manage import DbManage
608 from rhodecode.tests import HG_REPO, GIT_REPO, TESTS_TMP_PATH
608 from rhodecode.tests import HG_REPO, GIT_REPO, TESTS_TMP_PATH
609
609
610 # PART ONE create db
610 # PART ONE create db
611 dbconf = config['sqlalchemy.db1.url']
611 dbconf = config['sqlalchemy.db1.url']
612 log.debug('making test db %s' % dbconf)
612 log.debug('making test db %s' % dbconf)
613
613
614 # create test dir if it doesn't exist
614 # create test dir if it doesn't exist
615 if not os.path.isdir(repos_test_path):
615 if not os.path.isdir(repos_test_path):
616 log.debug('Creating testdir %s' % repos_test_path)
616 log.debug('Creating testdir %s' % repos_test_path)
617 os.makedirs(repos_test_path)
617 os.makedirs(repos_test_path)
618
618
619 dbmanage = DbManage(log_sql=True, dbconf=dbconf, root=config['here'],
619 dbmanage = DbManage(log_sql=True, dbconf=dbconf, root=config['here'],
620 tests=True)
620 tests=True)
621 dbmanage.create_tables(override=True)
621 dbmanage.create_tables(override=True)
622 dbmanage.create_settings(dbmanage.config_prompt(repos_test_path))
622 dbmanage.create_settings(dbmanage.config_prompt(repos_test_path))
623 dbmanage.create_default_user()
623 dbmanage.create_default_user()
624 dbmanage.admin_prompt()
624 dbmanage.admin_prompt()
625 dbmanage.create_permissions()
625 dbmanage.create_permissions()
626 dbmanage.populate_default_permissions()
626 dbmanage.populate_default_permissions()
627 Session().commit()
627 Session().commit()
628 # PART TWO make test repo
628 # PART TWO make test repo
629 log.debug('making test vcs repositories')
629 log.debug('making test vcs repositories')
630
630
631 idx_path = config['app_conf']['index_dir']
631 idx_path = config['app_conf']['index_dir']
632 data_path = config['app_conf']['cache_dir']
632 data_path = config['app_conf']['cache_dir']
633
633
634 #clean index and data
634 #clean index and data
635 if idx_path and os.path.exists(idx_path):
635 if idx_path and os.path.exists(idx_path):
636 log.debug('remove %s' % idx_path)
636 log.debug('remove %s' % idx_path)
637 shutil.rmtree(idx_path)
637 shutil.rmtree(idx_path)
638
638
639 if data_path and os.path.exists(data_path):
639 if data_path and os.path.exists(data_path):
640 log.debug('remove %s' % data_path)
640 log.debug('remove %s' % data_path)
641 shutil.rmtree(data_path)
641 shutil.rmtree(data_path)
642
642
643 #CREATE DEFAULT TEST REPOS
643 #CREATE DEFAULT TEST REPOS
644 cur_dir = dn(dn(abspath(__file__)))
644 cur_dir = dn(dn(abspath(__file__)))
645 tar = tarfile.open(jn(cur_dir, 'tests', "vcs_test_hg.tar.gz"))
645 tar = tarfile.open(jn(cur_dir, 'tests', "vcs_test_hg.tar.gz"))
646 tar.extractall(jn(TESTS_TMP_PATH, HG_REPO))
646 tar.extractall(jn(TESTS_TMP_PATH, HG_REPO))
647 tar.close()
647 tar.close()
648
648
649 cur_dir = dn(dn(abspath(__file__)))
649 cur_dir = dn(dn(abspath(__file__)))
650 tar = tarfile.open(jn(cur_dir, 'tests', "vcs_test_git.tar.gz"))
650 tar = tarfile.open(jn(cur_dir, 'tests', "vcs_test_git.tar.gz"))
651 tar.extractall(jn(TESTS_TMP_PATH, GIT_REPO))
651 tar.extractall(jn(TESTS_TMP_PATH, GIT_REPO))
652 tar.close()
652 tar.close()
653
653
654 #LOAD VCS test stuff
654 #LOAD VCS test stuff
655 from rhodecode.tests.vcs import setup_package
655 from rhodecode.tests.vcs import setup_package
656 setup_package()
656 setup_package()
657
657
658
658
659 #==============================================================================
659 #==============================================================================
660 # PASTER COMMANDS
660 # PASTER COMMANDS
661 #==============================================================================
661 #==============================================================================
662 class BasePasterCommand(Command):
662 class BasePasterCommand(Command):
663 """
663 """
664 Abstract Base Class for paster commands.
664 Abstract Base Class for paster commands.
665
665
666 The celery commands are somewhat aggressive about loading
666 The celery commands are somewhat aggressive about loading
667 celery.conf, and since our module sets the `CELERY_LOADER`
667 celery.conf, and since our module sets the `CELERY_LOADER`
668 environment variable to our loader, we have to bootstrap a bit and
668 environment variable to our loader, we have to bootstrap a bit and
669 make sure we've had a chance to load the pylons config off of the
669 make sure we've had a chance to load the pylons config off of the
670 command line, otherwise everything fails.
670 command line, otherwise everything fails.
671 """
671 """
672 min_args = 1
672 min_args = 1
673 min_args_error = "Please provide a paster config file as an argument."
673 min_args_error = "Please provide a paster config file as an argument."
674 takes_config_file = 1
674 takes_config_file = 1
675 requires_config_file = True
675 requires_config_file = True
676
676
677 def notify_msg(self, msg, log=False):
677 def notify_msg(self, msg, log=False):
678 """Make a notification to user, additionally if logger is passed
678 """Make a notification to user, additionally if logger is passed
679 it logs this action using given logger
679 it logs this action using given logger
680
680
681 :param msg: message that will be printed to user
681 :param msg: message that will be printed to user
682 :param log: logging instance, to use to additionally log this message
682 :param log: logging instance, to use to additionally log this message
683
683
684 """
684 """
685 if log and isinstance(log, logging):
685 if log and isinstance(log, logging):
686 log(msg)
686 log(msg)
687
687
688 def run(self, args):
688 def run(self, args):
689 """
689 """
690 Overrides Command.run
690 Overrides Command.run
691
691
692 Checks for a config file argument and loads it.
692 Checks for a config file argument and loads it.
693 """
693 """
694 if len(args) < self.min_args:
694 if len(args) < self.min_args:
695 raise BadCommand(
695 raise BadCommand(
696 self.min_args_error % {'min_args': self.min_args,
696 self.min_args_error % {'min_args': self.min_args,
697 'actual_args': len(args)})
697 'actual_args': len(args)})
698
698
699 # Decrement because we're going to lob off the first argument.
699 # Decrement because we're going to lob off the first argument.
700 # @@ This is hacky
700 # @@ This is hacky
701 self.min_args -= 1
701 self.min_args -= 1
702 self.bootstrap_config(args[0])
702 self.bootstrap_config(args[0])
703 self.update_parser()
703 self.update_parser()
704 return super(BasePasterCommand, self).run(args[1:])
704 return super(BasePasterCommand, self).run(args[1:])
705
705
706 def update_parser(self):
706 def update_parser(self):
707 """
707 """
708 Abstract method. Allows for the class's parser to be updated
708 Abstract method. Allows for the class's parser to be updated
709 before the superclass's `run` method is called. Necessary to
709 before the superclass's `run` method is called. Necessary to
710 allow options/arguments to be passed through to the underlying
710 allow options/arguments to be passed through to the underlying
711 celery command.
711 celery command.
712 """
712 """
713 raise NotImplementedError("Abstract Method.")
713 raise NotImplementedError("Abstract Method.")
714
714
715 def bootstrap_config(self, conf):
715 def bootstrap_config(self, conf):
716 """
716 """
717 Loads the pylons configuration.
717 Loads the pylons configuration.
718 """
718 """
719 from pylons import config as pylonsconfig
719 from pylons import config as pylonsconfig
720
720
721 self.path_to_ini_file = os.path.realpath(conf)
721 self.path_to_ini_file = os.path.realpath(conf)
722 conf = paste.deploy.appconfig('config:' + self.path_to_ini_file)
722 conf = paste.deploy.appconfig('config:' + self.path_to_ini_file)
723 pylonsconfig.init_app(conf.global_conf, conf.local_conf)
723 pylonsconfig.init_app(conf.global_conf, conf.local_conf)
724
724
725 def _init_session(self):
725 def _init_session(self):
726 """
726 """
727 Inits SqlAlchemy Session
727 Inits SqlAlchemy Session
728 """
728 """
729 logging.config.fileConfig(self.path_to_ini_file)
729 logging.config.fileConfig(self.path_to_ini_file)
730 from pylons import config
730 from pylons import config
731 from rhodecode.model import init_model
731 from rhodecode.model import init_model
732 from rhodecode.lib.utils2 import engine_from_config
732 from rhodecode.lib.utils2 import engine_from_config
733
733
734 #get to remove repos !!
734 #get to remove repos !!
735 add_cache(config)
735 add_cache(config)
736 engine = engine_from_config(config, 'sqlalchemy.db1.')
736 engine = engine_from_config(config, 'sqlalchemy.db1.')
737 init_model(engine)
737 init_model(engine)
738
738
739
739
740 def check_git_version():
740 def check_git_version():
741 """
741 """
742 Checks what version of git is installed in system, and issues a warning
742 Checks what version of git is installed in system, and issues a warning
743 if it's too old for RhodeCode to properly work.
743 if it's too old for RhodeCode to properly work.
744 """
744 """
745 from rhodecode import BACKENDS
745 from rhodecode import BACKENDS
746 from rhodecode.lib.vcs.backends.git.repository import GitRepository
746 from rhodecode.lib.vcs.backends.git.repository import GitRepository
747 from distutils.version import StrictVersion
747 from distutils.version import StrictVersion
748
748
749 stdout, stderr = GitRepository._run_git_command('--version', _bare=True,
749 stdout, stderr = GitRepository._run_git_command('--version', _bare=True,
750 _safe=True)
750 _safe=True)
751
751
752 ver = (stdout.split(' ')[-1] or '').strip() or '0.0.0'
752 ver = (stdout.split(' ')[-1] or '').strip() or '0.0.0'
753 if len(ver.split('.')) > 3:
753 if len(ver.split('.')) > 3:
754 #StrictVersion needs to be only 3 element type
754 #StrictVersion needs to be only 3 element type
755 ver = '.'.join(ver.split('.')[:3])
755 ver = '.'.join(ver.split('.')[:3])
756 try:
756 try:
757 _ver = StrictVersion(ver)
757 _ver = StrictVersion(ver)
758 except Exception:
758 except Exception:
759 _ver = StrictVersion('0.0.0')
759 _ver = StrictVersion('0.0.0')
760 stderr = traceback.format_exc()
760 stderr = traceback.format_exc()
761
761
762 req_ver = '1.7.4'
762 req_ver = '1.7.4'
763 to_old_git = False
763 to_old_git = False
764 if _ver < StrictVersion(req_ver):
764 if _ver < StrictVersion(req_ver):
765 to_old_git = True
765 to_old_git = True
766
766
767 if 'git' in BACKENDS:
767 if 'git' in BACKENDS:
768 log.debug('GIT version detected: %s' % stdout)
768 log.debug('GIT version detected: %s' % stdout)
769 if stderr:
769 if stderr:
770 log.warning('Unable to detect git version, org error was: %r' % stderr)
770 log.warning('Unable to detect git version, org error was: %r' % stderr)
771 elif to_old_git:
771 elif to_old_git:
772 log.warning('RhodeCode detected git version %s, which is too old '
772 log.warning('RhodeCode detected git version %s, which is too old '
773 'for the system to function properly. Make sure '
773 'for the system to function properly. Make sure '
774 'its version is at least %s' % (ver, req_ver))
774 'its version is at least %s' % (ver, req_ver))
775 return _ver
775 return _ver
776
776
777
777
778 @decorator.decorator
778 @decorator.decorator
779 def jsonify(func, *args, **kwargs):
779 def jsonify(func, *args, **kwargs):
780 """Action decorator that formats output for JSON
780 """Action decorator that formats output for JSON
781
781
782 Given a function that will return content, this decorator will turn
782 Given a function that will return content, this decorator will turn
783 the result into JSON, with a content-type of 'application/json' and
783 the result into JSON, with a content-type of 'application/json' and
784 output it.
784 output it.
785
785
786 """
786 """
787 from pylons.decorators.util import get_pylons
787 from pylons.decorators.util import get_pylons
788 from rhodecode.lib.compat import json
788 from rhodecode.lib.compat import json
789 pylons = get_pylons(args)
789 pylons = get_pylons(args)
790 pylons.response.headers['Content-Type'] = 'application/json; charset=utf-8'
790 pylons.response.headers['Content-Type'] = 'application/json; charset=utf-8'
791 data = func(*args, **kwargs)
791 data = func(*args, **kwargs)
792 if isinstance(data, (list, tuple)):
792 if isinstance(data, (list, tuple)):
793 msg = "JSON responses with Array envelopes are susceptible to " \
793 msg = "JSON responses with Array envelopes are susceptible to " \
794 "cross-site data leak attacks, see " \
794 "cross-site data leak attacks, see " \
795 "http://wiki.pylonshq.com/display/pylonsfaq/Warnings"
795 "http://wiki.pylonshq.com/display/pylonsfaq/Warnings"
796 warnings.warn(msg, Warning, 2)
796 warnings.warn(msg, Warning, 2)
797 log.warning(msg)
797 log.warning(msg)
798 log.debug("Returning JSON wrapped action output")
798 log.debug("Returning JSON wrapped action output")
799 return json.dumps(data, encoding='utf-8')
799 return json.dumps(data, encoding='utf-8')
@@ -1,2176 +1,2130 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.model.db
3 rhodecode.model.db
4 ~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~
5
5
6 Database Models for RhodeCode
6 Database Models for RhodeCode
7
7
8 :created_on: Apr 08, 2010
8 :created_on: Apr 08, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2010-2012 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 modify
13 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 3 of the License, or
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25
25
26 import os
26 import os
27 import logging
27 import logging
28 import datetime
28 import datetime
29 import traceback
29 import traceback
30 import hashlib
30 import hashlib
31 import time
31 import time
32 from collections import defaultdict
32 from collections import defaultdict
33
33
34 from sqlalchemy import *
34 from sqlalchemy import *
35 from sqlalchemy.ext.hybrid import hybrid_property
35 from sqlalchemy.ext.hybrid import hybrid_property
36 from sqlalchemy.orm import relationship, joinedload, class_mapper, validates
36 from sqlalchemy.orm import relationship, joinedload, class_mapper, validates
37 from sqlalchemy.exc import DatabaseError
37 from sqlalchemy.exc import DatabaseError
38 from beaker.cache import cache_region, region_invalidate
38 from beaker.cache import cache_region, region_invalidate
39 from webob.exc import HTTPNotFound
39 from webob.exc import HTTPNotFound
40
40
41 from pylons.i18n.translation import lazy_ugettext as _
41 from pylons.i18n.translation import lazy_ugettext as _
42
42
43 from rhodecode.lib.vcs import get_backend
43 from rhodecode.lib.vcs import get_backend
44 from rhodecode.lib.vcs.utils.helpers import get_scm
44 from rhodecode.lib.vcs.utils.helpers import get_scm
45 from rhodecode.lib.vcs.exceptions import VCSError
45 from rhodecode.lib.vcs.exceptions import VCSError
46 from rhodecode.lib.vcs.utils.lazy import LazyProperty
46 from rhodecode.lib.vcs.utils.lazy import LazyProperty
47 from rhodecode.lib.vcs.backends.base import EmptyChangeset
47 from rhodecode.lib.vcs.backends.base import EmptyChangeset
48
48
49 from rhodecode.lib.utils2 import str2bool, safe_str, get_changeset_safe, \
49 from rhodecode.lib.utils2 import str2bool, safe_str, get_changeset_safe, \
50 safe_unicode, remove_suffix, remove_prefix, time_to_datetime, _set_extras
50 safe_unicode, remove_suffix, remove_prefix, time_to_datetime, _set_extras
51 from rhodecode.lib.compat import json
51 from rhodecode.lib.compat import json
52 from rhodecode.lib.caching_query import FromCache
52 from rhodecode.lib.caching_query import FromCache
53
53
54 from rhodecode.model.meta import Base, Session
54 from rhodecode.model.meta import Base, Session
55
55
56 URL_SEP = '/'
56 URL_SEP = '/'
57 log = logging.getLogger(__name__)
57 log = logging.getLogger(__name__)
58
58
59 #==============================================================================
59 #==============================================================================
60 # BASE CLASSES
60 # BASE CLASSES
61 #==============================================================================
61 #==============================================================================
62
62
63 _hash_key = lambda k: hashlib.md5(safe_str(k)).hexdigest()
63 _hash_key = lambda k: hashlib.md5(safe_str(k)).hexdigest()
64
64
65
65
66 class BaseModel(object):
66 class BaseModel(object):
67 """
67 """
68 Base Model for all classess
68 Base Model for all classess
69 """
69 """
70
70
71 @classmethod
71 @classmethod
72 def _get_keys(cls):
72 def _get_keys(cls):
73 """return column names for this model """
73 """return column names for this model """
74 return class_mapper(cls).c.keys()
74 return class_mapper(cls).c.keys()
75
75
76 def get_dict(self):
76 def get_dict(self):
77 """
77 """
78 return dict with keys and values corresponding
78 return dict with keys and values corresponding
79 to this model data """
79 to this model data """
80
80
81 d = {}
81 d = {}
82 for k in self._get_keys():
82 for k in self._get_keys():
83 d[k] = getattr(self, k)
83 d[k] = getattr(self, k)
84
84
85 # also use __json__() if present to get additional fields
85 # also use __json__() if present to get additional fields
86 _json_attr = getattr(self, '__json__', None)
86 _json_attr = getattr(self, '__json__', None)
87 if _json_attr:
87 if _json_attr:
88 # update with attributes from __json__
88 # update with attributes from __json__
89 if callable(_json_attr):
89 if callable(_json_attr):
90 _json_attr = _json_attr()
90 _json_attr = _json_attr()
91 for k, val in _json_attr.iteritems():
91 for k, val in _json_attr.iteritems():
92 d[k] = val
92 d[k] = val
93 return d
93 return d
94
94
95 def get_appstruct(self):
95 def get_appstruct(self):
96 """return list with keys and values tupples corresponding
96 """return list with keys and values tupples corresponding
97 to this model data """
97 to this model data """
98
98
99 l = []
99 l = []
100 for k in self._get_keys():
100 for k in self._get_keys():
101 l.append((k, getattr(self, k),))
101 l.append((k, getattr(self, k),))
102 return l
102 return l
103
103
104 def populate_obj(self, populate_dict):
104 def populate_obj(self, populate_dict):
105 """populate model with data from given populate_dict"""
105 """populate model with data from given populate_dict"""
106
106
107 for k in self._get_keys():
107 for k in self._get_keys():
108 if k in populate_dict:
108 if k in populate_dict:
109 setattr(self, k, populate_dict[k])
109 setattr(self, k, populate_dict[k])
110
110
111 @classmethod
111 @classmethod
112 def query(cls):
112 def query(cls):
113 return Session().query(cls)
113 return Session().query(cls)
114
114
115 @classmethod
115 @classmethod
116 def get(cls, id_):
116 def get(cls, id_):
117 if id_:
117 if id_:
118 return cls.query().get(id_)
118 return cls.query().get(id_)
119
119
120 @classmethod
120 @classmethod
121 def get_or_404(cls, id_):
121 def get_or_404(cls, id_):
122 try:
122 try:
123 id_ = int(id_)
123 id_ = int(id_)
124 except (TypeError, ValueError):
124 except (TypeError, ValueError):
125 raise HTTPNotFound
125 raise HTTPNotFound
126
126
127 res = cls.query().get(id_)
127 res = cls.query().get(id_)
128 if not res:
128 if not res:
129 raise HTTPNotFound
129 raise HTTPNotFound
130 return res
130 return res
131
131
132 @classmethod
132 @classmethod
133 def getAll(cls):
133 def getAll(cls):
134 # deprecated and left for backward compatibility
134 # deprecated and left for backward compatibility
135 return cls.get_all()
135 return cls.get_all()
136
136
137 @classmethod
137 @classmethod
138 def get_all(cls):
138 def get_all(cls):
139 return cls.query().all()
139 return cls.query().all()
140
140
141 @classmethod
141 @classmethod
142 def delete(cls, id_):
142 def delete(cls, id_):
143 obj = cls.query().get(id_)
143 obj = cls.query().get(id_)
144 Session().delete(obj)
144 Session().delete(obj)
145
145
146 def __repr__(self):
146 def __repr__(self):
147 if hasattr(self, '__unicode__'):
147 if hasattr(self, '__unicode__'):
148 # python repr needs to return str
148 # python repr needs to return str
149 return safe_str(self.__unicode__())
149 return safe_str(self.__unicode__())
150 return '<DB:%s>' % (self.__class__.__name__)
150 return '<DB:%s>' % (self.__class__.__name__)
151
151
152
152
153 class RhodeCodeSetting(Base, BaseModel):
153 class RhodeCodeSetting(Base, BaseModel):
154 __tablename__ = 'rhodecode_settings'
154 __tablename__ = 'rhodecode_settings'
155 __table_args__ = (
155 __table_args__ = (
156 UniqueConstraint('app_settings_name'),
156 UniqueConstraint('app_settings_name'),
157 {'extend_existing': True, 'mysql_engine': 'InnoDB',
157 {'extend_existing': True, 'mysql_engine': 'InnoDB',
158 'mysql_charset': 'utf8'}
158 'mysql_charset': 'utf8'}
159 )
159 )
160 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
160 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
161 app_settings_name = Column("app_settings_name", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
161 app_settings_name = Column("app_settings_name", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
162 _app_settings_value = Column("app_settings_value", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
162 _app_settings_value = Column("app_settings_value", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
163
163
164 def __init__(self, k='', v=''):
164 def __init__(self, k='', v=''):
165 self.app_settings_name = k
165 self.app_settings_name = k
166 self.app_settings_value = v
166 self.app_settings_value = v
167
167
168 @validates('_app_settings_value')
168 @validates('_app_settings_value')
169 def validate_settings_value(self, key, val):
169 def validate_settings_value(self, key, val):
170 assert type(val) == unicode
170 assert type(val) == unicode
171 return val
171 return val
172
172
173 @hybrid_property
173 @hybrid_property
174 def app_settings_value(self):
174 def app_settings_value(self):
175 v = self._app_settings_value
175 v = self._app_settings_value
176 if self.app_settings_name in ["ldap_active",
176 if self.app_settings_name in ["ldap_active",
177 "default_repo_enable_statistics",
177 "default_repo_enable_statistics",
178 "default_repo_enable_locking",
178 "default_repo_enable_locking",
179 "default_repo_private",
179 "default_repo_private",
180 "default_repo_enable_downloads"]:
180 "default_repo_enable_downloads"]:
181 v = str2bool(v)
181 v = str2bool(v)
182 return v
182 return v
183
183
184 @app_settings_value.setter
184 @app_settings_value.setter
185 def app_settings_value(self, val):
185 def app_settings_value(self, val):
186 """
186 """
187 Setter that will always make sure we use unicode in app_settings_value
187 Setter that will always make sure we use unicode in app_settings_value
188
188
189 :param val:
189 :param val:
190 """
190 """
191 self._app_settings_value = safe_unicode(val)
191 self._app_settings_value = safe_unicode(val)
192
192
193 def __unicode__(self):
193 def __unicode__(self):
194 return u"<%s('%s:%s')>" % (
194 return u"<%s('%s:%s')>" % (
195 self.__class__.__name__,
195 self.__class__.__name__,
196 self.app_settings_name, self.app_settings_value
196 self.app_settings_name, self.app_settings_value
197 )
197 )
198
198
199 @classmethod
199 @classmethod
200 def get_by_name(cls, key):
200 def get_by_name(cls, key):
201 return cls.query()\
201 return cls.query()\
202 .filter(cls.app_settings_name == key).scalar()
202 .filter(cls.app_settings_name == key).scalar()
203
203
204 @classmethod
204 @classmethod
205 def get_by_name_or_create(cls, key):
205 def get_by_name_or_create(cls, key):
206 res = cls.get_by_name(key)
206 res = cls.get_by_name(key)
207 if not res:
207 if not res:
208 res = cls(key)
208 res = cls(key)
209 return res
209 return res
210
210
211 @classmethod
211 @classmethod
212 def get_app_settings(cls, cache=False):
212 def get_app_settings(cls, cache=False):
213
213
214 ret = cls.query()
214 ret = cls.query()
215
215
216 if cache:
216 if cache:
217 ret = ret.options(FromCache("sql_cache_short", "get_hg_settings"))
217 ret = ret.options(FromCache("sql_cache_short", "get_hg_settings"))
218
218
219 if not ret:
219 if not ret:
220 raise Exception('Could not get application settings !')
220 raise Exception('Could not get application settings !')
221 settings = {}
221 settings = {}
222 for each in ret:
222 for each in ret:
223 settings['rhodecode_' + each.app_settings_name] = \
223 settings['rhodecode_' + each.app_settings_name] = \
224 each.app_settings_value
224 each.app_settings_value
225
225
226 return settings
226 return settings
227
227
228 @classmethod
228 @classmethod
229 def get_ldap_settings(cls, cache=False):
229 def get_ldap_settings(cls, cache=False):
230 ret = cls.query()\
230 ret = cls.query()\
231 .filter(cls.app_settings_name.startswith('ldap_')).all()
231 .filter(cls.app_settings_name.startswith('ldap_')).all()
232 fd = {}
232 fd = {}
233 for row in ret:
233 for row in ret:
234 fd.update({row.app_settings_name: row.app_settings_value})
234 fd.update({row.app_settings_name: row.app_settings_value})
235
235
236 return fd
236 return fd
237
237
238 @classmethod
238 @classmethod
239 def get_default_repo_settings(cls, cache=False, strip_prefix=False):
239 def get_default_repo_settings(cls, cache=False, strip_prefix=False):
240 ret = cls.query()\
240 ret = cls.query()\
241 .filter(cls.app_settings_name.startswith('default_')).all()
241 .filter(cls.app_settings_name.startswith('default_')).all()
242 fd = {}
242 fd = {}
243 for row in ret:
243 for row in ret:
244 key = row.app_settings_name
244 key = row.app_settings_name
245 if strip_prefix:
245 if strip_prefix:
246 key = remove_prefix(key, prefix='default_')
246 key = remove_prefix(key, prefix='default_')
247 fd.update({key: row.app_settings_value})
247 fd.update({key: row.app_settings_value})
248
248
249 return fd
249 return fd
250
250
251
251
252 class RhodeCodeUi(Base, BaseModel):
252 class RhodeCodeUi(Base, BaseModel):
253 __tablename__ = 'rhodecode_ui'
253 __tablename__ = 'rhodecode_ui'
254 __table_args__ = (
254 __table_args__ = (
255 UniqueConstraint('ui_key'),
255 UniqueConstraint('ui_key'),
256 {'extend_existing': True, 'mysql_engine': 'InnoDB',
256 {'extend_existing': True, 'mysql_engine': 'InnoDB',
257 'mysql_charset': 'utf8'}
257 'mysql_charset': 'utf8'}
258 )
258 )
259
259
260 HOOK_UPDATE = 'changegroup.update'
260 HOOK_UPDATE = 'changegroup.update'
261 HOOK_REPO_SIZE = 'changegroup.repo_size'
261 HOOK_REPO_SIZE = 'changegroup.repo_size'
262 HOOK_PUSH = 'changegroup.push_logger'
262 HOOK_PUSH = 'changegroup.push_logger'
263 HOOK_PRE_PUSH = 'prechangegroup.pre_push'
263 HOOK_PRE_PUSH = 'prechangegroup.pre_push'
264 HOOK_PULL = 'outgoing.pull_logger'
264 HOOK_PULL = 'outgoing.pull_logger'
265 HOOK_PRE_PULL = 'preoutgoing.pre_pull'
265 HOOK_PRE_PULL = 'preoutgoing.pre_pull'
266
266
267 ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
267 ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
268 ui_section = Column("ui_section", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
268 ui_section = Column("ui_section", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
269 ui_key = Column("ui_key", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
269 ui_key = Column("ui_key", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
270 ui_value = Column("ui_value", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
270 ui_value = Column("ui_value", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
271 ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
271 ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
272
272
273 @classmethod
273 @classmethod
274 def get_by_key(cls, key):
274 def get_by_key(cls, key):
275 return cls.query().filter(cls.ui_key == key).scalar()
275 return cls.query().filter(cls.ui_key == key).scalar()
276
276
277 @classmethod
277 @classmethod
278 def get_builtin_hooks(cls):
278 def get_builtin_hooks(cls):
279 q = cls.query()
279 q = cls.query()
280 q = q.filter(cls.ui_key.in_([cls.HOOK_UPDATE, cls.HOOK_REPO_SIZE,
280 q = q.filter(cls.ui_key.in_([cls.HOOK_UPDATE, cls.HOOK_REPO_SIZE,
281 cls.HOOK_PUSH, cls.HOOK_PRE_PUSH,
281 cls.HOOK_PUSH, cls.HOOK_PRE_PUSH,
282 cls.HOOK_PULL, cls.HOOK_PRE_PULL]))
282 cls.HOOK_PULL, cls.HOOK_PRE_PULL]))
283 return q.all()
283 return q.all()
284
284
285 @classmethod
285 @classmethod
286 def get_custom_hooks(cls):
286 def get_custom_hooks(cls):
287 q = cls.query()
287 q = cls.query()
288 q = q.filter(~cls.ui_key.in_([cls.HOOK_UPDATE, cls.HOOK_REPO_SIZE,
288 q = q.filter(~cls.ui_key.in_([cls.HOOK_UPDATE, cls.HOOK_REPO_SIZE,
289 cls.HOOK_PUSH, cls.HOOK_PRE_PUSH,
289 cls.HOOK_PUSH, cls.HOOK_PRE_PUSH,
290 cls.HOOK_PULL, cls.HOOK_PRE_PULL]))
290 cls.HOOK_PULL, cls.HOOK_PRE_PULL]))
291 q = q.filter(cls.ui_section == 'hooks')
291 q = q.filter(cls.ui_section == 'hooks')
292 return q.all()
292 return q.all()
293
293
294 @classmethod
294 @classmethod
295 def get_repos_location(cls):
295 def get_repos_location(cls):
296 return cls.get_by_key('/').ui_value
296 return cls.get_by_key('/').ui_value
297
297
298 @classmethod
298 @classmethod
299 def create_or_update_hook(cls, key, val):
299 def create_or_update_hook(cls, key, val):
300 new_ui = cls.get_by_key(key) or cls()
300 new_ui = cls.get_by_key(key) or cls()
301 new_ui.ui_section = 'hooks'
301 new_ui.ui_section = 'hooks'
302 new_ui.ui_active = True
302 new_ui.ui_active = True
303 new_ui.ui_key = key
303 new_ui.ui_key = key
304 new_ui.ui_value = val
304 new_ui.ui_value = val
305
305
306 Session().add(new_ui)
306 Session().add(new_ui)
307
307
308 def __repr__(self):
308 def __repr__(self):
309 return '<DB:%s[%s:%s]>' % (self.__class__.__name__, self.ui_key,
309 return '<DB:%s[%s:%s]>' % (self.__class__.__name__, self.ui_key,
310 self.ui_value)
310 self.ui_value)
311
311
312
312
313 class User(Base, BaseModel):
313 class User(Base, BaseModel):
314 __tablename__ = 'users'
314 __tablename__ = 'users'
315 __table_args__ = (
315 __table_args__ = (
316 UniqueConstraint('username'), UniqueConstraint('email'),
316 UniqueConstraint('username'), UniqueConstraint('email'),
317 Index('u_username_idx', 'username'),
317 Index('u_username_idx', 'username'),
318 Index('u_email_idx', 'email'),
318 Index('u_email_idx', 'email'),
319 {'extend_existing': True, 'mysql_engine': 'InnoDB',
319 {'extend_existing': True, 'mysql_engine': 'InnoDB',
320 'mysql_charset': 'utf8'}
320 'mysql_charset': 'utf8'}
321 )
321 )
322 DEFAULT_USER = 'default'
322 DEFAULT_USER = 'default'
323
323
324 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
324 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
325 username = Column("username", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
325 username = Column("username", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
326 password = Column("password", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
326 password = Column("password", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
327 active = Column("active", Boolean(), nullable=True, unique=None, default=True)
327 active = Column("active", Boolean(), nullable=True, unique=None, default=True)
328 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
328 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
329 name = Column("firstname", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
329 name = Column("firstname", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
330 lastname = Column("lastname", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
330 lastname = Column("lastname", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
331 _email = Column("email", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
331 _email = Column("email", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
332 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
332 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
333 ldap_dn = Column("ldap_dn", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
333 ldap_dn = Column("ldap_dn", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
334 api_key = Column("api_key", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
334 api_key = Column("api_key", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
335 inherit_default_permissions = Column("inherit_default_permissions", Boolean(), nullable=False, unique=None, default=True)
335 inherit_default_permissions = Column("inherit_default_permissions", Boolean(), nullable=False, unique=None, default=True)
336
336
337 user_log = relationship('UserLog')
337 user_log = relationship('UserLog')
338 user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
338 user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
339
339
340 repositories = relationship('Repository')
340 repositories = relationship('Repository')
341 user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
341 user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
342 followings = relationship('UserFollowing', primaryjoin='UserFollowing.user_id==User.user_id', cascade='all')
342 followings = relationship('UserFollowing', primaryjoin='UserFollowing.user_id==User.user_id', cascade='all')
343
343
344 repo_to_perm = relationship('UserRepoToPerm', primaryjoin='UserRepoToPerm.user_id==User.user_id', cascade='all')
344 repo_to_perm = relationship('UserRepoToPerm', primaryjoin='UserRepoToPerm.user_id==User.user_id', cascade='all')
345 repo_group_to_perm = relationship('UserRepoGroupToPerm', primaryjoin='UserRepoGroupToPerm.user_id==User.user_id', cascade='all')
345 repo_group_to_perm = relationship('UserRepoGroupToPerm', primaryjoin='UserRepoGroupToPerm.user_id==User.user_id', cascade='all')
346
346
347 group_member = relationship('UserGroupMember', cascade='all')
347 group_member = relationship('UserGroupMember', cascade='all')
348
348
349 notifications = relationship('UserNotification', cascade='all')
349 notifications = relationship('UserNotification', cascade='all')
350 # notifications assigned to this user
350 # notifications assigned to this user
351 user_created_notifications = relationship('Notification', cascade='all')
351 user_created_notifications = relationship('Notification', cascade='all')
352 # comments created by this user
352 # comments created by this user
353 user_comments = relationship('ChangesetComment', cascade='all')
353 user_comments = relationship('ChangesetComment', cascade='all')
354 #extra emails for this user
354 #extra emails for this user
355 user_emails = relationship('UserEmailMap', cascade='all')
355 user_emails = relationship('UserEmailMap', cascade='all')
356
356
357 @hybrid_property
357 @hybrid_property
358 def email(self):
358 def email(self):
359 return self._email
359 return self._email
360
360
361 @email.setter
361 @email.setter
362 def email(self, val):
362 def email(self, val):
363 self._email = val.lower() if val else None
363 self._email = val.lower() if val else None
364
364
365 @property
365 @property
366 def firstname(self):
366 def firstname(self):
367 # alias for future
367 # alias for future
368 return self.name
368 return self.name
369
369
370 @property
370 @property
371 def emails(self):
371 def emails(self):
372 other = UserEmailMap.query().filter(UserEmailMap.user==self).all()
372 other = UserEmailMap.query().filter(UserEmailMap.user==self).all()
373 return [self.email] + [x.email for x in other]
373 return [self.email] + [x.email for x in other]
374
374
375 @property
375 @property
376 def ip_addresses(self):
376 def ip_addresses(self):
377 ret = UserIpMap.query().filter(UserIpMap.user == self).all()
377 ret = UserIpMap.query().filter(UserIpMap.user == self).all()
378 return [x.ip_addr for x in ret]
378 return [x.ip_addr for x in ret]
379
379
380 @property
380 @property
381 def username_and_name(self):
381 def username_and_name(self):
382 return '%s (%s %s)' % (self.username, self.firstname, self.lastname)
382 return '%s (%s %s)' % (self.username, self.firstname, self.lastname)
383
383
384 @property
384 @property
385 def full_name(self):
385 def full_name(self):
386 return '%s %s' % (self.firstname, self.lastname)
386 return '%s %s' % (self.firstname, self.lastname)
387
387
388 @property
388 @property
389 def full_name_or_username(self):
389 def full_name_or_username(self):
390 return ('%s %s' % (self.firstname, self.lastname)
390 return ('%s %s' % (self.firstname, self.lastname)
391 if (self.firstname and self.lastname) else self.username)
391 if (self.firstname and self.lastname) else self.username)
392
392
393 @property
393 @property
394 def full_contact(self):
394 def full_contact(self):
395 return '%s %s <%s>' % (self.firstname, self.lastname, self.email)
395 return '%s %s <%s>' % (self.firstname, self.lastname, self.email)
396
396
397 @property
397 @property
398 def short_contact(self):
398 def short_contact(self):
399 return '%s %s' % (self.firstname, self.lastname)
399 return '%s %s' % (self.firstname, self.lastname)
400
400
401 @property
401 @property
402 def is_admin(self):
402 def is_admin(self):
403 return self.admin
403 return self.admin
404
404
405 @property
405 @property
406 def AuthUser(self):
406 def AuthUser(self):
407 """
407 """
408 Returns instance of AuthUser for this user
408 Returns instance of AuthUser for this user
409 """
409 """
410 from rhodecode.lib.auth import AuthUser
410 from rhodecode.lib.auth import AuthUser
411 return AuthUser(user_id=self.user_id, api_key=self.api_key,
411 return AuthUser(user_id=self.user_id, api_key=self.api_key,
412 username=self.username)
412 username=self.username)
413
413
414 def __unicode__(self):
414 def __unicode__(self):
415 return u"<%s('id:%s:%s')>" % (self.__class__.__name__,
415 return u"<%s('id:%s:%s')>" % (self.__class__.__name__,
416 self.user_id, self.username)
416 self.user_id, self.username)
417
417
418 @classmethod
418 @classmethod
419 def get_by_username(cls, username, case_insensitive=False, cache=False):
419 def get_by_username(cls, username, case_insensitive=False, cache=False):
420 if case_insensitive:
420 if case_insensitive:
421 q = cls.query().filter(cls.username.ilike(username))
421 q = cls.query().filter(cls.username.ilike(username))
422 else:
422 else:
423 q = cls.query().filter(cls.username == username)
423 q = cls.query().filter(cls.username == username)
424
424
425 if cache:
425 if cache:
426 q = q.options(FromCache(
426 q = q.options(FromCache(
427 "sql_cache_short",
427 "sql_cache_short",
428 "get_user_%s" % _hash_key(username)
428 "get_user_%s" % _hash_key(username)
429 )
429 )
430 )
430 )
431 return q.scalar()
431 return q.scalar()
432
432
433 @classmethod
433 @classmethod
434 def get_by_api_key(cls, api_key, cache=False):
434 def get_by_api_key(cls, api_key, cache=False):
435 q = cls.query().filter(cls.api_key == api_key)
435 q = cls.query().filter(cls.api_key == api_key)
436
436
437 if cache:
437 if cache:
438 q = q.options(FromCache("sql_cache_short",
438 q = q.options(FromCache("sql_cache_short",
439 "get_api_key_%s" % api_key))
439 "get_api_key_%s" % api_key))
440 return q.scalar()
440 return q.scalar()
441
441
442 @classmethod
442 @classmethod
443 def get_by_email(cls, email, case_insensitive=False, cache=False):
443 def get_by_email(cls, email, case_insensitive=False, cache=False):
444 if case_insensitive:
444 if case_insensitive:
445 q = cls.query().filter(cls.email.ilike(email))
445 q = cls.query().filter(cls.email.ilike(email))
446 else:
446 else:
447 q = cls.query().filter(cls.email == email)
447 q = cls.query().filter(cls.email == email)
448
448
449 if cache:
449 if cache:
450 q = q.options(FromCache("sql_cache_short",
450 q = q.options(FromCache("sql_cache_short",
451 "get_email_key_%s" % email))
451 "get_email_key_%s" % email))
452
452
453 ret = q.scalar()
453 ret = q.scalar()
454 if ret is None:
454 if ret is None:
455 q = UserEmailMap.query()
455 q = UserEmailMap.query()
456 # try fetching in alternate email map
456 # try fetching in alternate email map
457 if case_insensitive:
457 if case_insensitive:
458 q = q.filter(UserEmailMap.email.ilike(email))
458 q = q.filter(UserEmailMap.email.ilike(email))
459 else:
459 else:
460 q = q.filter(UserEmailMap.email == email)
460 q = q.filter(UserEmailMap.email == email)
461 q = q.options(joinedload(UserEmailMap.user))
461 q = q.options(joinedload(UserEmailMap.user))
462 if cache:
462 if cache:
463 q = q.options(FromCache("sql_cache_short",
463 q = q.options(FromCache("sql_cache_short",
464 "get_email_map_key_%s" % email))
464 "get_email_map_key_%s" % email))
465 ret = getattr(q.scalar(), 'user', None)
465 ret = getattr(q.scalar(), 'user', None)
466
466
467 return ret
467 return ret
468
468
469 @classmethod
469 @classmethod
470 def get_from_cs_author(cls, author):
470 def get_from_cs_author(cls, author):
471 """
471 """
472 Tries to get User objects out of commit author string
472 Tries to get User objects out of commit author string
473
473
474 :param author:
474 :param author:
475 """
475 """
476 from rhodecode.lib.helpers import email, author_name
476 from rhodecode.lib.helpers import email, author_name
477 # Valid email in the attribute passed, see if they're in the system
477 # Valid email in the attribute passed, see if they're in the system
478 _email = email(author)
478 _email = email(author)
479 if _email:
479 if _email:
480 user = cls.get_by_email(_email, case_insensitive=True)
480 user = cls.get_by_email(_email, case_insensitive=True)
481 if user:
481 if user:
482 return user
482 return user
483 # Maybe we can match by username?
483 # Maybe we can match by username?
484 _author = author_name(author)
484 _author = author_name(author)
485 user = cls.get_by_username(_author, case_insensitive=True)
485 user = cls.get_by_username(_author, case_insensitive=True)
486 if user:
486 if user:
487 return user
487 return user
488
488
489 def update_lastlogin(self):
489 def update_lastlogin(self):
490 """Update user lastlogin"""
490 """Update user lastlogin"""
491 self.last_login = datetime.datetime.now()
491 self.last_login = datetime.datetime.now()
492 Session().add(self)
492 Session().add(self)
493 log.debug('updated user %s lastlogin' % self.username)
493 log.debug('updated user %s lastlogin' % self.username)
494
494
495 @classmethod
495 @classmethod
496 def get_first_admin(cls):
496 def get_first_admin(cls):
497 user = User.query().filter(User.admin == True).first()
497 user = User.query().filter(User.admin == True).first()
498 if user is None:
498 if user is None:
499 raise Exception('Missing administrative account!')
499 raise Exception('Missing administrative account!')
500 return user
500 return user
501
501
502 @classmethod
502 @classmethod
503 def get_default_user(cls, cache=False):
503 def get_default_user(cls, cache=False):
504 user = User.get_by_username(User.DEFAULT_USER, cache=cache)
504 user = User.get_by_username(User.DEFAULT_USER, cache=cache)
505 if user is None:
505 if user is None:
506 raise Exception('Missing default account!')
506 raise Exception('Missing default account!')
507 return user
507 return user
508
508
509 def get_api_data(self):
509 def get_api_data(self):
510 """
510 """
511 Common function for generating user related data for API
511 Common function for generating user related data for API
512 """
512 """
513 user = self
513 user = self
514 data = dict(
514 data = dict(
515 user_id=user.user_id,
515 user_id=user.user_id,
516 username=user.username,
516 username=user.username,
517 firstname=user.name,
517 firstname=user.name,
518 lastname=user.lastname,
518 lastname=user.lastname,
519 email=user.email,
519 email=user.email,
520 emails=user.emails,
520 emails=user.emails,
521 api_key=user.api_key,
521 api_key=user.api_key,
522 active=user.active,
522 active=user.active,
523 admin=user.admin,
523 admin=user.admin,
524 ldap_dn=user.ldap_dn,
524 ldap_dn=user.ldap_dn,
525 last_login=user.last_login,
525 last_login=user.last_login,
526 ip_addresses=user.ip_addresses
526 ip_addresses=user.ip_addresses
527 )
527 )
528 return data
528 return data
529
529
530 def __json__(self):
530 def __json__(self):
531 data = dict(
531 data = dict(
532 full_name=self.full_name,
532 full_name=self.full_name,
533 full_name_or_username=self.full_name_or_username,
533 full_name_or_username=self.full_name_or_username,
534 short_contact=self.short_contact,
534 short_contact=self.short_contact,
535 full_contact=self.full_contact
535 full_contact=self.full_contact
536 )
536 )
537 data.update(self.get_api_data())
537 data.update(self.get_api_data())
538 return data
538 return data
539
539
540
540
541 class UserEmailMap(Base, BaseModel):
541 class UserEmailMap(Base, BaseModel):
542 __tablename__ = 'user_email_map'
542 __tablename__ = 'user_email_map'
543 __table_args__ = (
543 __table_args__ = (
544 Index('uem_email_idx', 'email'),
544 Index('uem_email_idx', 'email'),
545 UniqueConstraint('email'),
545 UniqueConstraint('email'),
546 {'extend_existing': True, 'mysql_engine': 'InnoDB',
546 {'extend_existing': True, 'mysql_engine': 'InnoDB',
547 'mysql_charset': 'utf8'}
547 'mysql_charset': 'utf8'}
548 )
548 )
549 __mapper_args__ = {}
549 __mapper_args__ = {}
550
550
551 email_id = Column("email_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
551 email_id = Column("email_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
552 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
552 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
553 _email = Column("email", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
553 _email = Column("email", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
554 user = relationship('User', lazy='joined')
554 user = relationship('User', lazy='joined')
555
555
556 @validates('_email')
556 @validates('_email')
557 def validate_email(self, key, email):
557 def validate_email(self, key, email):
558 # check if this email is not main one
558 # check if this email is not main one
559 main_email = Session().query(User).filter(User.email == email).scalar()
559 main_email = Session().query(User).filter(User.email == email).scalar()
560 if main_email is not None:
560 if main_email is not None:
561 raise AttributeError('email %s is present is user table' % email)
561 raise AttributeError('email %s is present is user table' % email)
562 return email
562 return email
563
563
564 @hybrid_property
564 @hybrid_property
565 def email(self):
565 def email(self):
566 return self._email
566 return self._email
567
567
568 @email.setter
568 @email.setter
569 def email(self, val):
569 def email(self, val):
570 self._email = val.lower() if val else None
570 self._email = val.lower() if val else None
571
571
572
572
573 class UserIpMap(Base, BaseModel):
573 class UserIpMap(Base, BaseModel):
574 __tablename__ = 'user_ip_map'
574 __tablename__ = 'user_ip_map'
575 __table_args__ = (
575 __table_args__ = (
576 UniqueConstraint('user_id', 'ip_addr'),
576 UniqueConstraint('user_id', 'ip_addr'),
577 {'extend_existing': True, 'mysql_engine': 'InnoDB',
577 {'extend_existing': True, 'mysql_engine': 'InnoDB',
578 'mysql_charset': 'utf8'}
578 'mysql_charset': 'utf8'}
579 )
579 )
580 __mapper_args__ = {}
580 __mapper_args__ = {}
581
581
582 ip_id = Column("ip_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
582 ip_id = Column("ip_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
583 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
583 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
584 ip_addr = Column("ip_addr", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
584 ip_addr = Column("ip_addr", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
585 active = Column("active", Boolean(), nullable=True, unique=None, default=True)
585 active = Column("active", Boolean(), nullable=True, unique=None, default=True)
586 user = relationship('User', lazy='joined')
586 user = relationship('User', lazy='joined')
587
587
588 @classmethod
588 @classmethod
589 def _get_ip_range(cls, ip_addr):
589 def _get_ip_range(cls, ip_addr):
590 from rhodecode.lib import ipaddr
590 from rhodecode.lib import ipaddr
591 net = ipaddr.IPNetwork(address=ip_addr)
591 net = ipaddr.IPNetwork(address=ip_addr)
592 return [str(net.network), str(net.broadcast)]
592 return [str(net.network), str(net.broadcast)]
593
593
594 def __json__(self):
594 def __json__(self):
595 return dict(
595 return dict(
596 ip_addr=self.ip_addr,
596 ip_addr=self.ip_addr,
597 ip_range=self._get_ip_range(self.ip_addr)
597 ip_range=self._get_ip_range(self.ip_addr)
598 )
598 )
599
599
600
600
601 class UserLog(Base, BaseModel):
601 class UserLog(Base, BaseModel):
602 __tablename__ = 'user_logs'
602 __tablename__ = 'user_logs'
603 __table_args__ = (
603 __table_args__ = (
604 {'extend_existing': True, 'mysql_engine': 'InnoDB',
604 {'extend_existing': True, 'mysql_engine': 'InnoDB',
605 'mysql_charset': 'utf8'},
605 'mysql_charset': 'utf8'},
606 )
606 )
607 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
607 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
608 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
608 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
609 username = Column("username", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
609 username = Column("username", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
610 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True)
610 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True)
611 repository_name = Column("repository_name", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
611 repository_name = Column("repository_name", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
612 user_ip = Column("user_ip", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
612 user_ip = Column("user_ip", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
613 action = Column("action", UnicodeText(1200000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
613 action = Column("action", UnicodeText(1200000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
614 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
614 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
615
615
616 @property
616 @property
617 def action_as_day(self):
617 def action_as_day(self):
618 return datetime.date(*self.action_date.timetuple()[:3])
618 return datetime.date(*self.action_date.timetuple()[:3])
619
619
620 user = relationship('User')
620 user = relationship('User')
621 repository = relationship('Repository', cascade='')
621 repository = relationship('Repository', cascade='')
622
622
623
623
624 class UserGroup(Base, BaseModel):
624 class UserGroup(Base, BaseModel):
625 __tablename__ = 'users_groups'
625 __tablename__ = 'users_groups'
626 __table_args__ = (
626 __table_args__ = (
627 {'extend_existing': True, 'mysql_engine': 'InnoDB',
627 {'extend_existing': True, 'mysql_engine': 'InnoDB',
628 'mysql_charset': 'utf8'},
628 'mysql_charset': 'utf8'},
629 )
629 )
630
630
631 users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
631 users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
632 users_group_name = Column("users_group_name", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
632 users_group_name = Column("users_group_name", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
633 users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
633 users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
634 inherit_default_permissions = Column("users_group_inherit_default_permissions", Boolean(), nullable=False, unique=None, default=True)
634 inherit_default_permissions = Column("users_group_inherit_default_permissions", Boolean(), nullable=False, unique=None, default=True)
635 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
635 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
636
636
637 members = relationship('UserGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
637 members = relationship('UserGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
638 users_group_to_perm = relationship('UserGroupToPerm', cascade='all')
638 users_group_to_perm = relationship('UserGroupToPerm', cascade='all')
639 users_group_repo_to_perm = relationship('UserGroupRepoToPerm', cascade='all')
639 users_group_repo_to_perm = relationship('UserGroupRepoToPerm', cascade='all')
640 users_group_repo_group_to_perm = relationship('UserGroupRepoGroupToPerm', cascade='all')
640 users_group_repo_group_to_perm = relationship('UserGroupRepoGroupToPerm', cascade='all')
641 user_user_group_to_perm = relationship('UserUserGroupToPerm ', cascade='all')
641 user_user_group_to_perm = relationship('UserUserGroupToPerm ', cascade='all')
642 user = relationship('User')
642 user = relationship('User')
643
643
644 def __unicode__(self):
644 def __unicode__(self):
645 return u"<%s('id:%s:%s')>" % (self.__class__.__name__,
645 return u"<%s('id:%s:%s')>" % (self.__class__.__name__,
646 self.users_group_id,
646 self.users_group_id,
647 self.users_group_name)
647 self.users_group_name)
648
648
649 @classmethod
649 @classmethod
650 def get_by_group_name(cls, group_name, cache=False,
650 def get_by_group_name(cls, group_name, cache=False,
651 case_insensitive=False):
651 case_insensitive=False):
652 if case_insensitive:
652 if case_insensitive:
653 q = cls.query().filter(cls.users_group_name.ilike(group_name))
653 q = cls.query().filter(cls.users_group_name.ilike(group_name))
654 else:
654 else:
655 q = cls.query().filter(cls.users_group_name == group_name)
655 q = cls.query().filter(cls.users_group_name == group_name)
656 if cache:
656 if cache:
657 q = q.options(FromCache(
657 q = q.options(FromCache(
658 "sql_cache_short",
658 "sql_cache_short",
659 "get_user_%s" % _hash_key(group_name)
659 "get_user_%s" % _hash_key(group_name)
660 )
660 )
661 )
661 )
662 return q.scalar()
662 return q.scalar()
663
663
664 @classmethod
664 @classmethod
665 def get(cls, users_group_id, cache=False):
665 def get(cls, users_group_id, cache=False):
666 users_group = cls.query()
666 users_group = cls.query()
667 if cache:
667 if cache:
668 users_group = users_group.options(FromCache("sql_cache_short",
668 users_group = users_group.options(FromCache("sql_cache_short",
669 "get_users_group_%s" % users_group_id))
669 "get_users_group_%s" % users_group_id))
670 return users_group.get(users_group_id)
670 return users_group.get(users_group_id)
671
671
672 def get_api_data(self):
672 def get_api_data(self):
673 users_group = self
673 users_group = self
674
674
675 data = dict(
675 data = dict(
676 users_group_id=users_group.users_group_id,
676 users_group_id=users_group.users_group_id,
677 group_name=users_group.users_group_name,
677 group_name=users_group.users_group_name,
678 active=users_group.users_group_active,
678 active=users_group.users_group_active,
679 )
679 )
680
680
681 return data
681 return data
682
682
683
683
684 class UserGroupMember(Base, BaseModel):
684 class UserGroupMember(Base, BaseModel):
685 __tablename__ = 'users_groups_members'
685 __tablename__ = 'users_groups_members'
686 __table_args__ = (
686 __table_args__ = (
687 {'extend_existing': True, 'mysql_engine': 'InnoDB',
687 {'extend_existing': True, 'mysql_engine': 'InnoDB',
688 'mysql_charset': 'utf8'},
688 'mysql_charset': 'utf8'},
689 )
689 )
690
690
691 users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
691 users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
692 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
692 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
693 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
693 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
694
694
695 user = relationship('User', lazy='joined')
695 user = relationship('User', lazy='joined')
696 users_group = relationship('UserGroup')
696 users_group = relationship('UserGroup')
697
697
698 def __init__(self, gr_id='', u_id=''):
698 def __init__(self, gr_id='', u_id=''):
699 self.users_group_id = gr_id
699 self.users_group_id = gr_id
700 self.user_id = u_id
700 self.user_id = u_id
701
701
702
702
703 class RepositoryField(Base, BaseModel):
703 class RepositoryField(Base, BaseModel):
704 __tablename__ = 'repositories_fields'
704 __tablename__ = 'repositories_fields'
705 __table_args__ = (
705 __table_args__ = (
706 UniqueConstraint('repository_id', 'field_key'), # no-multi field
706 UniqueConstraint('repository_id', 'field_key'), # no-multi field
707 {'extend_existing': True, 'mysql_engine': 'InnoDB',
707 {'extend_existing': True, 'mysql_engine': 'InnoDB',
708 'mysql_charset': 'utf8'},
708 'mysql_charset': 'utf8'},
709 )
709 )
710 PREFIX = 'ex_' # prefix used in form to not conflict with already existing fields
710 PREFIX = 'ex_' # prefix used in form to not conflict with already existing fields
711
711
712 repo_field_id = Column("repo_field_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
712 repo_field_id = Column("repo_field_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
713 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
713 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
714 field_key = Column("field_key", String(250, convert_unicode=False, assert_unicode=None))
714 field_key = Column("field_key", String(250, convert_unicode=False, assert_unicode=None))
715 field_label = Column("field_label", String(1024, convert_unicode=False, assert_unicode=None), nullable=False)
715 field_label = Column("field_label", String(1024, convert_unicode=False, assert_unicode=None), nullable=False)
716 field_value = Column("field_value", String(10000, convert_unicode=False, assert_unicode=None), nullable=False)
716 field_value = Column("field_value", String(10000, convert_unicode=False, assert_unicode=None), nullable=False)
717 field_desc = Column("field_desc", String(1024, convert_unicode=False, assert_unicode=None), nullable=False)
717 field_desc = Column("field_desc", String(1024, convert_unicode=False, assert_unicode=None), nullable=False)
718 field_type = Column("field_type", String(256), nullable=False, unique=None)
718 field_type = Column("field_type", String(256), nullable=False, unique=None)
719 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
719 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
720
720
721 repository = relationship('Repository')
721 repository = relationship('Repository')
722
722
723 @property
723 @property
724 def field_key_prefixed(self):
724 def field_key_prefixed(self):
725 return 'ex_%s' % self.field_key
725 return 'ex_%s' % self.field_key
726
726
727 @classmethod
727 @classmethod
728 def un_prefix_key(cls, key):
728 def un_prefix_key(cls, key):
729 if key.startswith(cls.PREFIX):
729 if key.startswith(cls.PREFIX):
730 return key[len(cls.PREFIX):]
730 return key[len(cls.PREFIX):]
731 return key
731 return key
732
732
733 @classmethod
733 @classmethod
734 def get_by_key_name(cls, key, repo):
734 def get_by_key_name(cls, key, repo):
735 row = cls.query()\
735 row = cls.query()\
736 .filter(cls.repository == repo)\
736 .filter(cls.repository == repo)\
737 .filter(cls.field_key == key).scalar()
737 .filter(cls.field_key == key).scalar()
738 return row
738 return row
739
739
740
740
741 class Repository(Base, BaseModel):
741 class Repository(Base, BaseModel):
742 __tablename__ = 'repositories'
742 __tablename__ = 'repositories'
743 __table_args__ = (
743 __table_args__ = (
744 UniqueConstraint('repo_name'),
744 UniqueConstraint('repo_name'),
745 Index('r_repo_name_idx', 'repo_name'),
745 Index('r_repo_name_idx', 'repo_name'),
746 {'extend_existing': True, 'mysql_engine': 'InnoDB',
746 {'extend_existing': True, 'mysql_engine': 'InnoDB',
747 'mysql_charset': 'utf8'},
747 'mysql_charset': 'utf8'},
748 )
748 )
749
749
750 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
750 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
751 repo_name = Column("repo_name", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
751 repo_name = Column("repo_name", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
752 clone_uri = Column("clone_uri", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
752 clone_uri = Column("clone_uri", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
753 repo_type = Column("repo_type", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default=None)
753 repo_type = Column("repo_type", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default=None)
754 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
754 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
755 private = Column("private", Boolean(), nullable=True, unique=None, default=None)
755 private = Column("private", Boolean(), nullable=True, unique=None, default=None)
756 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
756 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
757 enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True)
757 enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True)
758 description = Column("description", String(10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
758 description = Column("description", String(10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
759 created_on = Column('created_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
759 created_on = Column('created_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
760 updated_on = Column('updated_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
760 updated_on = Column('updated_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
761 landing_rev = Column("landing_revision", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default=None)
761 landing_rev = Column("landing_revision", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default=None)
762 enable_locking = Column("enable_locking", Boolean(), nullable=False, unique=None, default=False)
762 enable_locking = Column("enable_locking", Boolean(), nullable=False, unique=None, default=False)
763 _locked = Column("locked", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
763 _locked = Column("locked", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
764 _changeset_cache = Column("changeset_cache", LargeBinary(), nullable=True) #JSON data
764 _changeset_cache = Column("changeset_cache", LargeBinary(), nullable=True) #JSON data
765
765
766 fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
766 fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
767 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
767 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
768
768
769 user = relationship('User')
769 user = relationship('User')
770 fork = relationship('Repository', remote_side=repo_id)
770 fork = relationship('Repository', remote_side=repo_id)
771 group = relationship('RepoGroup')
771 group = relationship('RepoGroup')
772 repo_to_perm = relationship('UserRepoToPerm', cascade='all', order_by='UserRepoToPerm.repo_to_perm_id')
772 repo_to_perm = relationship('UserRepoToPerm', cascade='all', order_by='UserRepoToPerm.repo_to_perm_id')
773 users_group_to_perm = relationship('UserGroupRepoToPerm', cascade='all')
773 users_group_to_perm = relationship('UserGroupRepoToPerm', cascade='all')
774 stats = relationship('Statistics', cascade='all', uselist=False)
774 stats = relationship('Statistics', cascade='all', uselist=False)
775
775
776 followers = relationship('UserFollowing',
776 followers = relationship('UserFollowing',
777 primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id',
777 primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id',
778 cascade='all')
778 cascade='all')
779 extra_fields = relationship('RepositoryField',
779 extra_fields = relationship('RepositoryField',
780 cascade="all, delete, delete-orphan")
780 cascade="all, delete, delete-orphan")
781
781
782 logs = relationship('UserLog')
782 logs = relationship('UserLog')
783 comments = relationship('ChangesetComment', cascade="all, delete, delete-orphan")
783 comments = relationship('ChangesetComment', cascade="all, delete, delete-orphan")
784
784
785 pull_requests_org = relationship('PullRequest',
785 pull_requests_org = relationship('PullRequest',
786 primaryjoin='PullRequest.org_repo_id==Repository.repo_id',
786 primaryjoin='PullRequest.org_repo_id==Repository.repo_id',
787 cascade="all, delete, delete-orphan")
787 cascade="all, delete, delete-orphan")
788
788
789 pull_requests_other = relationship('PullRequest',
789 pull_requests_other = relationship('PullRequest',
790 primaryjoin='PullRequest.other_repo_id==Repository.repo_id',
790 primaryjoin='PullRequest.other_repo_id==Repository.repo_id',
791 cascade="all, delete, delete-orphan")
791 cascade="all, delete, delete-orphan")
792
792
793 def __unicode__(self):
793 def __unicode__(self):
794 return u"<%s('%s:%s')>" % (self.__class__.__name__, self.repo_id,
794 return u"<%s('%s:%s')>" % (self.__class__.__name__, self.repo_id,
795 self.repo_name)
795 self.repo_name)
796
796
797 @hybrid_property
797 @hybrid_property
798 def locked(self):
798 def locked(self):
799 # always should return [user_id, timelocked]
799 # always should return [user_id, timelocked]
800 if self._locked:
800 if self._locked:
801 _lock_info = self._locked.split(':')
801 _lock_info = self._locked.split(':')
802 return int(_lock_info[0]), _lock_info[1]
802 return int(_lock_info[0]), _lock_info[1]
803 return [None, None]
803 return [None, None]
804
804
805 @locked.setter
805 @locked.setter
806 def locked(self, val):
806 def locked(self, val):
807 if val and isinstance(val, (list, tuple)):
807 if val and isinstance(val, (list, tuple)):
808 self._locked = ':'.join(map(str, val))
808 self._locked = ':'.join(map(str, val))
809 else:
809 else:
810 self._locked = None
810 self._locked = None
811
811
812 @hybrid_property
812 @hybrid_property
813 def changeset_cache(self):
813 def changeset_cache(self):
814 from rhodecode.lib.vcs.backends.base import EmptyChangeset
814 from rhodecode.lib.vcs.backends.base import EmptyChangeset
815 dummy = EmptyChangeset().__json__()
815 dummy = EmptyChangeset().__json__()
816 if not self._changeset_cache:
816 if not self._changeset_cache:
817 return dummy
817 return dummy
818 try:
818 try:
819 return json.loads(self._changeset_cache)
819 return json.loads(self._changeset_cache)
820 except TypeError:
820 except TypeError:
821 return dummy
821 return dummy
822
822
823 @changeset_cache.setter
823 @changeset_cache.setter
824 def changeset_cache(self, val):
824 def changeset_cache(self, val):
825 try:
825 try:
826 self._changeset_cache = json.dumps(val)
826 self._changeset_cache = json.dumps(val)
827 except Exception:
827 except Exception:
828 log.error(traceback.format_exc())
828 log.error(traceback.format_exc())
829
829
830 @classmethod
830 @classmethod
831 def url_sep(cls):
831 def url_sep(cls):
832 return URL_SEP
832 return URL_SEP
833
833
834 @classmethod
834 @classmethod
835 def normalize_repo_name(cls, repo_name):
835 def normalize_repo_name(cls, repo_name):
836 """
836 """
837 Normalizes os specific repo_name to the format internally stored inside
837 Normalizes os specific repo_name to the format internally stored inside
838 dabatabase using URL_SEP
838 dabatabase using URL_SEP
839
839
840 :param cls:
840 :param cls:
841 :param repo_name:
841 :param repo_name:
842 """
842 """
843 return cls.url_sep().join(repo_name.split(os.sep))
843 return cls.url_sep().join(repo_name.split(os.sep))
844
844
845 @classmethod
845 @classmethod
846 def get_by_repo_name(cls, repo_name):
846 def get_by_repo_name(cls, repo_name):
847 q = Session().query(cls).filter(cls.repo_name == repo_name)
847 q = Session().query(cls).filter(cls.repo_name == repo_name)
848 q = q.options(joinedload(Repository.fork))\
848 q = q.options(joinedload(Repository.fork))\
849 .options(joinedload(Repository.user))\
849 .options(joinedload(Repository.user))\
850 .options(joinedload(Repository.group))
850 .options(joinedload(Repository.group))
851 return q.scalar()
851 return q.scalar()
852
852
853 @classmethod
853 @classmethod
854 def get_by_full_path(cls, repo_full_path):
854 def get_by_full_path(cls, repo_full_path):
855 repo_name = repo_full_path.split(cls.base_path(), 1)[-1]
855 repo_name = repo_full_path.split(cls.base_path(), 1)[-1]
856 repo_name = cls.normalize_repo_name(repo_name)
856 repo_name = cls.normalize_repo_name(repo_name)
857 return cls.get_by_repo_name(repo_name.strip(URL_SEP))
857 return cls.get_by_repo_name(repo_name.strip(URL_SEP))
858
858
859 @classmethod
859 @classmethod
860 def get_repo_forks(cls, repo_id):
860 def get_repo_forks(cls, repo_id):
861 return cls.query().filter(Repository.fork_id == repo_id)
861 return cls.query().filter(Repository.fork_id == repo_id)
862
862
863 @classmethod
863 @classmethod
864 def base_path(cls):
864 def base_path(cls):
865 """
865 """
866 Returns base path when all repos are stored
866 Returns base path when all repos are stored
867
867
868 :param cls:
868 :param cls:
869 """
869 """
870 q = Session().query(RhodeCodeUi)\
870 q = Session().query(RhodeCodeUi)\
871 .filter(RhodeCodeUi.ui_key == cls.url_sep())
871 .filter(RhodeCodeUi.ui_key == cls.url_sep())
872 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
872 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
873 return q.one().ui_value
873 return q.one().ui_value
874
874
875 @property
875 @property
876 def forks(self):
876 def forks(self):
877 """
877 """
878 Return forks of this repo
878 Return forks of this repo
879 """
879 """
880 return Repository.get_repo_forks(self.repo_id)
880 return Repository.get_repo_forks(self.repo_id)
881
881
882 @property
882 @property
883 def parent(self):
883 def parent(self):
884 """
884 """
885 Returns fork parent
885 Returns fork parent
886 """
886 """
887 return self.fork
887 return self.fork
888
888
889 @property
889 @property
890 def just_name(self):
890 def just_name(self):
891 return self.repo_name.split(Repository.url_sep())[-1]
891 return self.repo_name.split(Repository.url_sep())[-1]
892
892
893 @property
893 @property
894 def groups_with_parents(self):
894 def groups_with_parents(self):
895 groups = []
895 groups = []
896 if self.group is None:
896 if self.group is None:
897 return groups
897 return groups
898
898
899 cur_gr = self.group
899 cur_gr = self.group
900 groups.insert(0, cur_gr)
900 groups.insert(0, cur_gr)
901 while 1:
901 while 1:
902 gr = getattr(cur_gr, 'parent_group', None)
902 gr = getattr(cur_gr, 'parent_group', None)
903 cur_gr = cur_gr.parent_group
903 cur_gr = cur_gr.parent_group
904 if gr is None:
904 if gr is None:
905 break
905 break
906 groups.insert(0, gr)
906 groups.insert(0, gr)
907
907
908 return groups
908 return groups
909
909
910 @property
910 @property
911 def groups_and_repo(self):
911 def groups_and_repo(self):
912 return self.groups_with_parents, self.just_name, self.repo_name
912 return self.groups_with_parents, self.just_name, self.repo_name
913
913
914 @LazyProperty
914 @LazyProperty
915 def repo_path(self):
915 def repo_path(self):
916 """
916 """
917 Returns base full path for that repository means where it actually
917 Returns base full path for that repository means where it actually
918 exists on a filesystem
918 exists on a filesystem
919 """
919 """
920 q = Session().query(RhodeCodeUi).filter(RhodeCodeUi.ui_key ==
920 q = Session().query(RhodeCodeUi).filter(RhodeCodeUi.ui_key ==
921 Repository.url_sep())
921 Repository.url_sep())
922 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
922 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
923 return q.one().ui_value
923 return q.one().ui_value
924
924
925 @property
925 @property
926 def repo_full_path(self):
926 def repo_full_path(self):
927 p = [self.repo_path]
927 p = [self.repo_path]
928 # we need to split the name by / since this is how we store the
928 # we need to split the name by / since this is how we store the
929 # names in the database, but that eventually needs to be converted
929 # names in the database, but that eventually needs to be converted
930 # into a valid system path
930 # into a valid system path
931 p += self.repo_name.split(Repository.url_sep())
931 p += self.repo_name.split(Repository.url_sep())
932 return os.path.join(*map(safe_unicode, p))
932 return os.path.join(*map(safe_unicode, p))
933
933
934 @property
934 @property
935 def cache_keys(self):
935 def cache_keys(self):
936 """
936 """
937 Returns associated cache keys for that repo
937 Returns associated cache keys for that repo
938 """
938 """
939 return CacheInvalidation.query()\
939 return CacheInvalidation.query()\
940 .filter(CacheInvalidation.cache_args == self.repo_name)\
940 .filter(CacheInvalidation.cache_args == self.repo_name)\
941 .order_by(CacheInvalidation.cache_key)\
941 .order_by(CacheInvalidation.cache_key)\
942 .all()
942 .all()
943
943
944 def get_new_name(self, repo_name):
944 def get_new_name(self, repo_name):
945 """
945 """
946 returns new full repository name based on assigned group and new new
946 returns new full repository name based on assigned group and new new
947
947
948 :param group_name:
948 :param group_name:
949 """
949 """
950 path_prefix = self.group.full_path_splitted if self.group else []
950 path_prefix = self.group.full_path_splitted if self.group else []
951 return Repository.url_sep().join(path_prefix + [repo_name])
951 return Repository.url_sep().join(path_prefix + [repo_name])
952
952
953 @property
953 @property
954 def _ui(self):
954 def _ui(self):
955 """
955 """
956 Creates an db based ui object for this repository
956 Creates an db based ui object for this repository
957 """
957 """
958 from rhodecode.lib.utils import make_ui
958 from rhodecode.lib.utils import make_ui
959 return make_ui('db', clear_session=False)
959 return make_ui('db', clear_session=False)
960
960
961 @classmethod
961 @classmethod
962 def is_valid(cls, repo_name):
962 def is_valid(cls, repo_name):
963 """
963 """
964 returns True if given repo name is a valid filesystem repository
964 returns True if given repo name is a valid filesystem repository
965
965
966 :param cls:
966 :param cls:
967 :param repo_name:
967 :param repo_name:
968 """
968 """
969 from rhodecode.lib.utils import is_valid_repo
969 from rhodecode.lib.utils import is_valid_repo
970
970
971 return is_valid_repo(repo_name, cls.base_path())
971 return is_valid_repo(repo_name, cls.base_path())
972
972
973 def get_api_data(self):
973 def get_api_data(self):
974 """
974 """
975 Common function for generating repo api data
975 Common function for generating repo api data
976
976
977 """
977 """
978 repo = self
978 repo = self
979 data = dict(
979 data = dict(
980 repo_id=repo.repo_id,
980 repo_id=repo.repo_id,
981 repo_name=repo.repo_name,
981 repo_name=repo.repo_name,
982 repo_type=repo.repo_type,
982 repo_type=repo.repo_type,
983 clone_uri=repo.clone_uri,
983 clone_uri=repo.clone_uri,
984 private=repo.private,
984 private=repo.private,
985 created_on=repo.created_on,
985 created_on=repo.created_on,
986 description=repo.description,
986 description=repo.description,
987 landing_rev=repo.landing_rev,
987 landing_rev=repo.landing_rev,
988 owner=repo.user.username,
988 owner=repo.user.username,
989 fork_of=repo.fork.repo_name if repo.fork else None,
989 fork_of=repo.fork.repo_name if repo.fork else None,
990 enable_statistics=repo.enable_statistics,
990 enable_statistics=repo.enable_statistics,
991 enable_locking=repo.enable_locking,
991 enable_locking=repo.enable_locking,
992 enable_downloads=repo.enable_downloads,
992 enable_downloads=repo.enable_downloads,
993 last_changeset=repo.changeset_cache,
993 last_changeset=repo.changeset_cache,
994 locked_by=User.get(self.locked[0]).get_api_data() \
994 locked_by=User.get(self.locked[0]).get_api_data() \
995 if self.locked[0] else None,
995 if self.locked[0] else None,
996 locked_date=time_to_datetime(self.locked[1]) \
996 locked_date=time_to_datetime(self.locked[1]) \
997 if self.locked[1] else None
997 if self.locked[1] else None
998 )
998 )
999 rc_config = RhodeCodeSetting.get_app_settings()
999 rc_config = RhodeCodeSetting.get_app_settings()
1000 repository_fields = str2bool(rc_config.get('rhodecode_repository_fields'))
1000 repository_fields = str2bool(rc_config.get('rhodecode_repository_fields'))
1001 if repository_fields:
1001 if repository_fields:
1002 for f in self.extra_fields:
1002 for f in self.extra_fields:
1003 data[f.field_key_prefixed] = f.field_value
1003 data[f.field_key_prefixed] = f.field_value
1004
1004
1005 return data
1005 return data
1006
1006
1007 @classmethod
1007 @classmethod
1008 def lock(cls, repo, user_id):
1008 def lock(cls, repo, user_id):
1009 repo.locked = [user_id, time.time()]
1009 repo.locked = [user_id, time.time()]
1010 Session().add(repo)
1010 Session().add(repo)
1011 Session().commit()
1011 Session().commit()
1012
1012
1013 @classmethod
1013 @classmethod
1014 def unlock(cls, repo):
1014 def unlock(cls, repo):
1015 repo.locked = None
1015 repo.locked = None
1016 Session().add(repo)
1016 Session().add(repo)
1017 Session().commit()
1017 Session().commit()
1018
1018
1019 @classmethod
1019 @classmethod
1020 def getlock(cls, repo):
1020 def getlock(cls, repo):
1021 return repo.locked
1021 return repo.locked
1022
1022
1023 @property
1023 @property
1024 def last_db_change(self):
1024 def last_db_change(self):
1025 return self.updated_on
1025 return self.updated_on
1026
1026
1027 def clone_url(self, **override):
1027 def clone_url(self, **override):
1028 from pylons import url
1028 from pylons import url
1029 from urlparse import urlparse
1029 from urlparse import urlparse
1030 import urllib
1030 import urllib
1031 parsed_url = urlparse(url('home', qualified=True))
1031 parsed_url = urlparse(url('home', qualified=True))
1032 default_clone_uri = '%(scheme)s://%(user)s%(pass)s%(netloc)s%(prefix)s%(path)s'
1032 default_clone_uri = '%(scheme)s://%(user)s%(pass)s%(netloc)s%(prefix)s%(path)s'
1033 decoded_path = safe_unicode(urllib.unquote(parsed_url.path))
1033 decoded_path = safe_unicode(urllib.unquote(parsed_url.path))
1034 args = {
1034 args = {
1035 'user': '',
1035 'user': '',
1036 'pass': '',
1036 'pass': '',
1037 'scheme': parsed_url.scheme,
1037 'scheme': parsed_url.scheme,
1038 'netloc': parsed_url.netloc,
1038 'netloc': parsed_url.netloc,
1039 'prefix': decoded_path,
1039 'prefix': decoded_path,
1040 'path': self.repo_name
1040 'path': self.repo_name
1041 }
1041 }
1042
1042
1043 args.update(override)
1043 args.update(override)
1044 return default_clone_uri % args
1044 return default_clone_uri % args
1045
1045
1046 #==========================================================================
1046 #==========================================================================
1047 # SCM PROPERTIES
1047 # SCM PROPERTIES
1048 #==========================================================================
1048 #==========================================================================
1049
1049
1050 def get_changeset(self, rev=None):
1050 def get_changeset(self, rev=None):
1051 return get_changeset_safe(self.scm_instance, rev)
1051 return get_changeset_safe(self.scm_instance, rev)
1052
1052
1053 def get_landing_changeset(self):
1053 def get_landing_changeset(self):
1054 """
1054 """
1055 Returns landing changeset, or if that doesn't exist returns the tip
1055 Returns landing changeset, or if that doesn't exist returns the tip
1056 """
1056 """
1057 cs = self.get_changeset(self.landing_rev) or self.get_changeset()
1057 cs = self.get_changeset(self.landing_rev) or self.get_changeset()
1058 return cs
1058 return cs
1059
1059
1060 def update_changeset_cache(self, cs_cache=None):
1060 def update_changeset_cache(self, cs_cache=None):
1061 """
1061 """
1062 Update cache of last changeset for repository, keys should be::
1062 Update cache of last changeset for repository, keys should be::
1063
1063
1064 short_id
1064 short_id
1065 raw_id
1065 raw_id
1066 revision
1066 revision
1067 message
1067 message
1068 date
1068 date
1069 author
1069 author
1070
1070
1071 :param cs_cache:
1071 :param cs_cache:
1072 """
1072 """
1073 from rhodecode.lib.vcs.backends.base import BaseChangeset
1073 from rhodecode.lib.vcs.backends.base import BaseChangeset
1074 if cs_cache is None:
1074 if cs_cache is None:
1075 cs_cache = EmptyChangeset()
1075 cs_cache = EmptyChangeset()
1076 # use no-cache version here
1076 # use no-cache version here
1077 scm_repo = self.scm_instance_no_cache()
1077 scm_repo = self.scm_instance_no_cache()
1078 if scm_repo:
1078 if scm_repo:
1079 cs_cache = scm_repo.get_changeset()
1079 cs_cache = scm_repo.get_changeset()
1080
1080
1081 if isinstance(cs_cache, BaseChangeset):
1081 if isinstance(cs_cache, BaseChangeset):
1082 cs_cache = cs_cache.__json__()
1082 cs_cache = cs_cache.__json__()
1083
1083
1084 if (cs_cache != self.changeset_cache or not self.changeset_cache):
1084 if (cs_cache != self.changeset_cache or not self.changeset_cache):
1085 _default = datetime.datetime.fromtimestamp(0)
1085 _default = datetime.datetime.fromtimestamp(0)
1086 last_change = cs_cache.get('date') or _default
1086 last_change = cs_cache.get('date') or _default
1087 log.debug('updated repo %s with new cs cache %s'
1087 log.debug('updated repo %s with new cs cache %s'
1088 % (self.repo_name, cs_cache))
1088 % (self.repo_name, cs_cache))
1089 self.updated_on = last_change
1089 self.updated_on = last_change
1090 self.changeset_cache = cs_cache
1090 self.changeset_cache = cs_cache
1091 Session().add(self)
1091 Session().add(self)
1092 Session().commit()
1092 Session().commit()
1093 else:
1093 else:
1094 log.debug('Skipping repo:%s already with latest changes'
1094 log.debug('Skipping repo:%s already with latest changes'
1095 % self.repo_name)
1095 % self.repo_name)
1096
1096
1097 @property
1097 @property
1098 def tip(self):
1098 def tip(self):
1099 return self.get_changeset('tip')
1099 return self.get_changeset('tip')
1100
1100
1101 @property
1101 @property
1102 def author(self):
1102 def author(self):
1103 return self.tip.author
1103 return self.tip.author
1104
1104
1105 @property
1105 @property
1106 def last_change(self):
1106 def last_change(self):
1107 return self.scm_instance.last_change
1107 return self.scm_instance.last_change
1108
1108
1109 def get_comments(self, revisions=None):
1109 def get_comments(self, revisions=None):
1110 """
1110 """
1111 Returns comments for this repository grouped by revisions
1111 Returns comments for this repository grouped by revisions
1112
1112
1113 :param revisions: filter query by revisions only
1113 :param revisions: filter query by revisions only
1114 """
1114 """
1115 cmts = ChangesetComment.query()\
1115 cmts = ChangesetComment.query()\
1116 .filter(ChangesetComment.repo == self)
1116 .filter(ChangesetComment.repo == self)
1117 if revisions:
1117 if revisions:
1118 cmts = cmts.filter(ChangesetComment.revision.in_(revisions))
1118 cmts = cmts.filter(ChangesetComment.revision.in_(revisions))
1119 grouped = defaultdict(list)
1119 grouped = defaultdict(list)
1120 for cmt in cmts.all():
1120 for cmt in cmts.all():
1121 grouped[cmt.revision].append(cmt)
1121 grouped[cmt.revision].append(cmt)
1122 return grouped
1122 return grouped
1123
1123
1124 def statuses(self, revisions=None):
1124 def statuses(self, revisions=None):
1125 """
1125 """
1126 Returns statuses for this repository
1126 Returns statuses for this repository
1127
1127
1128 :param revisions: list of revisions to get statuses for
1128 :param revisions: list of revisions to get statuses for
1129 :type revisions: list
1129 :type revisions: list
1130 """
1130 """
1131
1131
1132 statuses = ChangesetStatus.query()\
1132 statuses = ChangesetStatus.query()\
1133 .filter(ChangesetStatus.repo == self)\
1133 .filter(ChangesetStatus.repo == self)\
1134 .filter(ChangesetStatus.version == 0)
1134 .filter(ChangesetStatus.version == 0)
1135 if revisions:
1135 if revisions:
1136 statuses = statuses.filter(ChangesetStatus.revision.in_(revisions))
1136 statuses = statuses.filter(ChangesetStatus.revision.in_(revisions))
1137 grouped = {}
1137 grouped = {}
1138
1138
1139 #maybe we have open new pullrequest without a status ?
1139 #maybe we have open new pullrequest without a status ?
1140 stat = ChangesetStatus.STATUS_UNDER_REVIEW
1140 stat = ChangesetStatus.STATUS_UNDER_REVIEW
1141 status_lbl = ChangesetStatus.get_status_lbl(stat)
1141 status_lbl = ChangesetStatus.get_status_lbl(stat)
1142 for pr in PullRequest.query().filter(PullRequest.org_repo == self).all():
1142 for pr in PullRequest.query().filter(PullRequest.org_repo == self).all():
1143 for rev in pr.revisions:
1143 for rev in pr.revisions:
1144 pr_id = pr.pull_request_id
1144 pr_id = pr.pull_request_id
1145 pr_repo = pr.other_repo.repo_name
1145 pr_repo = pr.other_repo.repo_name
1146 grouped[rev] = [stat, status_lbl, pr_id, pr_repo]
1146 grouped[rev] = [stat, status_lbl, pr_id, pr_repo]
1147
1147
1148 for stat in statuses.all():
1148 for stat in statuses.all():
1149 pr_id = pr_repo = None
1149 pr_id = pr_repo = None
1150 if stat.pull_request:
1150 if stat.pull_request:
1151 pr_id = stat.pull_request.pull_request_id
1151 pr_id = stat.pull_request.pull_request_id
1152 pr_repo = stat.pull_request.other_repo.repo_name
1152 pr_repo = stat.pull_request.other_repo.repo_name
1153 grouped[stat.revision] = [str(stat.status), stat.status_lbl,
1153 grouped[stat.revision] = [str(stat.status), stat.status_lbl,
1154 pr_id, pr_repo]
1154 pr_id, pr_repo]
1155 return grouped
1155 return grouped
1156
1156
1157 def _repo_size(self):
1157 def _repo_size(self):
1158 from rhodecode.lib import helpers as h
1158 from rhodecode.lib import helpers as h
1159 log.debug('calculating repository size...')
1159 log.debug('calculating repository size...')
1160 return h.format_byte_size(self.scm_instance.size)
1160 return h.format_byte_size(self.scm_instance.size)
1161
1161
1162 #==========================================================================
1162 #==========================================================================
1163 # SCM CACHE INSTANCE
1163 # SCM CACHE INSTANCE
1164 #==========================================================================
1164 #==========================================================================
1165
1165
1166 @property
1167 def invalidate(self):
1168 return CacheInvalidation.invalidate(self.repo_name)
1169
1170 def set_invalidate(self):
1166 def set_invalidate(self):
1171 """
1167 """
1172 Mark caches of this repo as invalid.
1168 Mark caches of this repo as invalid.
1173 """
1169 """
1174 CacheInvalidation.set_invalidate(self.repo_name)
1170 CacheInvalidation.set_invalidate(self.repo_name)
1175
1171
1176 def scm_instance_no_cache(self):
1172 def scm_instance_no_cache(self):
1177 return self.__get_instance()
1173 return self.__get_instance()
1178
1174
1179 @property
1175 @property
1180 def scm_instance(self):
1176 def scm_instance(self):
1181 import rhodecode
1177 import rhodecode
1182 full_cache = str2bool(rhodecode.CONFIG.get('vcs_full_cache'))
1178 full_cache = str2bool(rhodecode.CONFIG.get('vcs_full_cache'))
1183 if full_cache:
1179 if full_cache:
1184 return self.scm_instance_cached()
1180 return self.scm_instance_cached()
1185 return self.__get_instance()
1181 return self.__get_instance()
1186
1182
1187 def scm_instance_cached(self, cache_map=None):
1183 def scm_instance_cached(self, valid_cache_keys=None):
1188 @cache_region('long_term')
1184 @cache_region('long_term')
1189 def _c(repo_name):
1185 def _c(repo_name):
1190 return self.__get_instance()
1186 return self.__get_instance()
1191 rn = self.repo_name
1187 rn = self.repo_name
1192
1188
1193 if cache_map:
1189 valid = CacheInvalidation.test_and_set_valid(rn, valid_cache_keys=valid_cache_keys)
1194 # get using prefilled cache_map
1190 if not valid:
1195 invalidate_repo = cache_map[self.repo_name]
1191 log.debug('Cache for %s invalidated, getting new object' % (rn))
1196 if invalidate_repo:
1197 invalidate_repo = (None if invalidate_repo.cache_active
1198 else invalidate_repo)
1199 else:
1200 # get from invalidate
1201 invalidate_repo = self.invalidate
1202
1203 if invalidate_repo is not None:
1204 region_invalidate(_c, None, rn)
1192 region_invalidate(_c, None, rn)
1205 log.debug('Cache for %s invalidated, getting new object' % (rn))
1206 # update our cache
1207 CacheInvalidation.set_valid(invalidate_repo.cache_key)
1208 else:
1193 else:
1209 log.debug('Getting obj for %s from cache' % (rn))
1194 log.debug('Getting obj for %s from cache' % (rn))
1210 return _c(rn)
1195 return _c(rn)
1211
1196
1212 def __get_instance(self):
1197 def __get_instance(self):
1213 repo_full_path = self.repo_full_path
1198 repo_full_path = self.repo_full_path
1214 try:
1199 try:
1215 alias = get_scm(repo_full_path)[0]
1200 alias = get_scm(repo_full_path)[0]
1216 log.debug('Creating instance of %s repository from %s'
1201 log.debug('Creating instance of %s repository from %s'
1217 % (alias, repo_full_path))
1202 % (alias, repo_full_path))
1218 backend = get_backend(alias)
1203 backend = get_backend(alias)
1219 except VCSError:
1204 except VCSError:
1220 log.error(traceback.format_exc())
1205 log.error(traceback.format_exc())
1221 log.error('Perhaps this repository is in db and not in '
1206 log.error('Perhaps this repository is in db and not in '
1222 'filesystem run rescan repositories with '
1207 'filesystem run rescan repositories with '
1223 '"destroy old data " option from admin panel')
1208 '"destroy old data " option from admin panel')
1224 return
1209 return
1225
1210
1226 if alias == 'hg':
1211 if alias == 'hg':
1227
1212
1228 repo = backend(safe_str(repo_full_path), create=False,
1213 repo = backend(safe_str(repo_full_path), create=False,
1229 baseui=self._ui)
1214 baseui=self._ui)
1230 # skip hidden web repository
1215 # skip hidden web repository
1231 if repo._get_hidden():
1216 if repo._get_hidden():
1232 return
1217 return
1233 else:
1218 else:
1234 repo = backend(repo_full_path, create=False)
1219 repo = backend(repo_full_path, create=False)
1235
1220
1236 return repo
1221 return repo
1237
1222
1238
1223
1239 class RepoGroup(Base, BaseModel):
1224 class RepoGroup(Base, BaseModel):
1240 __tablename__ = 'groups'
1225 __tablename__ = 'groups'
1241 __table_args__ = (
1226 __table_args__ = (
1242 UniqueConstraint('group_name', 'group_parent_id'),
1227 UniqueConstraint('group_name', 'group_parent_id'),
1243 CheckConstraint('group_id != group_parent_id'),
1228 CheckConstraint('group_id != group_parent_id'),
1244 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1229 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1245 'mysql_charset': 'utf8'},
1230 'mysql_charset': 'utf8'},
1246 )
1231 )
1247 __mapper_args__ = {'order_by': 'group_name'}
1232 __mapper_args__ = {'order_by': 'group_name'}
1248
1233
1249 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1234 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1250 group_name = Column("group_name", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
1235 group_name = Column("group_name", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
1251 group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
1236 group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
1252 group_description = Column("group_description", String(10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1237 group_description = Column("group_description", String(10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1253 enable_locking = Column("enable_locking", Boolean(), nullable=False, unique=None, default=False)
1238 enable_locking = Column("enable_locking", Boolean(), nullable=False, unique=None, default=False)
1254 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
1239 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
1255
1240
1256 repo_group_to_perm = relationship('UserRepoGroupToPerm', cascade='all', order_by='UserRepoGroupToPerm.group_to_perm_id')
1241 repo_group_to_perm = relationship('UserRepoGroupToPerm', cascade='all', order_by='UserRepoGroupToPerm.group_to_perm_id')
1257 users_group_to_perm = relationship('UserGroupRepoGroupToPerm', cascade='all')
1242 users_group_to_perm = relationship('UserGroupRepoGroupToPerm', cascade='all')
1258 parent_group = relationship('RepoGroup', remote_side=group_id)
1243 parent_group = relationship('RepoGroup', remote_side=group_id)
1259 user = relationship('User')
1244 user = relationship('User')
1260
1245
1261 def __init__(self, group_name='', parent_group=None):
1246 def __init__(self, group_name='', parent_group=None):
1262 self.group_name = group_name
1247 self.group_name = group_name
1263 self.parent_group = parent_group
1248 self.parent_group = parent_group
1264
1249
1265 def __unicode__(self):
1250 def __unicode__(self):
1266 return u"<%s('id:%s:%s')>" % (self.__class__.__name__, self.group_id,
1251 return u"<%s('id:%s:%s')>" % (self.__class__.__name__, self.group_id,
1267 self.group_name)
1252 self.group_name)
1268
1253
1269 @classmethod
1254 @classmethod
1270 def groups_choices(cls, groups=None, show_empty_group=True):
1255 def groups_choices(cls, groups=None, show_empty_group=True):
1271 from webhelpers.html import literal as _literal
1256 from webhelpers.html import literal as _literal
1272 if not groups:
1257 if not groups:
1273 groups = cls.query().all()
1258 groups = cls.query().all()
1274
1259
1275 repo_groups = []
1260 repo_groups = []
1276 if show_empty_group:
1261 if show_empty_group:
1277 repo_groups = [('-1', '-- %s --' % _('top level'))]
1262 repo_groups = [('-1', '-- %s --' % _('top level'))]
1278 sep = ' &raquo; '
1263 sep = ' &raquo; '
1279 _name = lambda k: _literal(sep.join(k))
1264 _name = lambda k: _literal(sep.join(k))
1280
1265
1281 repo_groups.extend([(x.group_id, _name(x.full_path_splitted))
1266 repo_groups.extend([(x.group_id, _name(x.full_path_splitted))
1282 for x in groups])
1267 for x in groups])
1283
1268
1284 repo_groups = sorted(repo_groups, key=lambda t: t[1].split(sep)[0])
1269 repo_groups = sorted(repo_groups, key=lambda t: t[1].split(sep)[0])
1285 return repo_groups
1270 return repo_groups
1286
1271
1287 @classmethod
1272 @classmethod
1288 def url_sep(cls):
1273 def url_sep(cls):
1289 return URL_SEP
1274 return URL_SEP
1290
1275
1291 @classmethod
1276 @classmethod
1292 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
1277 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
1293 if case_insensitive:
1278 if case_insensitive:
1294 gr = cls.query()\
1279 gr = cls.query()\
1295 .filter(cls.group_name.ilike(group_name))
1280 .filter(cls.group_name.ilike(group_name))
1296 else:
1281 else:
1297 gr = cls.query()\
1282 gr = cls.query()\
1298 .filter(cls.group_name == group_name)
1283 .filter(cls.group_name == group_name)
1299 if cache:
1284 if cache:
1300 gr = gr.options(FromCache(
1285 gr = gr.options(FromCache(
1301 "sql_cache_short",
1286 "sql_cache_short",
1302 "get_group_%s" % _hash_key(group_name)
1287 "get_group_%s" % _hash_key(group_name)
1303 )
1288 )
1304 )
1289 )
1305 return gr.scalar()
1290 return gr.scalar()
1306
1291
1307 @property
1292 @property
1308 def parents(self):
1293 def parents(self):
1309 parents_recursion_limit = 5
1294 parents_recursion_limit = 5
1310 groups = []
1295 groups = []
1311 if self.parent_group is None:
1296 if self.parent_group is None:
1312 return groups
1297 return groups
1313 cur_gr = self.parent_group
1298 cur_gr = self.parent_group
1314 groups.insert(0, cur_gr)
1299 groups.insert(0, cur_gr)
1315 cnt = 0
1300 cnt = 0
1316 while 1:
1301 while 1:
1317 cnt += 1
1302 cnt += 1
1318 gr = getattr(cur_gr, 'parent_group', None)
1303 gr = getattr(cur_gr, 'parent_group', None)
1319 cur_gr = cur_gr.parent_group
1304 cur_gr = cur_gr.parent_group
1320 if gr is None:
1305 if gr is None:
1321 break
1306 break
1322 if cnt == parents_recursion_limit:
1307 if cnt == parents_recursion_limit:
1323 # this will prevent accidental infinit loops
1308 # this will prevent accidental infinit loops
1324 log.error('group nested more than %s' %
1309 log.error('group nested more than %s' %
1325 parents_recursion_limit)
1310 parents_recursion_limit)
1326 break
1311 break
1327
1312
1328 groups.insert(0, gr)
1313 groups.insert(0, gr)
1329 return groups
1314 return groups
1330
1315
1331 @property
1316 @property
1332 def children(self):
1317 def children(self):
1333 return RepoGroup.query().filter(RepoGroup.parent_group == self)
1318 return RepoGroup.query().filter(RepoGroup.parent_group == self)
1334
1319
1335 @property
1320 @property
1336 def name(self):
1321 def name(self):
1337 return self.group_name.split(RepoGroup.url_sep())[-1]
1322 return self.group_name.split(RepoGroup.url_sep())[-1]
1338
1323
1339 @property
1324 @property
1340 def full_path(self):
1325 def full_path(self):
1341 return self.group_name
1326 return self.group_name
1342
1327
1343 @property
1328 @property
1344 def full_path_splitted(self):
1329 def full_path_splitted(self):
1345 return self.group_name.split(RepoGroup.url_sep())
1330 return self.group_name.split(RepoGroup.url_sep())
1346
1331
1347 @property
1332 @property
1348 def repositories(self):
1333 def repositories(self):
1349 return Repository.query()\
1334 return Repository.query()\
1350 .filter(Repository.group == self)\
1335 .filter(Repository.group == self)\
1351 .order_by(Repository.repo_name)
1336 .order_by(Repository.repo_name)
1352
1337
1353 @property
1338 @property
1354 def repositories_recursive_count(self):
1339 def repositories_recursive_count(self):
1355 cnt = self.repositories.count()
1340 cnt = self.repositories.count()
1356
1341
1357 def children_count(group):
1342 def children_count(group):
1358 cnt = 0
1343 cnt = 0
1359 for child in group.children:
1344 for child in group.children:
1360 cnt += child.repositories.count()
1345 cnt += child.repositories.count()
1361 cnt += children_count(child)
1346 cnt += children_count(child)
1362 return cnt
1347 return cnt
1363
1348
1364 return cnt + children_count(self)
1349 return cnt + children_count(self)
1365
1350
1366 def _recursive_objects(self, include_repos=True):
1351 def _recursive_objects(self, include_repos=True):
1367 all_ = []
1352 all_ = []
1368
1353
1369 def _get_members(root_gr):
1354 def _get_members(root_gr):
1370 if include_repos:
1355 if include_repos:
1371 for r in root_gr.repositories:
1356 for r in root_gr.repositories:
1372 all_.append(r)
1357 all_.append(r)
1373 childs = root_gr.children.all()
1358 childs = root_gr.children.all()
1374 if childs:
1359 if childs:
1375 for gr in childs:
1360 for gr in childs:
1376 all_.append(gr)
1361 all_.append(gr)
1377 _get_members(gr)
1362 _get_members(gr)
1378
1363
1379 _get_members(self)
1364 _get_members(self)
1380 return [self] + all_
1365 return [self] + all_
1381
1366
1382 def recursive_groups_and_repos(self):
1367 def recursive_groups_and_repos(self):
1383 """
1368 """
1384 Recursive return all groups, with repositories in those groups
1369 Recursive return all groups, with repositories in those groups
1385 """
1370 """
1386 return self._recursive_objects()
1371 return self._recursive_objects()
1387
1372
1388 def recursive_groups(self):
1373 def recursive_groups(self):
1389 """
1374 """
1390 Returns all children groups for this group including children of children
1375 Returns all children groups for this group including children of children
1391 """
1376 """
1392 return self._recursive_objects(include_repos=False)
1377 return self._recursive_objects(include_repos=False)
1393
1378
1394 def get_new_name(self, group_name):
1379 def get_new_name(self, group_name):
1395 """
1380 """
1396 returns new full group name based on parent and new name
1381 returns new full group name based on parent and new name
1397
1382
1398 :param group_name:
1383 :param group_name:
1399 """
1384 """
1400 path_prefix = (self.parent_group.full_path_splitted if
1385 path_prefix = (self.parent_group.full_path_splitted if
1401 self.parent_group else [])
1386 self.parent_group else [])
1402 return RepoGroup.url_sep().join(path_prefix + [group_name])
1387 return RepoGroup.url_sep().join(path_prefix + [group_name])
1403
1388
1404
1389
1405 class Permission(Base, BaseModel):
1390 class Permission(Base, BaseModel):
1406 __tablename__ = 'permissions'
1391 __tablename__ = 'permissions'
1407 __table_args__ = (
1392 __table_args__ = (
1408 Index('p_perm_name_idx', 'permission_name'),
1393 Index('p_perm_name_idx', 'permission_name'),
1409 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1394 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1410 'mysql_charset': 'utf8'},
1395 'mysql_charset': 'utf8'},
1411 )
1396 )
1412 PERMS = [
1397 PERMS = [
1413 ('hg.admin', _('RhodeCode Administrator')),
1398 ('hg.admin', _('RhodeCode Administrator')),
1414
1399
1415 ('repository.none', _('Repository no access')),
1400 ('repository.none', _('Repository no access')),
1416 ('repository.read', _('Repository read access')),
1401 ('repository.read', _('Repository read access')),
1417 ('repository.write', _('Repository write access')),
1402 ('repository.write', _('Repository write access')),
1418 ('repository.admin', _('Repository admin access')),
1403 ('repository.admin', _('Repository admin access')),
1419
1404
1420 ('group.none', _('Repository group no access')),
1405 ('group.none', _('Repository group no access')),
1421 ('group.read', _('Repository group read access')),
1406 ('group.read', _('Repository group read access')),
1422 ('group.write', _('Repository group write access')),
1407 ('group.write', _('Repository group write access')),
1423 ('group.admin', _('Repository group admin access')),
1408 ('group.admin', _('Repository group admin access')),
1424
1409
1425 ('usergroup.none', _('User group no access')),
1410 ('usergroup.none', _('User group no access')),
1426 ('usergroup.read', _('User group read access')),
1411 ('usergroup.read', _('User group read access')),
1427 ('usergroup.write', _('User group write access')),
1412 ('usergroup.write', _('User group write access')),
1428 ('usergroup.admin', _('User group admin access')),
1413 ('usergroup.admin', _('User group admin access')),
1429
1414
1430 ('hg.repogroup.create.false', _('Repository Group creation disabled')),
1415 ('hg.repogroup.create.false', _('Repository Group creation disabled')),
1431 ('hg.repogroup.create.true', _('Repository Group creation enabled')),
1416 ('hg.repogroup.create.true', _('Repository Group creation enabled')),
1432
1417
1433 ('hg.usergroup.create.false', _('User Group creation disabled')),
1418 ('hg.usergroup.create.false', _('User Group creation disabled')),
1434 ('hg.usergroup.create.true', _('User Group creation enabled')),
1419 ('hg.usergroup.create.true', _('User Group creation enabled')),
1435
1420
1436 ('hg.create.none', _('Repository creation disabled')),
1421 ('hg.create.none', _('Repository creation disabled')),
1437 ('hg.create.repository', _('Repository creation enabled')),
1422 ('hg.create.repository', _('Repository creation enabled')),
1438
1423
1439 ('hg.fork.none', _('Repository forking disabled')),
1424 ('hg.fork.none', _('Repository forking disabled')),
1440 ('hg.fork.repository', _('Repository forking enabled')),
1425 ('hg.fork.repository', _('Repository forking enabled')),
1441
1426
1442 ('hg.register.none', _('Register disabled')),
1427 ('hg.register.none', _('Register disabled')),
1443 ('hg.register.manual_activate', _('Register new user with RhodeCode '
1428 ('hg.register.manual_activate', _('Register new user with RhodeCode '
1444 'with manual activation')),
1429 'with manual activation')),
1445
1430
1446 ('hg.register.auto_activate', _('Register new user with RhodeCode '
1431 ('hg.register.auto_activate', _('Register new user with RhodeCode '
1447 'with auto activation')),
1432 'with auto activation')),
1448 ]
1433 ]
1449
1434
1450 #definition of system default permissions for DEFAULT user
1435 #definition of system default permissions for DEFAULT user
1451 DEFAULT_USER_PERMISSIONS = [
1436 DEFAULT_USER_PERMISSIONS = [
1452 'repository.read',
1437 'repository.read',
1453 'group.read',
1438 'group.read',
1454 'usergroup.read',
1439 'usergroup.read',
1455 'hg.create.repository',
1440 'hg.create.repository',
1456 'hg.fork.repository',
1441 'hg.fork.repository',
1457 'hg.register.manual_activate',
1442 'hg.register.manual_activate',
1458 ]
1443 ]
1459
1444
1460 # defines which permissions are more important higher the more important
1445 # defines which permissions are more important higher the more important
1461 # Weight defines which permissions are more important.
1446 # Weight defines which permissions are more important.
1462 # The higher number the more important.
1447 # The higher number the more important.
1463 PERM_WEIGHTS = {
1448 PERM_WEIGHTS = {
1464 'repository.none': 0,
1449 'repository.none': 0,
1465 'repository.read': 1,
1450 'repository.read': 1,
1466 'repository.write': 3,
1451 'repository.write': 3,
1467 'repository.admin': 4,
1452 'repository.admin': 4,
1468
1453
1469 'group.none': 0,
1454 'group.none': 0,
1470 'group.read': 1,
1455 'group.read': 1,
1471 'group.write': 3,
1456 'group.write': 3,
1472 'group.admin': 4,
1457 'group.admin': 4,
1473
1458
1474 'usergroup.none': 0,
1459 'usergroup.none': 0,
1475 'usergroup.read': 1,
1460 'usergroup.read': 1,
1476 'usergroup.write': 3,
1461 'usergroup.write': 3,
1477 'usergroup.admin': 4,
1462 'usergroup.admin': 4,
1478 'hg.repogroup.create.false': 0,
1463 'hg.repogroup.create.false': 0,
1479 'hg.repogroup.create.true': 1,
1464 'hg.repogroup.create.true': 1,
1480
1465
1481 'hg.usergroup.create.false': 0,
1466 'hg.usergroup.create.false': 0,
1482 'hg.usergroup.create.true': 1,
1467 'hg.usergroup.create.true': 1,
1483
1468
1484 'hg.fork.none': 0,
1469 'hg.fork.none': 0,
1485 'hg.fork.repository': 1,
1470 'hg.fork.repository': 1,
1486 'hg.create.none': 0,
1471 'hg.create.none': 0,
1487 'hg.create.repository': 1
1472 'hg.create.repository': 1
1488 }
1473 }
1489
1474
1490 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1475 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1491 permission_name = Column("permission_name", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1476 permission_name = Column("permission_name", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1492 permission_longname = Column("permission_longname", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1477 permission_longname = Column("permission_longname", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1493
1478
1494 def __unicode__(self):
1479 def __unicode__(self):
1495 return u"<%s('%s:%s')>" % (
1480 return u"<%s('%s:%s')>" % (
1496 self.__class__.__name__, self.permission_id, self.permission_name
1481 self.__class__.__name__, self.permission_id, self.permission_name
1497 )
1482 )
1498
1483
1499 @classmethod
1484 @classmethod
1500 def get_by_key(cls, key):
1485 def get_by_key(cls, key):
1501 return cls.query().filter(cls.permission_name == key).scalar()
1486 return cls.query().filter(cls.permission_name == key).scalar()
1502
1487
1503 @classmethod
1488 @classmethod
1504 def get_default_perms(cls, default_user_id):
1489 def get_default_perms(cls, default_user_id):
1505 q = Session().query(UserRepoToPerm, Repository, cls)\
1490 q = Session().query(UserRepoToPerm, Repository, cls)\
1506 .join((Repository, UserRepoToPerm.repository_id == Repository.repo_id))\
1491 .join((Repository, UserRepoToPerm.repository_id == Repository.repo_id))\
1507 .join((cls, UserRepoToPerm.permission_id == cls.permission_id))\
1492 .join((cls, UserRepoToPerm.permission_id == cls.permission_id))\
1508 .filter(UserRepoToPerm.user_id == default_user_id)
1493 .filter(UserRepoToPerm.user_id == default_user_id)
1509
1494
1510 return q.all()
1495 return q.all()
1511
1496
1512 @classmethod
1497 @classmethod
1513 def get_default_group_perms(cls, default_user_id):
1498 def get_default_group_perms(cls, default_user_id):
1514 q = Session().query(UserRepoGroupToPerm, RepoGroup, cls)\
1499 q = Session().query(UserRepoGroupToPerm, RepoGroup, cls)\
1515 .join((RepoGroup, UserRepoGroupToPerm.group_id == RepoGroup.group_id))\
1500 .join((RepoGroup, UserRepoGroupToPerm.group_id == RepoGroup.group_id))\
1516 .join((cls, UserRepoGroupToPerm.permission_id == cls.permission_id))\
1501 .join((cls, UserRepoGroupToPerm.permission_id == cls.permission_id))\
1517 .filter(UserRepoGroupToPerm.user_id == default_user_id)
1502 .filter(UserRepoGroupToPerm.user_id == default_user_id)
1518
1503
1519 return q.all()
1504 return q.all()
1520
1505
1521 @classmethod
1506 @classmethod
1522 def get_default_user_group_perms(cls, default_user_id):
1507 def get_default_user_group_perms(cls, default_user_id):
1523 q = Session().query(UserUserGroupToPerm, UserGroup, cls)\
1508 q = Session().query(UserUserGroupToPerm, UserGroup, cls)\
1524 .join((UserGroup, UserUserGroupToPerm.user_group_id == UserGroup.users_group_id))\
1509 .join((UserGroup, UserUserGroupToPerm.user_group_id == UserGroup.users_group_id))\
1525 .join((cls, UserUserGroupToPerm.permission_id == cls.permission_id))\
1510 .join((cls, UserUserGroupToPerm.permission_id == cls.permission_id))\
1526 .filter(UserUserGroupToPerm.user_id == default_user_id)
1511 .filter(UserUserGroupToPerm.user_id == default_user_id)
1527
1512
1528 return q.all()
1513 return q.all()
1529
1514
1530
1515
1531 class UserRepoToPerm(Base, BaseModel):
1516 class UserRepoToPerm(Base, BaseModel):
1532 __tablename__ = 'repo_to_perm'
1517 __tablename__ = 'repo_to_perm'
1533 __table_args__ = (
1518 __table_args__ = (
1534 UniqueConstraint('user_id', 'repository_id', 'permission_id'),
1519 UniqueConstraint('user_id', 'repository_id', 'permission_id'),
1535 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1520 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1536 'mysql_charset': 'utf8'}
1521 'mysql_charset': 'utf8'}
1537 )
1522 )
1538 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1523 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1539 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1524 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1540 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1525 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1541 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
1526 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
1542
1527
1543 user = relationship('User')
1528 user = relationship('User')
1544 repository = relationship('Repository')
1529 repository = relationship('Repository')
1545 permission = relationship('Permission')
1530 permission = relationship('Permission')
1546
1531
1547 @classmethod
1532 @classmethod
1548 def create(cls, user, repository, permission):
1533 def create(cls, user, repository, permission):
1549 n = cls()
1534 n = cls()
1550 n.user = user
1535 n.user = user
1551 n.repository = repository
1536 n.repository = repository
1552 n.permission = permission
1537 n.permission = permission
1553 Session().add(n)
1538 Session().add(n)
1554 return n
1539 return n
1555
1540
1556 def __unicode__(self):
1541 def __unicode__(self):
1557 return u'<%s => %s >' % (self.user, self.repository)
1542 return u'<%s => %s >' % (self.user, self.repository)
1558
1543
1559
1544
1560 class UserUserGroupToPerm(Base, BaseModel):
1545 class UserUserGroupToPerm(Base, BaseModel):
1561 __tablename__ = 'user_user_group_to_perm'
1546 __tablename__ = 'user_user_group_to_perm'
1562 __table_args__ = (
1547 __table_args__ = (
1563 UniqueConstraint('user_id', 'user_group_id', 'permission_id'),
1548 UniqueConstraint('user_id', 'user_group_id', 'permission_id'),
1564 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1549 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1565 'mysql_charset': 'utf8'}
1550 'mysql_charset': 'utf8'}
1566 )
1551 )
1567 user_user_group_to_perm_id = Column("user_user_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1552 user_user_group_to_perm_id = Column("user_user_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1568 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1553 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1569 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1554 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1570 user_group_id = Column("user_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1555 user_group_id = Column("user_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1571
1556
1572 user = relationship('User')
1557 user = relationship('User')
1573 user_group = relationship('UserGroup')
1558 user_group = relationship('UserGroup')
1574 permission = relationship('Permission')
1559 permission = relationship('Permission')
1575
1560
1576 @classmethod
1561 @classmethod
1577 def create(cls, user, user_group, permission):
1562 def create(cls, user, user_group, permission):
1578 n = cls()
1563 n = cls()
1579 n.user = user
1564 n.user = user
1580 n.user_group = user_group
1565 n.user_group = user_group
1581 n.permission = permission
1566 n.permission = permission
1582 Session().add(n)
1567 Session().add(n)
1583 return n
1568 return n
1584
1569
1585 def __unicode__(self):
1570 def __unicode__(self):
1586 return u'<%s => %s >' % (self.user, self.user_group)
1571 return u'<%s => %s >' % (self.user, self.user_group)
1587
1572
1588
1573
1589 class UserToPerm(Base, BaseModel):
1574 class UserToPerm(Base, BaseModel):
1590 __tablename__ = 'user_to_perm'
1575 __tablename__ = 'user_to_perm'
1591 __table_args__ = (
1576 __table_args__ = (
1592 UniqueConstraint('user_id', 'permission_id'),
1577 UniqueConstraint('user_id', 'permission_id'),
1593 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1578 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1594 'mysql_charset': 'utf8'}
1579 'mysql_charset': 'utf8'}
1595 )
1580 )
1596 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1581 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1597 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1582 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1598 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1583 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1599
1584
1600 user = relationship('User')
1585 user = relationship('User')
1601 permission = relationship('Permission', lazy='joined')
1586 permission = relationship('Permission', lazy='joined')
1602
1587
1603 def __unicode__(self):
1588 def __unicode__(self):
1604 return u'<%s => %s >' % (self.user, self.permission)
1589 return u'<%s => %s >' % (self.user, self.permission)
1605
1590
1606
1591
1607 class UserGroupRepoToPerm(Base, BaseModel):
1592 class UserGroupRepoToPerm(Base, BaseModel):
1608 __tablename__ = 'users_group_repo_to_perm'
1593 __tablename__ = 'users_group_repo_to_perm'
1609 __table_args__ = (
1594 __table_args__ = (
1610 UniqueConstraint('repository_id', 'users_group_id', 'permission_id'),
1595 UniqueConstraint('repository_id', 'users_group_id', 'permission_id'),
1611 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1596 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1612 'mysql_charset': 'utf8'}
1597 'mysql_charset': 'utf8'}
1613 )
1598 )
1614 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1599 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1615 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1600 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1616 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1601 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1617 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
1602 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
1618
1603
1619 users_group = relationship('UserGroup')
1604 users_group = relationship('UserGroup')
1620 permission = relationship('Permission')
1605 permission = relationship('Permission')
1621 repository = relationship('Repository')
1606 repository = relationship('Repository')
1622
1607
1623 @classmethod
1608 @classmethod
1624 def create(cls, users_group, repository, permission):
1609 def create(cls, users_group, repository, permission):
1625 n = cls()
1610 n = cls()
1626 n.users_group = users_group
1611 n.users_group = users_group
1627 n.repository = repository
1612 n.repository = repository
1628 n.permission = permission
1613 n.permission = permission
1629 Session().add(n)
1614 Session().add(n)
1630 return n
1615 return n
1631
1616
1632 def __unicode__(self):
1617 def __unicode__(self):
1633 return u'<userGroup:%s => %s >' % (self.users_group, self.repository)
1618 return u'<userGroup:%s => %s >' % (self.users_group, self.repository)
1634
1619
1635
1620
1636 #TODO; not sure if this will be ever used
1621 #TODO; not sure if this will be ever used
1637 class UserGroupUserGroupToPerm(Base, BaseModel):
1622 class UserGroupUserGroupToPerm(Base, BaseModel):
1638 __tablename__ = 'user_group_user_group_to_perm'
1623 __tablename__ = 'user_group_user_group_to_perm'
1639 __table_args__ = (
1624 __table_args__ = (
1640 UniqueConstraint('user_group_id', 'user_group_id', 'permission_id'),
1625 UniqueConstraint('user_group_id', 'user_group_id', 'permission_id'),
1641 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1626 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1642 'mysql_charset': 'utf8'}
1627 'mysql_charset': 'utf8'}
1643 )
1628 )
1644 user_user_group_to_perm_id = Column("user_user_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1629 user_user_group_to_perm_id = Column("user_user_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1645 target_user_group_id = Column("target_users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1630 target_user_group_id = Column("target_users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1646 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1631 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1647 user_group_id = Column("user_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1632 user_group_id = Column("user_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1648
1633
1649 target_user_group = relationship('UserGroup', remote_side=target_user_group_id, primaryjoin='UserGroupUserGroupToPerm.target_user_group_id==UserGroup.users_group_id')
1634 target_user_group = relationship('UserGroup', remote_side=target_user_group_id, primaryjoin='UserGroupUserGroupToPerm.target_user_group_id==UserGroup.users_group_id')
1650 user_group = relationship('UserGroup', remote_side=user_group_id, primaryjoin='UserGroupUserGroupToPerm.user_group_id==UserGroup.users_group_id')
1635 user_group = relationship('UserGroup', remote_side=user_group_id, primaryjoin='UserGroupUserGroupToPerm.user_group_id==UserGroup.users_group_id')
1651 permission = relationship('Permission')
1636 permission = relationship('Permission')
1652
1637
1653 @classmethod
1638 @classmethod
1654 def create(cls, target_user_group, user_group, permission):
1639 def create(cls, target_user_group, user_group, permission):
1655 n = cls()
1640 n = cls()
1656 n.target_user_group = target_user_group
1641 n.target_user_group = target_user_group
1657 n.user_group = user_group
1642 n.user_group = user_group
1658 n.permission = permission
1643 n.permission = permission
1659 Session().add(n)
1644 Session().add(n)
1660 return n
1645 return n
1661
1646
1662 def __unicode__(self):
1647 def __unicode__(self):
1663 return u'<UserGroup:%s => %s >' % (self.target_user_group, self.user_group)
1648 return u'<UserGroup:%s => %s >' % (self.target_user_group, self.user_group)
1664
1649
1665
1650
1666 class UserGroupToPerm(Base, BaseModel):
1651 class UserGroupToPerm(Base, BaseModel):
1667 __tablename__ = 'users_group_to_perm'
1652 __tablename__ = 'users_group_to_perm'
1668 __table_args__ = (
1653 __table_args__ = (
1669 UniqueConstraint('users_group_id', 'permission_id',),
1654 UniqueConstraint('users_group_id', 'permission_id',),
1670 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1655 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1671 'mysql_charset': 'utf8'}
1656 'mysql_charset': 'utf8'}
1672 )
1657 )
1673 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1658 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1674 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1659 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1675 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1660 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1676
1661
1677 users_group = relationship('UserGroup')
1662 users_group = relationship('UserGroup')
1678 permission = relationship('Permission')
1663 permission = relationship('Permission')
1679
1664
1680
1665
1681 class UserRepoGroupToPerm(Base, BaseModel):
1666 class UserRepoGroupToPerm(Base, BaseModel):
1682 __tablename__ = 'user_repo_group_to_perm'
1667 __tablename__ = 'user_repo_group_to_perm'
1683 __table_args__ = (
1668 __table_args__ = (
1684 UniqueConstraint('user_id', 'group_id', 'permission_id'),
1669 UniqueConstraint('user_id', 'group_id', 'permission_id'),
1685 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1670 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1686 'mysql_charset': 'utf8'}
1671 'mysql_charset': 'utf8'}
1687 )
1672 )
1688
1673
1689 group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1674 group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1690 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1675 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1691 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
1676 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
1692 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1677 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1693
1678
1694 user = relationship('User')
1679 user = relationship('User')
1695 group = relationship('RepoGroup')
1680 group = relationship('RepoGroup')
1696 permission = relationship('Permission')
1681 permission = relationship('Permission')
1697
1682
1698
1683
1699 class UserGroupRepoGroupToPerm(Base, BaseModel):
1684 class UserGroupRepoGroupToPerm(Base, BaseModel):
1700 __tablename__ = 'users_group_repo_group_to_perm'
1685 __tablename__ = 'users_group_repo_group_to_perm'
1701 __table_args__ = (
1686 __table_args__ = (
1702 UniqueConstraint('users_group_id', 'group_id'),
1687 UniqueConstraint('users_group_id', 'group_id'),
1703 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1688 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1704 'mysql_charset': 'utf8'}
1689 'mysql_charset': 'utf8'}
1705 )
1690 )
1706
1691
1707 users_group_repo_group_to_perm_id = Column("users_group_repo_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1692 users_group_repo_group_to_perm_id = Column("users_group_repo_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1708 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1693 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1709 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
1694 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
1710 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1695 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1711
1696
1712 users_group = relationship('UserGroup')
1697 users_group = relationship('UserGroup')
1713 permission = relationship('Permission')
1698 permission = relationship('Permission')
1714 group = relationship('RepoGroup')
1699 group = relationship('RepoGroup')
1715
1700
1716
1701
1717 class Statistics(Base, BaseModel):
1702 class Statistics(Base, BaseModel):
1718 __tablename__ = 'statistics'
1703 __tablename__ = 'statistics'
1719 __table_args__ = (
1704 __table_args__ = (
1720 UniqueConstraint('repository_id'),
1705 UniqueConstraint('repository_id'),
1721 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1706 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1722 'mysql_charset': 'utf8'}
1707 'mysql_charset': 'utf8'}
1723 )
1708 )
1724 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1709 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1725 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
1710 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
1726 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
1711 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
1727 commit_activity = Column("commit_activity", LargeBinary(1000000), nullable=False)#JSON data
1712 commit_activity = Column("commit_activity", LargeBinary(1000000), nullable=False)#JSON data
1728 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
1713 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
1729 languages = Column("languages", LargeBinary(1000000), nullable=False)#JSON data
1714 languages = Column("languages", LargeBinary(1000000), nullable=False)#JSON data
1730
1715
1731 repository = relationship('Repository', single_parent=True)
1716 repository = relationship('Repository', single_parent=True)
1732
1717
1733
1718
1734 class UserFollowing(Base, BaseModel):
1719 class UserFollowing(Base, BaseModel):
1735 __tablename__ = 'user_followings'
1720 __tablename__ = 'user_followings'
1736 __table_args__ = (
1721 __table_args__ = (
1737 UniqueConstraint('user_id', 'follows_repository_id'),
1722 UniqueConstraint('user_id', 'follows_repository_id'),
1738 UniqueConstraint('user_id', 'follows_user_id'),
1723 UniqueConstraint('user_id', 'follows_user_id'),
1739 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1724 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1740 'mysql_charset': 'utf8'}
1725 'mysql_charset': 'utf8'}
1741 )
1726 )
1742
1727
1743 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1728 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1744 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1729 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1745 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
1730 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
1746 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
1731 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
1747 follows_from = Column('follows_from', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
1732 follows_from = Column('follows_from', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
1748
1733
1749 user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id')
1734 user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id')
1750
1735
1751 follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
1736 follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
1752 follows_repository = relationship('Repository', order_by='Repository.repo_name')
1737 follows_repository = relationship('Repository', order_by='Repository.repo_name')
1753
1738
1754 @classmethod
1739 @classmethod
1755 def get_repo_followers(cls, repo_id):
1740 def get_repo_followers(cls, repo_id):
1756 return cls.query().filter(cls.follows_repo_id == repo_id)
1741 return cls.query().filter(cls.follows_repo_id == repo_id)
1757
1742
1758
1743
1759 class CacheInvalidation(Base, BaseModel):
1744 class CacheInvalidation(Base, BaseModel):
1760 __tablename__ = 'cache_invalidation'
1745 __tablename__ = 'cache_invalidation'
1761 __table_args__ = (
1746 __table_args__ = (
1762 UniqueConstraint('cache_key'),
1747 UniqueConstraint('cache_key'),
1763 Index('key_idx', 'cache_key'),
1748 Index('key_idx', 'cache_key'),
1764 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1749 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1765 'mysql_charset': 'utf8'},
1750 'mysql_charset': 'utf8'},
1766 )
1751 )
1767 # cache_id, not used
1752 # cache_id, not used
1768 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1753 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1769 # cache_key as created by _get_cache_key
1754 # cache_key as created by _get_cache_key
1770 cache_key = Column("cache_key", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1755 cache_key = Column("cache_key", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1771 # cache_args is a repo_name
1756 # cache_args is a repo_name
1772 cache_args = Column("cache_args", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1757 cache_args = Column("cache_args", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1773 # instance sets cache_active True when it is caching,
1758 # instance sets cache_active True when it is caching,
1774 # other instances set cache_active to False to indicate that this cache is invalid
1759 # other instances set cache_active to False to indicate that this cache is invalid
1775 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
1760 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
1776
1761
1777 def __init__(self, cache_key, repo_name=''):
1762 def __init__(self, cache_key, repo_name=''):
1778 self.cache_key = cache_key
1763 self.cache_key = cache_key
1779 self.cache_args = repo_name
1764 self.cache_args = repo_name
1780 self.cache_active = False
1765 self.cache_active = False
1781
1766
1782 def __unicode__(self):
1767 def __unicode__(self):
1783 return u"<%s('%s:%s[%s]')>" % (self.__class__.__name__,
1768 return u"<%s('%s:%s[%s]')>" % (self.__class__.__name__,
1784 self.cache_id, self.cache_key, self.cache_active)
1769 self.cache_id, self.cache_key, self.cache_active)
1785
1770
1786 def _cache_key_partition(self):
1771 def _cache_key_partition(self):
1787 prefix, repo_name, suffix = self.cache_key.partition(self.cache_args)
1772 prefix, repo_name, suffix = self.cache_key.partition(self.cache_args)
1788 return prefix, repo_name, suffix
1773 return prefix, repo_name, suffix
1789
1774
1790 def get_prefix(self):
1775 def get_prefix(self):
1791 """
1776 """
1792 get prefix that might have been used in _get_cache_key to
1777 get prefix that might have been used in _get_cache_key to
1793 generate self.cache_key. Only used for informational purposes
1778 generate self.cache_key. Only used for informational purposes
1794 in repo_edit.html.
1779 in repo_edit.html.
1795 """
1780 """
1796 # prefix, repo_name, suffix
1781 # prefix, repo_name, suffix
1797 return self._cache_key_partition()[0]
1782 return self._cache_key_partition()[0]
1798
1783
1799 def get_suffix(self):
1784 def get_suffix(self):
1800 """
1785 """
1801 get suffix that might have been used in _get_cache_key to
1786 get suffix that might have been used in _get_cache_key to
1802 generate self.cache_key. Only used for informational purposes
1787 generate self.cache_key. Only used for informational purposes
1803 in repo_edit.html.
1788 in repo_edit.html.
1804 """
1789 """
1805 # prefix, repo_name, suffix
1790 # prefix, repo_name, suffix
1806 return self._cache_key_partition()[2]
1791 return self._cache_key_partition()[2]
1807
1792
1808 @classmethod
1793 @classmethod
1809 def clear_cache(cls):
1794 def clear_cache(cls):
1810 """
1795 """
1811 Delete all cache keys from database.
1796 Delete all cache keys from database.
1812 Should only be run when all instances are down and all entries thus stale.
1797 Should only be run when all instances are down and all entries thus stale.
1813 """
1798 """
1814 cls.query().delete()
1799 cls.query().delete()
1815 Session().commit()
1800 Session().commit()
1816
1801
1817 @classmethod
1802 @classmethod
1818 def _get_cache_key(cls, key):
1803 def _get_cache_key(cls, key):
1819 """
1804 """
1820 Wrapper for generating a unique cache key for this instance and "key".
1805 Wrapper for generating a unique cache key for this instance and "key".
1821 key must / will start with a repo_name which will be stored in .cache_args .
1806 key must / will start with a repo_name which will be stored in .cache_args .
1822 """
1807 """
1823 import rhodecode
1808 import rhodecode
1824 prefix = rhodecode.CONFIG.get('instance_id', '')
1809 prefix = rhodecode.CONFIG.get('instance_id', '')
1825 return "%s%s" % (prefix, key)
1810 return "%s%s" % (prefix, key)
1826
1811
1827 @classmethod
1812 @classmethod
1828 def invalidate(cls, key):
1829 """
1830 Returns Invalidation object if the local cache with the given key is invalid,
1831 None otherwise.
1832 """
1833 repo_name = key
1834 repo_name = remove_suffix(repo_name, '_README')
1835 repo_name = remove_suffix(repo_name, '_RSS')
1836 repo_name = remove_suffix(repo_name, '_ATOM')
1837
1838 cache_key = cls._get_cache_key(key)
1839 inv_obj = Session().query(cls).filter(cls.cache_key == cache_key).scalar()
1840 if not inv_obj:
1841 try:
1842 inv_obj = CacheInvalidation(cache_key, repo_name)
1843 Session().add(inv_obj)
1844 Session().commit()
1845 except Exception:
1846 log.error(traceback.format_exc())
1847 Session().rollback()
1848 return
1849
1850 if not inv_obj.cache_active:
1851 # `cache_active = False` means that this cache
1852 # no longer is valid
1853 return inv_obj
1854
1855 @classmethod
1856 def set_invalidate(cls, repo_name):
1813 def set_invalidate(cls, repo_name):
1857 """
1814 """
1858 Mark all caches of a repo as invalid in the database.
1815 Mark all caches of a repo as invalid in the database.
1859 """
1816 """
1860 inv_objs = Session().query(cls).filter(cls.cache_args == repo_name).all()
1817 inv_objs = Session().query(cls).filter(cls.cache_args == repo_name).all()
1861
1818
1862 try:
1819 try:
1863 for inv_obj in inv_objs:
1820 for inv_obj in inv_objs:
1864 log.debug('marking %s key for invalidation based on repo_name=%s'
1821 log.debug('marking %s key for invalidation based on repo_name=%s'
1865 % (inv_obj, safe_str(repo_name)))
1822 % (inv_obj, safe_str(repo_name)))
1866 inv_obj.cache_active = False
1823 inv_obj.cache_active = False
1867 Session().add(inv_obj)
1824 Session().add(inv_obj)
1868 Session().commit()
1825 Session().commit()
1869 except Exception:
1826 except Exception:
1870 log.error(traceback.format_exc())
1827 log.error(traceback.format_exc())
1871 Session().rollback()
1828 Session().rollback()
1872
1829
1873 @classmethod
1830 @classmethod
1874 def set_valid(cls, cache_key):
1831 def test_and_set_valid(cls, key, valid_cache_keys=None):
1832 """
1833 Mark this cache key as active and currently cached.
1834 Return True if the existing cache registration still was valid.
1835 Return False to indicate that it had been invalidated and caches should be refreshed.
1875 """
1836 """
1876 Mark this cache key as active and currently cached
1837 cache_key = cls._get_cache_key(key)
1877 """
1838
1839 if valid_cache_keys and cache_key in valid_cache_keys:
1840 return True
1841
1842 repo_name = key
1843 repo_name = remove_suffix(repo_name, '_README')
1844 repo_name = remove_suffix(repo_name, '_RSS')
1845 repo_name = remove_suffix(repo_name, '_ATOM')
1846
1847 try:
1878 inv_obj = cls.query().filter(cls.cache_key == cache_key).scalar()
1848 inv_obj = cls.query().filter(cls.cache_key == cache_key).scalar()
1849 if not inv_obj:
1850 inv_obj = CacheInvalidation(cache_key, repo_name)
1851 was_valid = inv_obj.cache_active
1879 inv_obj.cache_active = True
1852 inv_obj.cache_active = True
1880 Session().add(inv_obj)
1853 Session().add(inv_obj)
1881 Session().commit()
1854 Session().commit()
1855 return was_valid
1856 except Exception:
1857 log.error(traceback.format_exc())
1858 Session().rollback()
1859 return False
1882
1860
1883 @classmethod
1861 @classmethod
1884 def get_cache_map(cls):
1862 def get_valid_cache_keys(cls):
1885
1863 """
1886 class cachemapdict(dict):
1864 Return opaque object with information of which caches still are valid
1887
1865 and can be used without checking for invalidation.
1888 def __init__(self, *args, **kwargs):
1866 """
1889 self.fixkey = kwargs.pop('fixkey', False)
1867 return set(inv_obj.cache_key for inv_obj in cls.query().filter(cls.cache_active).all())
1890 super(cachemapdict, self).__init__(*args, **kwargs)
1891
1892 def __getattr__(self, name):
1893 cache_key = name
1894 if self.fixkey:
1895 cache_key = cls._get_cache_key(name)
1896 if cache_key in self.__dict__:
1897 return self.__dict__[cache_key]
1898 else:
1899 return self[cache_key]
1900
1901 def __getitem__(self, name):
1902 cache_key = name
1903 if self.fixkey:
1904 cache_key = cls._get_cache_key(name)
1905 try:
1906 return super(cachemapdict, self).__getitem__(cache_key)
1907 except KeyError:
1908 return None
1909
1910 cache_map = cachemapdict(fixkey=True)
1911 for obj in cls.query().all():
1912 cache_map[obj.cache_key] = cachemapdict(obj.get_dict())
1913 return cache_map
1914
1868
1915
1869
1916 class ChangesetComment(Base, BaseModel):
1870 class ChangesetComment(Base, BaseModel):
1917 __tablename__ = 'changeset_comments'
1871 __tablename__ = 'changeset_comments'
1918 __table_args__ = (
1872 __table_args__ = (
1919 Index('cc_revision_idx', 'revision'),
1873 Index('cc_revision_idx', 'revision'),
1920 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1874 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1921 'mysql_charset': 'utf8'},
1875 'mysql_charset': 'utf8'},
1922 )
1876 )
1923 comment_id = Column('comment_id', Integer(), nullable=False, primary_key=True)
1877 comment_id = Column('comment_id', Integer(), nullable=False, primary_key=True)
1924 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1878 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1925 revision = Column('revision', String(40), nullable=True)
1879 revision = Column('revision', String(40), nullable=True)
1926 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
1880 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
1927 line_no = Column('line_no', Unicode(10), nullable=True)
1881 line_no = Column('line_no', Unicode(10), nullable=True)
1928 hl_lines = Column('hl_lines', Unicode(512), nullable=True)
1882 hl_lines = Column('hl_lines', Unicode(512), nullable=True)
1929 f_path = Column('f_path', Unicode(1000), nullable=True)
1883 f_path = Column('f_path', Unicode(1000), nullable=True)
1930 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=False)
1884 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=False)
1931 text = Column('text', UnicodeText(25000), nullable=False)
1885 text = Column('text', UnicodeText(25000), nullable=False)
1932 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1886 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1933 modified_at = Column('modified_at', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1887 modified_at = Column('modified_at', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1934
1888
1935 author = relationship('User', lazy='joined')
1889 author = relationship('User', lazy='joined')
1936 repo = relationship('Repository')
1890 repo = relationship('Repository')
1937 status_change = relationship('ChangesetStatus', cascade="all, delete, delete-orphan")
1891 status_change = relationship('ChangesetStatus', cascade="all, delete, delete-orphan")
1938 pull_request = relationship('PullRequest', lazy='joined')
1892 pull_request = relationship('PullRequest', lazy='joined')
1939
1893
1940 @classmethod
1894 @classmethod
1941 def get_users(cls, revision=None, pull_request_id=None):
1895 def get_users(cls, revision=None, pull_request_id=None):
1942 """
1896 """
1943 Returns user associated with this ChangesetComment. ie those
1897 Returns user associated with this ChangesetComment. ie those
1944 who actually commented
1898 who actually commented
1945
1899
1946 :param cls:
1900 :param cls:
1947 :param revision:
1901 :param revision:
1948 """
1902 """
1949 q = Session().query(User)\
1903 q = Session().query(User)\
1950 .join(ChangesetComment.author)
1904 .join(ChangesetComment.author)
1951 if revision:
1905 if revision:
1952 q = q.filter(cls.revision == revision)
1906 q = q.filter(cls.revision == revision)
1953 elif pull_request_id:
1907 elif pull_request_id:
1954 q = q.filter(cls.pull_request_id == pull_request_id)
1908 q = q.filter(cls.pull_request_id == pull_request_id)
1955 return q.all()
1909 return q.all()
1956
1910
1957
1911
1958 class ChangesetStatus(Base, BaseModel):
1912 class ChangesetStatus(Base, BaseModel):
1959 __tablename__ = 'changeset_statuses'
1913 __tablename__ = 'changeset_statuses'
1960 __table_args__ = (
1914 __table_args__ = (
1961 Index('cs_revision_idx', 'revision'),
1915 Index('cs_revision_idx', 'revision'),
1962 Index('cs_version_idx', 'version'),
1916 Index('cs_version_idx', 'version'),
1963 UniqueConstraint('repo_id', 'revision', 'version'),
1917 UniqueConstraint('repo_id', 'revision', 'version'),
1964 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1918 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1965 'mysql_charset': 'utf8'}
1919 'mysql_charset': 'utf8'}
1966 )
1920 )
1967 STATUS_NOT_REVIEWED = DEFAULT = 'not_reviewed'
1921 STATUS_NOT_REVIEWED = DEFAULT = 'not_reviewed'
1968 STATUS_APPROVED = 'approved'
1922 STATUS_APPROVED = 'approved'
1969 STATUS_REJECTED = 'rejected'
1923 STATUS_REJECTED = 'rejected'
1970 STATUS_UNDER_REVIEW = 'under_review'
1924 STATUS_UNDER_REVIEW = 'under_review'
1971
1925
1972 STATUSES = [
1926 STATUSES = [
1973 (STATUS_NOT_REVIEWED, _("Not Reviewed")), # (no icon) and default
1927 (STATUS_NOT_REVIEWED, _("Not Reviewed")), # (no icon) and default
1974 (STATUS_APPROVED, _("Approved")),
1928 (STATUS_APPROVED, _("Approved")),
1975 (STATUS_REJECTED, _("Rejected")),
1929 (STATUS_REJECTED, _("Rejected")),
1976 (STATUS_UNDER_REVIEW, _("Under Review")),
1930 (STATUS_UNDER_REVIEW, _("Under Review")),
1977 ]
1931 ]
1978
1932
1979 changeset_status_id = Column('changeset_status_id', Integer(), nullable=False, primary_key=True)
1933 changeset_status_id = Column('changeset_status_id', Integer(), nullable=False, primary_key=True)
1980 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1934 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1981 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
1935 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
1982 revision = Column('revision', String(40), nullable=False)
1936 revision = Column('revision', String(40), nullable=False)
1983 status = Column('status', String(128), nullable=False, default=DEFAULT)
1937 status = Column('status', String(128), nullable=False, default=DEFAULT)
1984 changeset_comment_id = Column('changeset_comment_id', Integer(), ForeignKey('changeset_comments.comment_id'))
1938 changeset_comment_id = Column('changeset_comment_id', Integer(), ForeignKey('changeset_comments.comment_id'))
1985 modified_at = Column('modified_at', DateTime(), nullable=False, default=datetime.datetime.now)
1939 modified_at = Column('modified_at', DateTime(), nullable=False, default=datetime.datetime.now)
1986 version = Column('version', Integer(), nullable=False, default=0)
1940 version = Column('version', Integer(), nullable=False, default=0)
1987 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
1941 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
1988
1942
1989 author = relationship('User', lazy='joined')
1943 author = relationship('User', lazy='joined')
1990 repo = relationship('Repository')
1944 repo = relationship('Repository')
1991 comment = relationship('ChangesetComment', lazy='joined')
1945 comment = relationship('ChangesetComment', lazy='joined')
1992 pull_request = relationship('PullRequest', lazy='joined')
1946 pull_request = relationship('PullRequest', lazy='joined')
1993
1947
1994 def __unicode__(self):
1948 def __unicode__(self):
1995 return u"<%s('%s:%s')>" % (
1949 return u"<%s('%s:%s')>" % (
1996 self.__class__.__name__,
1950 self.__class__.__name__,
1997 self.status, self.author
1951 self.status, self.author
1998 )
1952 )
1999
1953
2000 @classmethod
1954 @classmethod
2001 def get_status_lbl(cls, value):
1955 def get_status_lbl(cls, value):
2002 return dict(cls.STATUSES).get(value)
1956 return dict(cls.STATUSES).get(value)
2003
1957
2004 @property
1958 @property
2005 def status_lbl(self):
1959 def status_lbl(self):
2006 return ChangesetStatus.get_status_lbl(self.status)
1960 return ChangesetStatus.get_status_lbl(self.status)
2007
1961
2008
1962
2009 class PullRequest(Base, BaseModel):
1963 class PullRequest(Base, BaseModel):
2010 __tablename__ = 'pull_requests'
1964 __tablename__ = 'pull_requests'
2011 __table_args__ = (
1965 __table_args__ = (
2012 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1966 {'extend_existing': True, 'mysql_engine': 'InnoDB',
2013 'mysql_charset': 'utf8'},
1967 'mysql_charset': 'utf8'},
2014 )
1968 )
2015
1969
2016 STATUS_NEW = u'new'
1970 STATUS_NEW = u'new'
2017 STATUS_OPEN = u'open'
1971 STATUS_OPEN = u'open'
2018 STATUS_CLOSED = u'closed'
1972 STATUS_CLOSED = u'closed'
2019
1973
2020 pull_request_id = Column('pull_request_id', Integer(), nullable=False, primary_key=True)
1974 pull_request_id = Column('pull_request_id', Integer(), nullable=False, primary_key=True)
2021 title = Column('title', Unicode(256), nullable=True)
1975 title = Column('title', Unicode(256), nullable=True)
2022 description = Column('description', UnicodeText(10240), nullable=True)
1976 description = Column('description', UnicodeText(10240), nullable=True)
2023 status = Column('status', Unicode(256), nullable=False, default=STATUS_NEW)
1977 status = Column('status', Unicode(256), nullable=False, default=STATUS_NEW)
2024 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1978 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
2025 updated_on = Column('updated_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1979 updated_on = Column('updated_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
2026 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
1980 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
2027 _revisions = Column('revisions', UnicodeText(20500)) # 500 revisions max
1981 _revisions = Column('revisions', UnicodeText(20500)) # 500 revisions max
2028 org_repo_id = Column('org_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1982 org_repo_id = Column('org_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
2029 org_ref = Column('org_ref', Unicode(256), nullable=False)
1983 org_ref = Column('org_ref', Unicode(256), nullable=False)
2030 other_repo_id = Column('other_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1984 other_repo_id = Column('other_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
2031 other_ref = Column('other_ref', Unicode(256), nullable=False)
1985 other_ref = Column('other_ref', Unicode(256), nullable=False)
2032
1986
2033 @hybrid_property
1987 @hybrid_property
2034 def revisions(self):
1988 def revisions(self):
2035 return self._revisions.split(':')
1989 return self._revisions.split(':')
2036
1990
2037 @revisions.setter
1991 @revisions.setter
2038 def revisions(self, val):
1992 def revisions(self, val):
2039 self._revisions = ':'.join(val)
1993 self._revisions = ':'.join(val)
2040
1994
2041 @property
1995 @property
2042 def org_ref_parts(self):
1996 def org_ref_parts(self):
2043 return self.org_ref.split(':')
1997 return self.org_ref.split(':')
2044
1998
2045 @property
1999 @property
2046 def other_ref_parts(self):
2000 def other_ref_parts(self):
2047 return self.other_ref.split(':')
2001 return self.other_ref.split(':')
2048
2002
2049 author = relationship('User', lazy='joined')
2003 author = relationship('User', lazy='joined')
2050 reviewers = relationship('PullRequestReviewers',
2004 reviewers = relationship('PullRequestReviewers',
2051 cascade="all, delete, delete-orphan")
2005 cascade="all, delete, delete-orphan")
2052 org_repo = relationship('Repository', primaryjoin='PullRequest.org_repo_id==Repository.repo_id')
2006 org_repo = relationship('Repository', primaryjoin='PullRequest.org_repo_id==Repository.repo_id')
2053 other_repo = relationship('Repository', primaryjoin='PullRequest.other_repo_id==Repository.repo_id')
2007 other_repo = relationship('Repository', primaryjoin='PullRequest.other_repo_id==Repository.repo_id')
2054 statuses = relationship('ChangesetStatus')
2008 statuses = relationship('ChangesetStatus')
2055 comments = relationship('ChangesetComment',
2009 comments = relationship('ChangesetComment',
2056 cascade="all, delete, delete-orphan")
2010 cascade="all, delete, delete-orphan")
2057
2011
2058 def is_closed(self):
2012 def is_closed(self):
2059 return self.status == self.STATUS_CLOSED
2013 return self.status == self.STATUS_CLOSED
2060
2014
2061 @property
2015 @property
2062 def last_review_status(self):
2016 def last_review_status(self):
2063 return self.statuses[-1].status if self.statuses else ''
2017 return self.statuses[-1].status if self.statuses else ''
2064
2018
2065 def __json__(self):
2019 def __json__(self):
2066 return dict(
2020 return dict(
2067 revisions=self.revisions
2021 revisions=self.revisions
2068 )
2022 )
2069
2023
2070
2024
2071 class PullRequestReviewers(Base, BaseModel):
2025 class PullRequestReviewers(Base, BaseModel):
2072 __tablename__ = 'pull_request_reviewers'
2026 __tablename__ = 'pull_request_reviewers'
2073 __table_args__ = (
2027 __table_args__ = (
2074 {'extend_existing': True, 'mysql_engine': 'InnoDB',
2028 {'extend_existing': True, 'mysql_engine': 'InnoDB',
2075 'mysql_charset': 'utf8'},
2029 'mysql_charset': 'utf8'},
2076 )
2030 )
2077
2031
2078 def __init__(self, user=None, pull_request=None):
2032 def __init__(self, user=None, pull_request=None):
2079 self.user = user
2033 self.user = user
2080 self.pull_request = pull_request
2034 self.pull_request = pull_request
2081
2035
2082 pull_requests_reviewers_id = Column('pull_requests_reviewers_id', Integer(), nullable=False, primary_key=True)
2036 pull_requests_reviewers_id = Column('pull_requests_reviewers_id', Integer(), nullable=False, primary_key=True)
2083 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=False)
2037 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=False)
2084 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True)
2038 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True)
2085
2039
2086 user = relationship('User')
2040 user = relationship('User')
2087 pull_request = relationship('PullRequest')
2041 pull_request = relationship('PullRequest')
2088
2042
2089
2043
2090 class Notification(Base, BaseModel):
2044 class Notification(Base, BaseModel):
2091 __tablename__ = 'notifications'
2045 __tablename__ = 'notifications'
2092 __table_args__ = (
2046 __table_args__ = (
2093 Index('notification_type_idx', 'type'),
2047 Index('notification_type_idx', 'type'),
2094 {'extend_existing': True, 'mysql_engine': 'InnoDB',
2048 {'extend_existing': True, 'mysql_engine': 'InnoDB',
2095 'mysql_charset': 'utf8'},
2049 'mysql_charset': 'utf8'},
2096 )
2050 )
2097
2051
2098 TYPE_CHANGESET_COMMENT = u'cs_comment'
2052 TYPE_CHANGESET_COMMENT = u'cs_comment'
2099 TYPE_MESSAGE = u'message'
2053 TYPE_MESSAGE = u'message'
2100 TYPE_MENTION = u'mention'
2054 TYPE_MENTION = u'mention'
2101 TYPE_REGISTRATION = u'registration'
2055 TYPE_REGISTRATION = u'registration'
2102 TYPE_PULL_REQUEST = u'pull_request'
2056 TYPE_PULL_REQUEST = u'pull_request'
2103 TYPE_PULL_REQUEST_COMMENT = u'pull_request_comment'
2057 TYPE_PULL_REQUEST_COMMENT = u'pull_request_comment'
2104
2058
2105 notification_id = Column('notification_id', Integer(), nullable=False, primary_key=True)
2059 notification_id = Column('notification_id', Integer(), nullable=False, primary_key=True)
2106 subject = Column('subject', Unicode(512), nullable=True)
2060 subject = Column('subject', Unicode(512), nullable=True)
2107 body = Column('body', UnicodeText(50000), nullable=True)
2061 body = Column('body', UnicodeText(50000), nullable=True)
2108 created_by = Column("created_by", Integer(), ForeignKey('users.user_id'), nullable=True)
2062 created_by = Column("created_by", Integer(), ForeignKey('users.user_id'), nullable=True)
2109 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
2063 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
2110 type_ = Column('type', Unicode(256))
2064 type_ = Column('type', Unicode(256))
2111
2065
2112 created_by_user = relationship('User')
2066 created_by_user = relationship('User')
2113 notifications_to_users = relationship('UserNotification', lazy='joined',
2067 notifications_to_users = relationship('UserNotification', lazy='joined',
2114 cascade="all, delete, delete-orphan")
2068 cascade="all, delete, delete-orphan")
2115
2069
2116 @property
2070 @property
2117 def recipients(self):
2071 def recipients(self):
2118 return [x.user for x in UserNotification.query()\
2072 return [x.user for x in UserNotification.query()\
2119 .filter(UserNotification.notification == self)\
2073 .filter(UserNotification.notification == self)\
2120 .order_by(UserNotification.user_id.asc()).all()]
2074 .order_by(UserNotification.user_id.asc()).all()]
2121
2075
2122 @classmethod
2076 @classmethod
2123 def create(cls, created_by, subject, body, recipients, type_=None):
2077 def create(cls, created_by, subject, body, recipients, type_=None):
2124 if type_ is None:
2078 if type_ is None:
2125 type_ = Notification.TYPE_MESSAGE
2079 type_ = Notification.TYPE_MESSAGE
2126
2080
2127 notification = cls()
2081 notification = cls()
2128 notification.created_by_user = created_by
2082 notification.created_by_user = created_by
2129 notification.subject = subject
2083 notification.subject = subject
2130 notification.body = body
2084 notification.body = body
2131 notification.type_ = type_
2085 notification.type_ = type_
2132 notification.created_on = datetime.datetime.now()
2086 notification.created_on = datetime.datetime.now()
2133
2087
2134 for u in recipients:
2088 for u in recipients:
2135 assoc = UserNotification()
2089 assoc = UserNotification()
2136 assoc.notification = notification
2090 assoc.notification = notification
2137 u.notifications.append(assoc)
2091 u.notifications.append(assoc)
2138 Session().add(notification)
2092 Session().add(notification)
2139 return notification
2093 return notification
2140
2094
2141 @property
2095 @property
2142 def description(self):
2096 def description(self):
2143 from rhodecode.model.notification import NotificationModel
2097 from rhodecode.model.notification import NotificationModel
2144 return NotificationModel().make_description(self)
2098 return NotificationModel().make_description(self)
2145
2099
2146
2100
2147 class UserNotification(Base, BaseModel):
2101 class UserNotification(Base, BaseModel):
2148 __tablename__ = 'user_to_notification'
2102 __tablename__ = 'user_to_notification'
2149 __table_args__ = (
2103 __table_args__ = (
2150 UniqueConstraint('user_id', 'notification_id'),
2104 UniqueConstraint('user_id', 'notification_id'),
2151 {'extend_existing': True, 'mysql_engine': 'InnoDB',
2105 {'extend_existing': True, 'mysql_engine': 'InnoDB',
2152 'mysql_charset': 'utf8'}
2106 'mysql_charset': 'utf8'}
2153 )
2107 )
2154 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), primary_key=True)
2108 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), primary_key=True)
2155 notification_id = Column("notification_id", Integer(), ForeignKey('notifications.notification_id'), primary_key=True)
2109 notification_id = Column("notification_id", Integer(), ForeignKey('notifications.notification_id'), primary_key=True)
2156 read = Column('read', Boolean, default=False)
2110 read = Column('read', Boolean, default=False)
2157 sent_on = Column('sent_on', DateTime(timezone=False), nullable=True, unique=None)
2111 sent_on = Column('sent_on', DateTime(timezone=False), nullable=True, unique=None)
2158
2112
2159 user = relationship('User', lazy="joined")
2113 user = relationship('User', lazy="joined")
2160 notification = relationship('Notification', lazy="joined",
2114 notification = relationship('Notification', lazy="joined",
2161 order_by=lambda: Notification.created_on.desc(),)
2115 order_by=lambda: Notification.created_on.desc(),)
2162
2116
2163 def mark_as_read(self):
2117 def mark_as_read(self):
2164 self.read = True
2118 self.read = True
2165 Session().add(self)
2119 Session().add(self)
2166
2120
2167
2121
2168 class DbMigrateVersion(Base, BaseModel):
2122 class DbMigrateVersion(Base, BaseModel):
2169 __tablename__ = 'db_migrate_version'
2123 __tablename__ = 'db_migrate_version'
2170 __table_args__ = (
2124 __table_args__ = (
2171 {'extend_existing': True, 'mysql_engine': 'InnoDB',
2125 {'extend_existing': True, 'mysql_engine': 'InnoDB',
2172 'mysql_charset': 'utf8'},
2126 'mysql_charset': 'utf8'},
2173 )
2127 )
2174 repository_id = Column('repository_id', String(250), primary_key=True)
2128 repository_id = Column('repository_id', String(250), primary_key=True)
2175 repository_path = Column('repository_path', Text)
2129 repository_path = Column('repository_path', Text)
2176 version = Column('version', Integer)
2130 version = Column('version', Integer)
@@ -1,693 +1,693 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.model.scm
3 rhodecode.model.scm
4 ~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~
5
5
6 Scm model for RhodeCode
6 Scm model for RhodeCode
7
7
8 :created_on: Apr 9, 2010
8 :created_on: Apr 9, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2010-2012 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 modify
13 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 3 of the License, or
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 from __future__ import with_statement
25 from __future__ import with_statement
26 import os
26 import os
27 import re
27 import re
28 import time
28 import time
29 import traceback
29 import traceback
30 import logging
30 import logging
31 import cStringIO
31 import cStringIO
32 import pkg_resources
32 import pkg_resources
33 from os.path import dirname as dn, join as jn
33 from os.path import dirname as dn, join as jn
34
34
35 from sqlalchemy import func
35 from sqlalchemy import func
36 from pylons.i18n.translation import _
36 from pylons.i18n.translation import _
37
37
38 import rhodecode
38 import rhodecode
39 from rhodecode.lib.vcs import get_backend
39 from rhodecode.lib.vcs import get_backend
40 from rhodecode.lib.vcs.exceptions import RepositoryError
40 from rhodecode.lib.vcs.exceptions import RepositoryError
41 from rhodecode.lib.vcs.utils.lazy import LazyProperty
41 from rhodecode.lib.vcs.utils.lazy import LazyProperty
42 from rhodecode.lib.vcs.nodes import FileNode
42 from rhodecode.lib.vcs.nodes import FileNode
43 from rhodecode.lib.vcs.backends.base import EmptyChangeset
43 from rhodecode.lib.vcs.backends.base import EmptyChangeset
44
44
45 from rhodecode import BACKENDS
45 from rhodecode import BACKENDS
46 from rhodecode.lib import helpers as h
46 from rhodecode.lib import helpers as h
47 from rhodecode.lib.utils2 import safe_str, safe_unicode, get_server_url,\
47 from rhodecode.lib.utils2 import safe_str, safe_unicode, get_server_url,\
48 _set_extras
48 _set_extras
49 from rhodecode.lib.auth import HasRepoPermissionAny, HasReposGroupPermissionAny,\
49 from rhodecode.lib.auth import HasRepoPermissionAny, HasReposGroupPermissionAny,\
50 HasUserGroupPermissionAnyDecorator, HasUserGroupPermissionAny
50 HasUserGroupPermissionAnyDecorator, HasUserGroupPermissionAny
51 from rhodecode.lib.utils import get_filesystem_repos, make_ui, \
51 from rhodecode.lib.utils import get_filesystem_repos, make_ui, \
52 action_logger, REMOVED_REPO_PAT
52 action_logger, REMOVED_REPO_PAT
53 from rhodecode.model import BaseModel
53 from rhodecode.model import BaseModel
54 from rhodecode.model.db import Repository, RhodeCodeUi, CacheInvalidation, \
54 from rhodecode.model.db import Repository, RhodeCodeUi, CacheInvalidation, \
55 UserFollowing, UserLog, User, RepoGroup, PullRequest
55 UserFollowing, UserLog, User, RepoGroup, PullRequest
56 from rhodecode.lib.hooks import log_push_action
56 from rhodecode.lib.hooks import log_push_action
57
57
58 log = logging.getLogger(__name__)
58 log = logging.getLogger(__name__)
59
59
60
60
61 class UserTemp(object):
61 class UserTemp(object):
62 def __init__(self, user_id):
62 def __init__(self, user_id):
63 self.user_id = user_id
63 self.user_id = user_id
64
64
65 def __repr__(self):
65 def __repr__(self):
66 return "<%s('id:%s')>" % (self.__class__.__name__, self.user_id)
66 return "<%s('id:%s')>" % (self.__class__.__name__, self.user_id)
67
67
68
68
69 class RepoTemp(object):
69 class RepoTemp(object):
70 def __init__(self, repo_id):
70 def __init__(self, repo_id):
71 self.repo_id = repo_id
71 self.repo_id = repo_id
72
72
73 def __repr__(self):
73 def __repr__(self):
74 return "<%s('id:%s')>" % (self.__class__.__name__, self.repo_id)
74 return "<%s('id:%s')>" % (self.__class__.__name__, self.repo_id)
75
75
76
76
77 class CachedRepoList(object):
77 class CachedRepoList(object):
78 """
78 """
79 Cached repo list, uses in-memory cache after initialization, that is
79 Cached repo list, uses in-memory cache after initialization, that is
80 super fast
80 super fast
81 """
81 """
82
82
83 def __init__(self, db_repo_list, repos_path, order_by=None, perm_set=None):
83 def __init__(self, db_repo_list, repos_path, order_by=None, perm_set=None):
84 self.db_repo_list = db_repo_list
84 self.db_repo_list = db_repo_list
85 self.repos_path = repos_path
85 self.repos_path = repos_path
86 self.order_by = order_by
86 self.order_by = order_by
87 self.reversed = (order_by or '').startswith('-')
87 self.reversed = (order_by or '').startswith('-')
88 if not perm_set:
88 if not perm_set:
89 perm_set = ['repository.read', 'repository.write',
89 perm_set = ['repository.read', 'repository.write',
90 'repository.admin']
90 'repository.admin']
91 self.perm_set = perm_set
91 self.perm_set = perm_set
92
92
93 def __len__(self):
93 def __len__(self):
94 return len(self.db_repo_list)
94 return len(self.db_repo_list)
95
95
96 def __repr__(self):
96 def __repr__(self):
97 return '<%s (%s)>' % (self.__class__.__name__, self.__len__())
97 return '<%s (%s)>' % (self.__class__.__name__, self.__len__())
98
98
99 def __iter__(self):
99 def __iter__(self):
100 # pre-propagated cache_map to save executing select statements
100 # pre-propagated valid_cache_keys to save executing select statements
101 # for each repo
101 # for each repo
102 cache_map = CacheInvalidation.get_cache_map()
102 valid_cache_keys = CacheInvalidation.get_valid_cache_keys()
103
103
104 for dbr in self.db_repo_list:
104 for dbr in self.db_repo_list:
105 scmr = dbr.scm_instance_cached(cache_map)
105 scmr = dbr.scm_instance_cached(valid_cache_keys)
106 # check permission at this level
106 # check permission at this level
107 if not HasRepoPermissionAny(
107 if not HasRepoPermissionAny(
108 *self.perm_set)(dbr.repo_name, 'get repo check'):
108 *self.perm_set)(dbr.repo_name, 'get repo check'):
109 continue
109 continue
110
110
111 try:
111 try:
112 last_change = scmr.last_change
112 last_change = scmr.last_change
113 tip = h.get_changeset_safe(scmr, 'tip')
113 tip = h.get_changeset_safe(scmr, 'tip')
114 except Exception:
114 except Exception:
115 log.error(
115 log.error(
116 '%s this repository is present in database but it '
116 '%s this repository is present in database but it '
117 'cannot be created as an scm instance, org_exc:%s'
117 'cannot be created as an scm instance, org_exc:%s'
118 % (dbr.repo_name, traceback.format_exc())
118 % (dbr.repo_name, traceback.format_exc())
119 )
119 )
120 continue
120 continue
121
121
122 tmp_d = {}
122 tmp_d = {}
123 tmp_d['name'] = dbr.repo_name
123 tmp_d['name'] = dbr.repo_name
124 tmp_d['name_sort'] = tmp_d['name'].lower()
124 tmp_d['name_sort'] = tmp_d['name'].lower()
125 tmp_d['raw_name'] = tmp_d['name'].lower()
125 tmp_d['raw_name'] = tmp_d['name'].lower()
126 tmp_d['description'] = dbr.description
126 tmp_d['description'] = dbr.description
127 tmp_d['description_sort'] = tmp_d['description'].lower()
127 tmp_d['description_sort'] = tmp_d['description'].lower()
128 tmp_d['last_change'] = last_change
128 tmp_d['last_change'] = last_change
129 tmp_d['last_change_sort'] = time.mktime(last_change.timetuple())
129 tmp_d['last_change_sort'] = time.mktime(last_change.timetuple())
130 tmp_d['tip'] = tip.raw_id
130 tmp_d['tip'] = tip.raw_id
131 tmp_d['tip_sort'] = tip.revision
131 tmp_d['tip_sort'] = tip.revision
132 tmp_d['rev'] = tip.revision
132 tmp_d['rev'] = tip.revision
133 tmp_d['contact'] = dbr.user.full_contact
133 tmp_d['contact'] = dbr.user.full_contact
134 tmp_d['contact_sort'] = tmp_d['contact']
134 tmp_d['contact_sort'] = tmp_d['contact']
135 tmp_d['owner_sort'] = tmp_d['contact']
135 tmp_d['owner_sort'] = tmp_d['contact']
136 tmp_d['repo_archives'] = list(scmr._get_archives())
136 tmp_d['repo_archives'] = list(scmr._get_archives())
137 tmp_d['last_msg'] = tip.message
137 tmp_d['last_msg'] = tip.message
138 tmp_d['author'] = tip.author
138 tmp_d['author'] = tip.author
139 tmp_d['dbrepo'] = dbr.get_dict()
139 tmp_d['dbrepo'] = dbr.get_dict()
140 tmp_d['dbrepo_fork'] = dbr.fork.get_dict() if dbr.fork else {}
140 tmp_d['dbrepo_fork'] = dbr.fork.get_dict() if dbr.fork else {}
141 yield tmp_d
141 yield tmp_d
142
142
143
143
144 class SimpleCachedRepoList(CachedRepoList):
144 class SimpleCachedRepoList(CachedRepoList):
145 """
145 """
146 Lighter version of CachedRepoList without the scm initialisation
146 Lighter version of CachedRepoList without the scm initialisation
147 """
147 """
148
148
149 def __iter__(self):
149 def __iter__(self):
150 for dbr in self.db_repo_list:
150 for dbr in self.db_repo_list:
151 # check permission at this level
151 # check permission at this level
152 if not HasRepoPermissionAny(
152 if not HasRepoPermissionAny(
153 *self.perm_set)(dbr.repo_name, 'get repo check'):
153 *self.perm_set)(dbr.repo_name, 'get repo check'):
154 continue
154 continue
155
155
156 tmp_d = {}
156 tmp_d = {}
157 tmp_d['name'] = dbr.repo_name
157 tmp_d['name'] = dbr.repo_name
158 tmp_d['name_sort'] = tmp_d['name'].lower()
158 tmp_d['name_sort'] = tmp_d['name'].lower()
159 tmp_d['raw_name'] = tmp_d['name'].lower()
159 tmp_d['raw_name'] = tmp_d['name'].lower()
160 tmp_d['description'] = dbr.description
160 tmp_d['description'] = dbr.description
161 tmp_d['description_sort'] = tmp_d['description'].lower()
161 tmp_d['description_sort'] = tmp_d['description'].lower()
162 tmp_d['dbrepo'] = dbr.get_dict()
162 tmp_d['dbrepo'] = dbr.get_dict()
163 tmp_d['dbrepo_fork'] = dbr.fork.get_dict() if dbr.fork else {}
163 tmp_d['dbrepo_fork'] = dbr.fork.get_dict() if dbr.fork else {}
164 yield tmp_d
164 yield tmp_d
165
165
166
166
167 class _PermCheckIterator(object):
167 class _PermCheckIterator(object):
168 def __init__(self, obj_list, obj_attr, perm_set, perm_checker):
168 def __init__(self, obj_list, obj_attr, perm_set, perm_checker):
169 """
169 """
170 Creates iterator from given list of objects, additionally
170 Creates iterator from given list of objects, additionally
171 checking permission for them from perm_set var
171 checking permission for them from perm_set var
172
172
173 :param obj_list: list of db objects
173 :param obj_list: list of db objects
174 :param obj_attr: attribute of object to pass into perm_checker
174 :param obj_attr: attribute of object to pass into perm_checker
175 :param perm_set: list of permissions to check
175 :param perm_set: list of permissions to check
176 :param perm_checker: callable to check permissions against
176 :param perm_checker: callable to check permissions against
177 """
177 """
178 self.obj_list = obj_list
178 self.obj_list = obj_list
179 self.obj_attr = obj_attr
179 self.obj_attr = obj_attr
180 self.perm_set = perm_set
180 self.perm_set = perm_set
181 self.perm_checker = perm_checker
181 self.perm_checker = perm_checker
182
182
183 def __len__(self):
183 def __len__(self):
184 return len(self.obj_list)
184 return len(self.obj_list)
185
185
186 def __repr__(self):
186 def __repr__(self):
187 return '<%s (%s)>' % (self.__class__.__name__, self.__len__())
187 return '<%s (%s)>' % (self.__class__.__name__, self.__len__())
188
188
189 def __iter__(self):
189 def __iter__(self):
190 for db_obj in self.obj_list:
190 for db_obj in self.obj_list:
191 # check permission at this level
191 # check permission at this level
192 name = getattr(db_obj, self.obj_attr, None)
192 name = getattr(db_obj, self.obj_attr, None)
193 if not self.perm_checker(*self.perm_set)(name, self.__class__.__name__):
193 if not self.perm_checker(*self.perm_set)(name, self.__class__.__name__):
194 continue
194 continue
195
195
196 yield db_obj
196 yield db_obj
197
197
198
198
199 class RepoGroupList(_PermCheckIterator):
199 class RepoGroupList(_PermCheckIterator):
200
200
201 def __init__(self, db_repo_group_list, perm_set=None):
201 def __init__(self, db_repo_group_list, perm_set=None):
202 if not perm_set:
202 if not perm_set:
203 perm_set = ['group.read', 'group.write', 'group.admin']
203 perm_set = ['group.read', 'group.write', 'group.admin']
204
204
205 super(RepoGroupList, self).__init__(obj_list=db_repo_group_list,
205 super(RepoGroupList, self).__init__(obj_list=db_repo_group_list,
206 obj_attr='group_name', perm_set=perm_set,
206 obj_attr='group_name', perm_set=perm_set,
207 perm_checker=HasReposGroupPermissionAny)
207 perm_checker=HasReposGroupPermissionAny)
208
208
209
209
210 class UserGroupList(_PermCheckIterator):
210 class UserGroupList(_PermCheckIterator):
211
211
212 def __init__(self, db_user_group_list, perm_set=None):
212 def __init__(self, db_user_group_list, perm_set=None):
213 if not perm_set:
213 if not perm_set:
214 perm_set = ['usergroup.read', 'usergroup.write', 'usergroup.admin']
214 perm_set = ['usergroup.read', 'usergroup.write', 'usergroup.admin']
215
215
216 super(UserGroupList, self).__init__(obj_list=db_user_group_list,
216 super(UserGroupList, self).__init__(obj_list=db_user_group_list,
217 obj_attr='users_group_name', perm_set=perm_set,
217 obj_attr='users_group_name', perm_set=perm_set,
218 perm_checker=HasUserGroupPermissionAny)
218 perm_checker=HasUserGroupPermissionAny)
219
219
220
220
221 class ScmModel(BaseModel):
221 class ScmModel(BaseModel):
222 """
222 """
223 Generic Scm Model
223 Generic Scm Model
224 """
224 """
225
225
226 def __get_repo(self, instance):
226 def __get_repo(self, instance):
227 cls = Repository
227 cls = Repository
228 if isinstance(instance, cls):
228 if isinstance(instance, cls):
229 return instance
229 return instance
230 elif isinstance(instance, int) or safe_str(instance).isdigit():
230 elif isinstance(instance, int) or safe_str(instance).isdigit():
231 return cls.get(instance)
231 return cls.get(instance)
232 elif isinstance(instance, basestring):
232 elif isinstance(instance, basestring):
233 return cls.get_by_repo_name(instance)
233 return cls.get_by_repo_name(instance)
234 elif instance:
234 elif instance:
235 raise Exception('given object must be int, basestr or Instance'
235 raise Exception('given object must be int, basestr or Instance'
236 ' of %s got %s' % (type(cls), type(instance)))
236 ' of %s got %s' % (type(cls), type(instance)))
237
237
238 @LazyProperty
238 @LazyProperty
239 def repos_path(self):
239 def repos_path(self):
240 """
240 """
241 Get's the repositories root path from database
241 Get's the repositories root path from database
242 """
242 """
243
243
244 q = self.sa.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key == '/').one()
244 q = self.sa.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key == '/').one()
245
245
246 return q.ui_value
246 return q.ui_value
247
247
248 def repo_scan(self, repos_path=None):
248 def repo_scan(self, repos_path=None):
249 """
249 """
250 Listing of repositories in given path. This path should not be a
250 Listing of repositories in given path. This path should not be a
251 repository itself. Return a dictionary of repository objects
251 repository itself. Return a dictionary of repository objects
252
252
253 :param repos_path: path to directory containing repositories
253 :param repos_path: path to directory containing repositories
254 """
254 """
255
255
256 if repos_path is None:
256 if repos_path is None:
257 repos_path = self.repos_path
257 repos_path = self.repos_path
258
258
259 log.info('scanning for repositories in %s' % repos_path)
259 log.info('scanning for repositories in %s' % repos_path)
260
260
261 baseui = make_ui('db')
261 baseui = make_ui('db')
262 repos = {}
262 repos = {}
263
263
264 for name, path in get_filesystem_repos(repos_path, recursive=True):
264 for name, path in get_filesystem_repos(repos_path, recursive=True):
265 # name need to be decomposed and put back together using the /
265 # name need to be decomposed and put back together using the /
266 # since this is internal storage separator for rhodecode
266 # since this is internal storage separator for rhodecode
267 name = Repository.normalize_repo_name(name)
267 name = Repository.normalize_repo_name(name)
268
268
269 try:
269 try:
270 if name in repos:
270 if name in repos:
271 raise RepositoryError('Duplicate repository name %s '
271 raise RepositoryError('Duplicate repository name %s '
272 'found in %s' % (name, path))
272 'found in %s' % (name, path))
273 else:
273 else:
274
274
275 klass = get_backend(path[0])
275 klass = get_backend(path[0])
276
276
277 if path[0] == 'hg' and path[0] in BACKENDS.keys():
277 if path[0] == 'hg' and path[0] in BACKENDS.keys():
278 repos[name] = klass(safe_str(path[1]), baseui=baseui)
278 repos[name] = klass(safe_str(path[1]), baseui=baseui)
279
279
280 if path[0] == 'git' and path[0] in BACKENDS.keys():
280 if path[0] == 'git' and path[0] in BACKENDS.keys():
281 repos[name] = klass(path[1])
281 repos[name] = klass(path[1])
282 except OSError:
282 except OSError:
283 continue
283 continue
284 log.debug('found %s paths with repositories' % (len(repos)))
284 log.debug('found %s paths with repositories' % (len(repos)))
285 return repos
285 return repos
286
286
287 def get_repos(self, all_repos=None, sort_key=None, simple=False):
287 def get_repos(self, all_repos=None, sort_key=None, simple=False):
288 """
288 """
289 Get all repos from db and for each repo create it's
289 Get all repos from db and for each repo create it's
290 backend instance and fill that backed with information from database
290 backend instance and fill that backed with information from database
291
291
292 :param all_repos: list of repository names as strings
292 :param all_repos: list of repository names as strings
293 give specific repositories list, good for filtering
293 give specific repositories list, good for filtering
294
294
295 :param sort_key: initial sorting of repos
295 :param sort_key: initial sorting of repos
296 :param simple: use SimpleCachedList - one without the SCM info
296 :param simple: use SimpleCachedList - one without the SCM info
297 """
297 """
298 if all_repos is None:
298 if all_repos is None:
299 all_repos = self.sa.query(Repository)\
299 all_repos = self.sa.query(Repository)\
300 .filter(Repository.group_id == None)\
300 .filter(Repository.group_id == None)\
301 .order_by(func.lower(Repository.repo_name)).all()
301 .order_by(func.lower(Repository.repo_name)).all()
302 if simple:
302 if simple:
303 repo_iter = SimpleCachedRepoList(all_repos,
303 repo_iter = SimpleCachedRepoList(all_repos,
304 repos_path=self.repos_path,
304 repos_path=self.repos_path,
305 order_by=sort_key)
305 order_by=sort_key)
306 else:
306 else:
307 repo_iter = CachedRepoList(all_repos,
307 repo_iter = CachedRepoList(all_repos,
308 repos_path=self.repos_path,
308 repos_path=self.repos_path,
309 order_by=sort_key)
309 order_by=sort_key)
310
310
311 return repo_iter
311 return repo_iter
312
312
313 def get_repos_groups(self, all_groups=None):
313 def get_repos_groups(self, all_groups=None):
314 if all_groups is None:
314 if all_groups is None:
315 all_groups = RepoGroup.query()\
315 all_groups = RepoGroup.query()\
316 .filter(RepoGroup.group_parent_id == None).all()
316 .filter(RepoGroup.group_parent_id == None).all()
317 return [x for x in RepoGroupList(all_groups)]
317 return [x for x in RepoGroupList(all_groups)]
318
318
319 def mark_for_invalidation(self, repo_name):
319 def mark_for_invalidation(self, repo_name):
320 """
320 """
321 Mark caches of this repo invalid in the database.
321 Mark caches of this repo invalid in the database.
322
322
323 :param repo_name: the repo for which caches should be marked invalid
323 :param repo_name: the repo for which caches should be marked invalid
324 """
324 """
325 CacheInvalidation.set_invalidate(repo_name)
325 CacheInvalidation.set_invalidate(repo_name)
326 repo = Repository.get_by_repo_name(repo_name)
326 repo = Repository.get_by_repo_name(repo_name)
327 if repo:
327 if repo:
328 repo.update_changeset_cache()
328 repo.update_changeset_cache()
329
329
330 def toggle_following_repo(self, follow_repo_id, user_id):
330 def toggle_following_repo(self, follow_repo_id, user_id):
331
331
332 f = self.sa.query(UserFollowing)\
332 f = self.sa.query(UserFollowing)\
333 .filter(UserFollowing.follows_repo_id == follow_repo_id)\
333 .filter(UserFollowing.follows_repo_id == follow_repo_id)\
334 .filter(UserFollowing.user_id == user_id).scalar()
334 .filter(UserFollowing.user_id == user_id).scalar()
335
335
336 if f is not None:
336 if f is not None:
337 try:
337 try:
338 self.sa.delete(f)
338 self.sa.delete(f)
339 action_logger(UserTemp(user_id),
339 action_logger(UserTemp(user_id),
340 'stopped_following_repo',
340 'stopped_following_repo',
341 RepoTemp(follow_repo_id))
341 RepoTemp(follow_repo_id))
342 return
342 return
343 except Exception:
343 except Exception:
344 log.error(traceback.format_exc())
344 log.error(traceback.format_exc())
345 raise
345 raise
346
346
347 try:
347 try:
348 f = UserFollowing()
348 f = UserFollowing()
349 f.user_id = user_id
349 f.user_id = user_id
350 f.follows_repo_id = follow_repo_id
350 f.follows_repo_id = follow_repo_id
351 self.sa.add(f)
351 self.sa.add(f)
352
352
353 action_logger(UserTemp(user_id),
353 action_logger(UserTemp(user_id),
354 'started_following_repo',
354 'started_following_repo',
355 RepoTemp(follow_repo_id))
355 RepoTemp(follow_repo_id))
356 except Exception:
356 except Exception:
357 log.error(traceback.format_exc())
357 log.error(traceback.format_exc())
358 raise
358 raise
359
359
360 def toggle_following_user(self, follow_user_id, user_id):
360 def toggle_following_user(self, follow_user_id, user_id):
361 f = self.sa.query(UserFollowing)\
361 f = self.sa.query(UserFollowing)\
362 .filter(UserFollowing.follows_user_id == follow_user_id)\
362 .filter(UserFollowing.follows_user_id == follow_user_id)\
363 .filter(UserFollowing.user_id == user_id).scalar()
363 .filter(UserFollowing.user_id == user_id).scalar()
364
364
365 if f is not None:
365 if f is not None:
366 try:
366 try:
367 self.sa.delete(f)
367 self.sa.delete(f)
368 return
368 return
369 except Exception:
369 except Exception:
370 log.error(traceback.format_exc())
370 log.error(traceback.format_exc())
371 raise
371 raise
372
372
373 try:
373 try:
374 f = UserFollowing()
374 f = UserFollowing()
375 f.user_id = user_id
375 f.user_id = user_id
376 f.follows_user_id = follow_user_id
376 f.follows_user_id = follow_user_id
377 self.sa.add(f)
377 self.sa.add(f)
378 except Exception:
378 except Exception:
379 log.error(traceback.format_exc())
379 log.error(traceback.format_exc())
380 raise
380 raise
381
381
382 def is_following_repo(self, repo_name, user_id, cache=False):
382 def is_following_repo(self, repo_name, user_id, cache=False):
383 r = self.sa.query(Repository)\
383 r = self.sa.query(Repository)\
384 .filter(Repository.repo_name == repo_name).scalar()
384 .filter(Repository.repo_name == repo_name).scalar()
385
385
386 f = self.sa.query(UserFollowing)\
386 f = self.sa.query(UserFollowing)\
387 .filter(UserFollowing.follows_repository == r)\
387 .filter(UserFollowing.follows_repository == r)\
388 .filter(UserFollowing.user_id == user_id).scalar()
388 .filter(UserFollowing.user_id == user_id).scalar()
389
389
390 return f is not None
390 return f is not None
391
391
392 def is_following_user(self, username, user_id, cache=False):
392 def is_following_user(self, username, user_id, cache=False):
393 u = User.get_by_username(username)
393 u = User.get_by_username(username)
394
394
395 f = self.sa.query(UserFollowing)\
395 f = self.sa.query(UserFollowing)\
396 .filter(UserFollowing.follows_user == u)\
396 .filter(UserFollowing.follows_user == u)\
397 .filter(UserFollowing.user_id == user_id).scalar()
397 .filter(UserFollowing.user_id == user_id).scalar()
398
398
399 return f is not None
399 return f is not None
400
400
401 def get_followers(self, repo):
401 def get_followers(self, repo):
402 repo = self._get_repo(repo)
402 repo = self._get_repo(repo)
403
403
404 return self.sa.query(UserFollowing)\
404 return self.sa.query(UserFollowing)\
405 .filter(UserFollowing.follows_repository == repo).count()
405 .filter(UserFollowing.follows_repository == repo).count()
406
406
407 def get_forks(self, repo):
407 def get_forks(self, repo):
408 repo = self._get_repo(repo)
408 repo = self._get_repo(repo)
409 return self.sa.query(Repository)\
409 return self.sa.query(Repository)\
410 .filter(Repository.fork == repo).count()
410 .filter(Repository.fork == repo).count()
411
411
412 def get_pull_requests(self, repo):
412 def get_pull_requests(self, repo):
413 repo = self._get_repo(repo)
413 repo = self._get_repo(repo)
414 return self.sa.query(PullRequest)\
414 return self.sa.query(PullRequest)\
415 .filter(PullRequest.other_repo == repo)\
415 .filter(PullRequest.other_repo == repo)\
416 .filter(PullRequest.status != PullRequest.STATUS_CLOSED).count()
416 .filter(PullRequest.status != PullRequest.STATUS_CLOSED).count()
417
417
418 def mark_as_fork(self, repo, fork, user):
418 def mark_as_fork(self, repo, fork, user):
419 repo = self.__get_repo(repo)
419 repo = self.__get_repo(repo)
420 fork = self.__get_repo(fork)
420 fork = self.__get_repo(fork)
421 if fork and repo.repo_id == fork.repo_id:
421 if fork and repo.repo_id == fork.repo_id:
422 raise Exception("Cannot set repository as fork of itself")
422 raise Exception("Cannot set repository as fork of itself")
423 repo.fork = fork
423 repo.fork = fork
424 self.sa.add(repo)
424 self.sa.add(repo)
425 return repo
425 return repo
426
426
427 def _handle_push(self, repo, username, action, repo_name, revisions):
427 def _handle_push(self, repo, username, action, repo_name, revisions):
428 """
428 """
429 Triggers push action hooks
429 Triggers push action hooks
430
430
431 :param repo: SCM repo
431 :param repo: SCM repo
432 :param username: username who pushes
432 :param username: username who pushes
433 :param action: push/push_loca/push_remote
433 :param action: push/push_loca/push_remote
434 :param repo_name: name of repo
434 :param repo_name: name of repo
435 :param revisions: list of revisions that we pushed
435 :param revisions: list of revisions that we pushed
436 """
436 """
437 from rhodecode import CONFIG
437 from rhodecode import CONFIG
438 from rhodecode.lib.base import _get_ip_addr
438 from rhodecode.lib.base import _get_ip_addr
439 try:
439 try:
440 from pylons import request
440 from pylons import request
441 environ = request.environ
441 environ = request.environ
442 except TypeError:
442 except TypeError:
443 # we might use this outside of request context, let's fake the
443 # we might use this outside of request context, let's fake the
444 # environ data
444 # environ data
445 from webob import Request
445 from webob import Request
446 environ = Request.blank('').environ
446 environ = Request.blank('').environ
447
447
448 #trigger push hook
448 #trigger push hook
449 extras = {
449 extras = {
450 'ip': _get_ip_addr(environ),
450 'ip': _get_ip_addr(environ),
451 'username': username,
451 'username': username,
452 'action': 'push_local',
452 'action': 'push_local',
453 'repository': repo_name,
453 'repository': repo_name,
454 'scm': repo.alias,
454 'scm': repo.alias,
455 'config': CONFIG['__file__'],
455 'config': CONFIG['__file__'],
456 'server_url': get_server_url(environ),
456 'server_url': get_server_url(environ),
457 'make_lock': None,
457 'make_lock': None,
458 'locked_by': [None, None]
458 'locked_by': [None, None]
459 }
459 }
460 _scm_repo = repo._repo
460 _scm_repo = repo._repo
461 _set_extras(extras)
461 _set_extras(extras)
462 if repo.alias == 'hg':
462 if repo.alias == 'hg':
463 log_push_action(_scm_repo.ui, _scm_repo, node=revisions[0])
463 log_push_action(_scm_repo.ui, _scm_repo, node=revisions[0])
464 elif repo.alias == 'git':
464 elif repo.alias == 'git':
465 log_push_action(None, _scm_repo, _git_revs=revisions)
465 log_push_action(None, _scm_repo, _git_revs=revisions)
466
466
467 def _get_IMC_module(self, scm_type):
467 def _get_IMC_module(self, scm_type):
468 """
468 """
469 Returns InMemoryCommit class based on scm_type
469 Returns InMemoryCommit class based on scm_type
470
470
471 :param scm_type:
471 :param scm_type:
472 """
472 """
473 if scm_type == 'hg':
473 if scm_type == 'hg':
474 from rhodecode.lib.vcs.backends.hg import \
474 from rhodecode.lib.vcs.backends.hg import \
475 MercurialInMemoryChangeset as IMC
475 MercurialInMemoryChangeset as IMC
476 elif scm_type == 'git':
476 elif scm_type == 'git':
477 from rhodecode.lib.vcs.backends.git import \
477 from rhodecode.lib.vcs.backends.git import \
478 GitInMemoryChangeset as IMC
478 GitInMemoryChangeset as IMC
479 return IMC
479 return IMC
480
480
481 def pull_changes(self, repo, username):
481 def pull_changes(self, repo, username):
482 dbrepo = self.__get_repo(repo)
482 dbrepo = self.__get_repo(repo)
483 clone_uri = dbrepo.clone_uri
483 clone_uri = dbrepo.clone_uri
484 if not clone_uri:
484 if not clone_uri:
485 raise Exception("This repository doesn't have a clone uri")
485 raise Exception("This repository doesn't have a clone uri")
486
486
487 repo = dbrepo.scm_instance
487 repo = dbrepo.scm_instance
488 repo_name = dbrepo.repo_name
488 repo_name = dbrepo.repo_name
489 try:
489 try:
490 if repo.alias == 'git':
490 if repo.alias == 'git':
491 repo.fetch(clone_uri)
491 repo.fetch(clone_uri)
492 else:
492 else:
493 repo.pull(clone_uri)
493 repo.pull(clone_uri)
494 self.mark_for_invalidation(repo_name)
494 self.mark_for_invalidation(repo_name)
495 except Exception:
495 except Exception:
496 log.error(traceback.format_exc())
496 log.error(traceback.format_exc())
497 raise
497 raise
498
498
499 def commit_change(self, repo, repo_name, cs, user, author, message,
499 def commit_change(self, repo, repo_name, cs, user, author, message,
500 content, f_path):
500 content, f_path):
501 """
501 """
502 Commits changes
502 Commits changes
503
503
504 :param repo: SCM instance
504 :param repo: SCM instance
505
505
506 """
506 """
507 user = self._get_user(user)
507 user = self._get_user(user)
508 IMC = self._get_IMC_module(repo.alias)
508 IMC = self._get_IMC_module(repo.alias)
509
509
510 # decoding here will force that we have proper encoded values
510 # decoding here will force that we have proper encoded values
511 # in any other case this will throw exceptions and deny commit
511 # in any other case this will throw exceptions and deny commit
512 content = safe_str(content)
512 content = safe_str(content)
513 path = safe_str(f_path)
513 path = safe_str(f_path)
514 # message and author needs to be unicode
514 # message and author needs to be unicode
515 # proper backend should then translate that into required type
515 # proper backend should then translate that into required type
516 message = safe_unicode(message)
516 message = safe_unicode(message)
517 author = safe_unicode(author)
517 author = safe_unicode(author)
518 m = IMC(repo)
518 m = IMC(repo)
519 m.change(FileNode(path, content))
519 m.change(FileNode(path, content))
520 tip = m.commit(message=message,
520 tip = m.commit(message=message,
521 author=author,
521 author=author,
522 parents=[cs], branch=cs.branch)
522 parents=[cs], branch=cs.branch)
523
523
524 self.mark_for_invalidation(repo_name)
524 self.mark_for_invalidation(repo_name)
525 self._handle_push(repo,
525 self._handle_push(repo,
526 username=user.username,
526 username=user.username,
527 action='push_local',
527 action='push_local',
528 repo_name=repo_name,
528 repo_name=repo_name,
529 revisions=[tip.raw_id])
529 revisions=[tip.raw_id])
530 return tip
530 return tip
531
531
532 def create_node(self, repo, repo_name, cs, user, author, message, content,
532 def create_node(self, repo, repo_name, cs, user, author, message, content,
533 f_path):
533 f_path):
534 user = self._get_user(user)
534 user = self._get_user(user)
535 IMC = self._get_IMC_module(repo.alias)
535 IMC = self._get_IMC_module(repo.alias)
536
536
537 # decoding here will force that we have proper encoded values
537 # decoding here will force that we have proper encoded values
538 # in any other case this will throw exceptions and deny commit
538 # in any other case this will throw exceptions and deny commit
539 if isinstance(content, (basestring,)):
539 if isinstance(content, (basestring,)):
540 content = safe_str(content)
540 content = safe_str(content)
541 elif isinstance(content, (file, cStringIO.OutputType,)):
541 elif isinstance(content, (file, cStringIO.OutputType,)):
542 content = content.read()
542 content = content.read()
543 else:
543 else:
544 raise Exception('Content is of unrecognized type %s' % (
544 raise Exception('Content is of unrecognized type %s' % (
545 type(content)
545 type(content)
546 ))
546 ))
547
547
548 message = safe_unicode(message)
548 message = safe_unicode(message)
549 author = safe_unicode(author)
549 author = safe_unicode(author)
550 path = safe_str(f_path)
550 path = safe_str(f_path)
551 m = IMC(repo)
551 m = IMC(repo)
552
552
553 if isinstance(cs, EmptyChangeset):
553 if isinstance(cs, EmptyChangeset):
554 # EmptyChangeset means we we're editing empty repository
554 # EmptyChangeset means we we're editing empty repository
555 parents = None
555 parents = None
556 else:
556 else:
557 parents = [cs]
557 parents = [cs]
558
558
559 m.add(FileNode(path, content=content))
559 m.add(FileNode(path, content=content))
560 tip = m.commit(message=message,
560 tip = m.commit(message=message,
561 author=author,
561 author=author,
562 parents=parents, branch=cs.branch)
562 parents=parents, branch=cs.branch)
563
563
564 self.mark_for_invalidation(repo_name)
564 self.mark_for_invalidation(repo_name)
565 self._handle_push(repo,
565 self._handle_push(repo,
566 username=user.username,
566 username=user.username,
567 action='push_local',
567 action='push_local',
568 repo_name=repo_name,
568 repo_name=repo_name,
569 revisions=[tip.raw_id])
569 revisions=[tip.raw_id])
570 return tip
570 return tip
571
571
572 def get_nodes(self, repo_name, revision, root_path='/', flat=True):
572 def get_nodes(self, repo_name, revision, root_path='/', flat=True):
573 """
573 """
574 recursive walk in root dir and return a set of all path in that dir
574 recursive walk in root dir and return a set of all path in that dir
575 based on repository walk function
575 based on repository walk function
576
576
577 :param repo_name: name of repository
577 :param repo_name: name of repository
578 :param revision: revision for which to list nodes
578 :param revision: revision for which to list nodes
579 :param root_path: root path to list
579 :param root_path: root path to list
580 :param flat: return as a list, if False returns a dict with decription
580 :param flat: return as a list, if False returns a dict with decription
581
581
582 """
582 """
583 _files = list()
583 _files = list()
584 _dirs = list()
584 _dirs = list()
585 try:
585 try:
586 _repo = self.__get_repo(repo_name)
586 _repo = self.__get_repo(repo_name)
587 changeset = _repo.scm_instance.get_changeset(revision)
587 changeset = _repo.scm_instance.get_changeset(revision)
588 root_path = root_path.lstrip('/')
588 root_path = root_path.lstrip('/')
589 for topnode, dirs, files in changeset.walk(root_path):
589 for topnode, dirs, files in changeset.walk(root_path):
590 for f in files:
590 for f in files:
591 _files.append(f.path if flat else {"name": f.path,
591 _files.append(f.path if flat else {"name": f.path,
592 "type": "file"})
592 "type": "file"})
593 for d in dirs:
593 for d in dirs:
594 _dirs.append(d.path if flat else {"name": d.path,
594 _dirs.append(d.path if flat else {"name": d.path,
595 "type": "dir"})
595 "type": "dir"})
596 except RepositoryError:
596 except RepositoryError:
597 log.debug(traceback.format_exc())
597 log.debug(traceback.format_exc())
598 raise
598 raise
599
599
600 return _dirs, _files
600 return _dirs, _files
601
601
602 def get_unread_journal(self):
602 def get_unread_journal(self):
603 return self.sa.query(UserLog).count()
603 return self.sa.query(UserLog).count()
604
604
605 def get_repo_landing_revs(self, repo=None):
605 def get_repo_landing_revs(self, repo=None):
606 """
606 """
607 Generates select option with tags branches and bookmarks (for hg only)
607 Generates select option with tags branches and bookmarks (for hg only)
608 grouped by type
608 grouped by type
609
609
610 :param repo:
610 :param repo:
611 :type repo:
611 :type repo:
612 """
612 """
613
613
614 hist_l = []
614 hist_l = []
615 choices = []
615 choices = []
616 repo = self.__get_repo(repo)
616 repo = self.__get_repo(repo)
617 hist_l.append(['tip', _('latest tip')])
617 hist_l.append(['tip', _('latest tip')])
618 choices.append('tip')
618 choices.append('tip')
619 if not repo:
619 if not repo:
620 return choices, hist_l
620 return choices, hist_l
621
621
622 repo = repo.scm_instance
622 repo = repo.scm_instance
623
623
624 branches_group = ([(k, k) for k, v in
624 branches_group = ([(k, k) for k, v in
625 repo.branches.iteritems()], _("Branches"))
625 repo.branches.iteritems()], _("Branches"))
626 hist_l.append(branches_group)
626 hist_l.append(branches_group)
627 choices.extend([x[0] for x in branches_group[0]])
627 choices.extend([x[0] for x in branches_group[0]])
628
628
629 if repo.alias == 'hg':
629 if repo.alias == 'hg':
630 bookmarks_group = ([(k, k) for k, v in
630 bookmarks_group = ([(k, k) for k, v in
631 repo.bookmarks.iteritems()], _("Bookmarks"))
631 repo.bookmarks.iteritems()], _("Bookmarks"))
632 hist_l.append(bookmarks_group)
632 hist_l.append(bookmarks_group)
633 choices.extend([x[0] for x in bookmarks_group[0]])
633 choices.extend([x[0] for x in bookmarks_group[0]])
634
634
635 tags_group = ([(k, k) for k, v in
635 tags_group = ([(k, k) for k, v in
636 repo.tags.iteritems()], _("Tags"))
636 repo.tags.iteritems()], _("Tags"))
637 hist_l.append(tags_group)
637 hist_l.append(tags_group)
638 choices.extend([x[0] for x in tags_group[0]])
638 choices.extend([x[0] for x in tags_group[0]])
639
639
640 return choices, hist_l
640 return choices, hist_l
641
641
642 def install_git_hook(self, repo, force_create=False):
642 def install_git_hook(self, repo, force_create=False):
643 """
643 """
644 Creates a rhodecode hook inside a git repository
644 Creates a rhodecode hook inside a git repository
645
645
646 :param repo: Instance of VCS repo
646 :param repo: Instance of VCS repo
647 :param force_create: Create even if same name hook exists
647 :param force_create: Create even if same name hook exists
648 """
648 """
649
649
650 loc = jn(repo.path, 'hooks')
650 loc = jn(repo.path, 'hooks')
651 if not repo.bare:
651 if not repo.bare:
652 loc = jn(repo.path, '.git', 'hooks')
652 loc = jn(repo.path, '.git', 'hooks')
653 if not os.path.isdir(loc):
653 if not os.path.isdir(loc):
654 os.makedirs(loc)
654 os.makedirs(loc)
655
655
656 tmpl_post = pkg_resources.resource_string(
656 tmpl_post = pkg_resources.resource_string(
657 'rhodecode', jn('config', 'post_receive_tmpl.py')
657 'rhodecode', jn('config', 'post_receive_tmpl.py')
658 )
658 )
659 tmpl_pre = pkg_resources.resource_string(
659 tmpl_pre = pkg_resources.resource_string(
660 'rhodecode', jn('config', 'pre_receive_tmpl.py')
660 'rhodecode', jn('config', 'pre_receive_tmpl.py')
661 )
661 )
662
662
663 for h_type, tmpl in [('pre', tmpl_pre), ('post', tmpl_post)]:
663 for h_type, tmpl in [('pre', tmpl_pre), ('post', tmpl_post)]:
664 _hook_file = jn(loc, '%s-receive' % h_type)
664 _hook_file = jn(loc, '%s-receive' % h_type)
665 _rhodecode_hook = False
665 _rhodecode_hook = False
666 log.debug('Installing git hook in repo %s' % repo)
666 log.debug('Installing git hook in repo %s' % repo)
667 if os.path.exists(_hook_file):
667 if os.path.exists(_hook_file):
668 # let's take a look at this hook, maybe it's rhodecode ?
668 # let's take a look at this hook, maybe it's rhodecode ?
669 log.debug('hook exists, checking if it is from rhodecode')
669 log.debug('hook exists, checking if it is from rhodecode')
670 _HOOK_VER_PAT = re.compile(r'^RC_HOOK_VER')
670 _HOOK_VER_PAT = re.compile(r'^RC_HOOK_VER')
671 with open(_hook_file, 'rb') as f:
671 with open(_hook_file, 'rb') as f:
672 data = f.read()
672 data = f.read()
673 matches = re.compile(r'(?:%s)\s*=\s*(.*)'
673 matches = re.compile(r'(?:%s)\s*=\s*(.*)'
674 % 'RC_HOOK_VER').search(data)
674 % 'RC_HOOK_VER').search(data)
675 if matches:
675 if matches:
676 try:
676 try:
677 ver = matches.groups()[0]
677 ver = matches.groups()[0]
678 log.debug('got %s it is rhodecode' % (ver))
678 log.debug('got %s it is rhodecode' % (ver))
679 _rhodecode_hook = True
679 _rhodecode_hook = True
680 except Exception:
680 except Exception:
681 log.error(traceback.format_exc())
681 log.error(traceback.format_exc())
682 else:
682 else:
683 # there is no hook in this dir, so we want to create one
683 # there is no hook in this dir, so we want to create one
684 _rhodecode_hook = True
684 _rhodecode_hook = True
685
685
686 if _rhodecode_hook or force_create:
686 if _rhodecode_hook or force_create:
687 log.debug('writing %s hook file !' % h_type)
687 log.debug('writing %s hook file !' % h_type)
688 with open(_hook_file, 'wb') as f:
688 with open(_hook_file, 'wb') as f:
689 tmpl = tmpl.replace('_TMPL_', rhodecode.__version__)
689 tmpl = tmpl.replace('_TMPL_', rhodecode.__version__)
690 f.write(tmpl)
690 f.write(tmpl)
691 os.chmod(_hook_file, 0755)
691 os.chmod(_hook_file, 0755)
692 else:
692 else:
693 log.debug('skipping writing hook file')
693 log.debug('skipping writing hook file')
General Comments 0
You need to be logged in to leave comments. Login now