##// END OF EJS Templates
code refactoring
marcink -
r1675:7c487d26 beta
parent child Browse files
Show More
@@ -1,291 +1,289 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.controllers.changeset
3 rhodecode.controllers.changeset
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 changeset controller for pylons showoing changes beetween
6 changeset controller for pylons showoing changes beetween
7 revisions
7 revisions
8
8
9 :created_on: Apr 25, 2010
9 :created_on: Apr 25, 2010
10 :author: marcink
10 :author: marcink
11 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
12 :license: GPLv3, see COPYING for more details.
12 :license: GPLv3, see COPYING for more details.
13 """
13 """
14 # This program is free software: you can redistribute it and/or modify
14 # This program is free software: you can redistribute it and/or modify
15 # it under the terms of the GNU General Public License as published by
15 # it under the terms of the GNU General Public License as published by
16 # the Free Software Foundation, either version 3 of the License, or
16 # the Free Software Foundation, either version 3 of the License, or
17 # (at your option) any later version.
17 # (at your option) any later version.
18 #
18 #
19 # This program is distributed in the hope that it will be useful,
19 # This program is distributed in the hope that it will be useful,
20 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # but WITHOUT ANY WARRANTY; without even the implied warranty of
21 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 # GNU General Public License for more details.
22 # GNU General Public License for more details.
23 #
23 #
24 # You should have received a copy of the GNU General Public License
24 # You should have received a copy of the GNU General Public License
25 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 # along with this program. If not, see <http://www.gnu.org/licenses/>.
26 import logging
26 import logging
27 import traceback
27 import traceback
28
28
29 from pylons import tmpl_context as c, url, request, response
29 from pylons import tmpl_context as c, url, request, response
30 from pylons.i18n.translation import _
30 from pylons.i18n.translation import _
31 from pylons.controllers.util import redirect
31 from pylons.controllers.util import redirect
32 from pylons.decorators import jsonify
32 from pylons.decorators import jsonify
33
33
34 import rhodecode.lib.helpers as h
34 import rhodecode.lib.helpers as h
35 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator, \
35 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator, \
36 NotAnonymous
36 NotAnonymous
37 from rhodecode.lib.base import BaseRepoController, render
37 from rhodecode.lib.base import BaseRepoController, render
38 from rhodecode.lib.utils import EmptyChangeset
38 from rhodecode.lib.utils import EmptyChangeset
39 from rhodecode.lib.compat import OrderedDict
39 from rhodecode.lib.compat import OrderedDict
40 from rhodecode.model.db import ChangesetComment
40 from rhodecode.model.db import ChangesetComment
41 from rhodecode.model.comment import ChangesetCommentsModel
41 from rhodecode.model.comment import ChangesetCommentsModel
42
42
43 from vcs.exceptions import RepositoryError, ChangesetError, \
43 from vcs.exceptions import RepositoryError, ChangesetError, \
44 ChangesetDoesNotExistError
44 ChangesetDoesNotExistError
45 from vcs.nodes import FileNode
45 from vcs.nodes import FileNode
46 from vcs.utils import diffs as differ
46 from vcs.utils import diffs as differ
47 from webob.exc import HTTPForbidden
47 from webob.exc import HTTPForbidden
48
48
49 log = logging.getLogger(__name__)
49 log = logging.getLogger(__name__)
50
50
51
51
52 class ChangesetController(BaseRepoController):
52 class ChangesetController(BaseRepoController):
53
53
54 @LoginRequired()
54 @LoginRequired()
55 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
55 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
56 'repository.admin')
56 'repository.admin')
57 def __before__(self):
57 def __before__(self):
58 super(ChangesetController, self).__before__()
58 super(ChangesetController, self).__before__()
59 c.affected_files_cut_off = 60
59 c.affected_files_cut_off = 60
60
60
61 def index(self, revision):
61 def index(self, revision):
62
62
63 def wrap_to_table(str):
63 def wrap_to_table(str):
64
64
65 return '''<table class="code-difftable">
65 return '''<table class="code-difftable">
66 <tr class="line">
66 <tr class="line">
67 <td class="lineno new"></td>
67 <td class="lineno new"></td>
68 <td class="code"><pre>%s</pre></td>
68 <td class="code"><pre>%s</pre></td>
69 </tr>
69 </tr>
70 </table>''' % str
70 </table>''' % str
71
71
72 #get ranges of revisions if preset
72 #get ranges of revisions if preset
73 rev_range = revision.split('...')[:2]
73 rev_range = revision.split('...')[:2]
74
74
75 try:
75 try:
76 if len(rev_range) == 2:
76 if len(rev_range) == 2:
77 rev_start = rev_range[0]
77 rev_start = rev_range[0]
78 rev_end = rev_range[1]
78 rev_end = rev_range[1]
79 rev_ranges = c.rhodecode_repo.get_changesets(start=rev_start,
79 rev_ranges = c.rhodecode_repo.get_changesets(start=rev_start,
80 end=rev_end)
80 end=rev_end)
81 else:
81 else:
82 rev_ranges = [c.rhodecode_repo.get_changeset(revision)]
82 rev_ranges = [c.rhodecode_repo.get_changeset(revision)]
83
83
84 c.cs_ranges = list(rev_ranges)
84 c.cs_ranges = list(rev_ranges)
85 if not c.cs_ranges:
85 if not c.cs_ranges:
86 raise RepositoryError('Changeset range returned empty result')
86 raise RepositoryError('Changeset range returned empty result')
87
87
88 except (RepositoryError, ChangesetDoesNotExistError, Exception), e:
88 except (RepositoryError, ChangesetDoesNotExistError, Exception), e:
89 log.error(traceback.format_exc())
89 log.error(traceback.format_exc())
90 h.flash(str(e), category='warning')
90 h.flash(str(e), category='warning')
91 return redirect(url('home'))
91 return redirect(url('home'))
92
92
93 c.changes = OrderedDict()
93 c.changes = OrderedDict()
94 c.sum_added = 0
94 c.sum_added = 0
95 c.sum_removed = 0
95 c.sum_removed = 0
96 c.lines_added = 0
96 c.lines_added = 0
97 c.lines_deleted = 0
97 c.lines_deleted = 0
98 c.cut_off = False # defines if cut off limit is reached
98 c.cut_off = False # defines if cut off limit is reached
99
99
100 c.comments = []
100 c.comments = []
101 for cs in c.cs_ranges:
102 c.comments.extend(ChangesetComment.query()\
103 .filter(ChangesetComment.repo_id == c.rhodecode_db_repo.repo_id)\
104 .filter(ChangesetComment.commit_id == cs.raw_id)\
105 .filter(ChangesetComment.line_no == None)\
106 .filter(ChangesetComment.f_path == None).all())
107
101
108 # Iterate over ranges (default changeset view is always one changeset)
102 # Iterate over ranges (default changeset view is always one changeset)
109 for changeset in c.cs_ranges:
103 for changeset in c.cs_ranges:
104 c.comments.extend(ChangesetCommentsModel()\
105 .get_comments(c.rhodecode_db_repo.repo_id,
106 changeset.raw_id))
107
110 c.changes[changeset.raw_id] = []
108 c.changes[changeset.raw_id] = []
111 try:
109 try:
112 changeset_parent = changeset.parents[0]
110 changeset_parent = changeset.parents[0]
113 except IndexError:
111 except IndexError:
114 changeset_parent = None
112 changeset_parent = None
115
113
116 #==================================================================
114 #==================================================================
117 # ADDED FILES
115 # ADDED FILES
118 #==================================================================
116 #==================================================================
119 for node in changeset.added:
117 for node in changeset.added:
120
118
121 filenode_old = FileNode(node.path, '', EmptyChangeset())
119 filenode_old = FileNode(node.path, '', EmptyChangeset())
122 if filenode_old.is_binary or node.is_binary:
120 if filenode_old.is_binary or node.is_binary:
123 diff = wrap_to_table(_('binary file'))
121 diff = wrap_to_table(_('binary file'))
124 st = (0, 0)
122 st = (0, 0)
125 else:
123 else:
126 # in this case node.size is good parameter since those are
124 # in this case node.size is good parameter since those are
127 # added nodes and their size defines how many changes were
125 # added nodes and their size defines how many changes were
128 # made
126 # made
129 c.sum_added += node.size
127 c.sum_added += node.size
130 if c.sum_added < self.cut_off_limit:
128 if c.sum_added < self.cut_off_limit:
131 f_gitdiff = differ.get_gitdiff(filenode_old, node)
129 f_gitdiff = differ.get_gitdiff(filenode_old, node)
132 d = differ.DiffProcessor(f_gitdiff, format='gitdiff')
130 d = differ.DiffProcessor(f_gitdiff, format='gitdiff')
133
131
134 st = d.stat()
132 st = d.stat()
135 diff = d.as_html()
133 diff = d.as_html()
136
134
137 else:
135 else:
138 diff = wrap_to_table(_('Changeset is to big and '
136 diff = wrap_to_table(_('Changeset is to big and '
139 'was cut off, see raw '
137 'was cut off, see raw '
140 'changeset instead'))
138 'changeset instead'))
141 c.cut_off = True
139 c.cut_off = True
142 break
140 break
143
141
144 cs1 = None
142 cs1 = None
145 cs2 = node.last_changeset.raw_id
143 cs2 = node.last_changeset.raw_id
146 c.lines_added += st[0]
144 c.lines_added += st[0]
147 c.lines_deleted += st[1]
145 c.lines_deleted += st[1]
148 c.changes[changeset.raw_id].append(('added', node, diff,
146 c.changes[changeset.raw_id].append(('added', node, diff,
149 cs1, cs2, st))
147 cs1, cs2, st))
150
148
151 #==================================================================
149 #==================================================================
152 # CHANGED FILES
150 # CHANGED FILES
153 #==================================================================
151 #==================================================================
154 if not c.cut_off:
152 if not c.cut_off:
155 for node in changeset.changed:
153 for node in changeset.changed:
156 try:
154 try:
157 filenode_old = changeset_parent.get_node(node.path)
155 filenode_old = changeset_parent.get_node(node.path)
158 except ChangesetError:
156 except ChangesetError:
159 log.warning('Unable to fetch parent node for diff')
157 log.warning('Unable to fetch parent node for diff')
160 filenode_old = FileNode(node.path, '',
158 filenode_old = FileNode(node.path, '',
161 EmptyChangeset())
159 EmptyChangeset())
162
160
163 if filenode_old.is_binary or node.is_binary:
161 if filenode_old.is_binary or node.is_binary:
164 diff = wrap_to_table(_('binary file'))
162 diff = wrap_to_table(_('binary file'))
165 st = (0, 0)
163 st = (0, 0)
166 else:
164 else:
167
165
168 if c.sum_removed < self.cut_off_limit:
166 if c.sum_removed < self.cut_off_limit:
169 f_gitdiff = differ.get_gitdiff(filenode_old, node)
167 f_gitdiff = differ.get_gitdiff(filenode_old, node)
170 d = differ.DiffProcessor(f_gitdiff,
168 d = differ.DiffProcessor(f_gitdiff,
171 format='gitdiff')
169 format='gitdiff')
172 st = d.stat()
170 st = d.stat()
173 if (st[0] + st[1]) * 256 > self.cut_off_limit:
171 if (st[0] + st[1]) * 256 > self.cut_off_limit:
174 diff = wrap_to_table(_('Diff is to big '
172 diff = wrap_to_table(_('Diff is to big '
175 'and was cut off, see '
173 'and was cut off, see '
176 'raw diff instead'))
174 'raw diff instead'))
177 else:
175 else:
178 diff = d.as_html()
176 diff = d.as_html()
179
177
180 if diff:
178 if diff:
181 c.sum_removed += len(diff)
179 c.sum_removed += len(diff)
182 else:
180 else:
183 diff = wrap_to_table(_('Changeset is to big and '
181 diff = wrap_to_table(_('Changeset is to big and '
184 'was cut off, see raw '
182 'was cut off, see raw '
185 'changeset instead'))
183 'changeset instead'))
186 c.cut_off = True
184 c.cut_off = True
187 break
185 break
188
186
189 cs1 = filenode_old.last_changeset.raw_id
187 cs1 = filenode_old.last_changeset.raw_id
190 cs2 = node.last_changeset.raw_id
188 cs2 = node.last_changeset.raw_id
191 c.lines_added += st[0]
189 c.lines_added += st[0]
192 c.lines_deleted += st[1]
190 c.lines_deleted += st[1]
193 c.changes[changeset.raw_id].append(('changed', node, diff,
191 c.changes[changeset.raw_id].append(('changed', node, diff,
194 cs1, cs2, st))
192 cs1, cs2, st))
195
193
196 #==================================================================
194 #==================================================================
197 # REMOVED FILES
195 # REMOVED FILES
198 #==================================================================
196 #==================================================================
199 if not c.cut_off:
197 if not c.cut_off:
200 for node in changeset.removed:
198 for node in changeset.removed:
201 c.changes[changeset.raw_id].append(('removed', node, None,
199 c.changes[changeset.raw_id].append(('removed', node, None,
202 None, None, (0, 0)))
200 None, None, (0, 0)))
203
201
204 if len(c.cs_ranges) == 1:
202 if len(c.cs_ranges) == 1:
205 c.changeset = c.cs_ranges[0]
203 c.changeset = c.cs_ranges[0]
206 c.changes = c.changes[c.changeset.raw_id]
204 c.changes = c.changes[c.changeset.raw_id]
207
205
208 return render('changeset/changeset.html')
206 return render('changeset/changeset.html')
209 else:
207 else:
210 return render('changeset/changeset_range.html')
208 return render('changeset/changeset_range.html')
211
209
212 def raw_changeset(self, revision):
210 def raw_changeset(self, revision):
213
211
214 method = request.GET.get('diff', 'show')
212 method = request.GET.get('diff', 'show')
215 try:
213 try:
216 c.scm_type = c.rhodecode_repo.alias
214 c.scm_type = c.rhodecode_repo.alias
217 c.changeset = c.rhodecode_repo.get_changeset(revision)
215 c.changeset = c.rhodecode_repo.get_changeset(revision)
218 except RepositoryError:
216 except RepositoryError:
219 log.error(traceback.format_exc())
217 log.error(traceback.format_exc())
220 return redirect(url('home'))
218 return redirect(url('home'))
221 else:
219 else:
222 try:
220 try:
223 c.changeset_parent = c.changeset.parents[0]
221 c.changeset_parent = c.changeset.parents[0]
224 except IndexError:
222 except IndexError:
225 c.changeset_parent = None
223 c.changeset_parent = None
226 c.changes = []
224 c.changes = []
227
225
228 for node in c.changeset.added:
226 for node in c.changeset.added:
229 filenode_old = FileNode(node.path, '')
227 filenode_old = FileNode(node.path, '')
230 if filenode_old.is_binary or node.is_binary:
228 if filenode_old.is_binary or node.is_binary:
231 diff = _('binary file') + '\n'
229 diff = _('binary file') + '\n'
232 else:
230 else:
233 f_gitdiff = differ.get_gitdiff(filenode_old, node)
231 f_gitdiff = differ.get_gitdiff(filenode_old, node)
234 diff = differ.DiffProcessor(f_gitdiff,
232 diff = differ.DiffProcessor(f_gitdiff,
235 format='gitdiff').raw_diff()
233 format='gitdiff').raw_diff()
236
234
237 cs1 = None
235 cs1 = None
238 cs2 = node.last_changeset.raw_id
236 cs2 = node.last_changeset.raw_id
239 c.changes.append(('added', node, diff, cs1, cs2))
237 c.changes.append(('added', node, diff, cs1, cs2))
240
238
241 for node in c.changeset.changed:
239 for node in c.changeset.changed:
242 filenode_old = c.changeset_parent.get_node(node.path)
240 filenode_old = c.changeset_parent.get_node(node.path)
243 if filenode_old.is_binary or node.is_binary:
241 if filenode_old.is_binary or node.is_binary:
244 diff = _('binary file')
242 diff = _('binary file')
245 else:
243 else:
246 f_gitdiff = differ.get_gitdiff(filenode_old, node)
244 f_gitdiff = differ.get_gitdiff(filenode_old, node)
247 diff = differ.DiffProcessor(f_gitdiff,
245 diff = differ.DiffProcessor(f_gitdiff,
248 format='gitdiff').raw_diff()
246 format='gitdiff').raw_diff()
249
247
250 cs1 = filenode_old.last_changeset.raw_id
248 cs1 = filenode_old.last_changeset.raw_id
251 cs2 = node.last_changeset.raw_id
249 cs2 = node.last_changeset.raw_id
252 c.changes.append(('changed', node, diff, cs1, cs2))
250 c.changes.append(('changed', node, diff, cs1, cs2))
253
251
254 response.content_type = 'text/plain'
252 response.content_type = 'text/plain'
255
253
256 if method == 'download':
254 if method == 'download':
257 response.content_disposition = 'attachment; filename=%s.patch' \
255 response.content_disposition = 'attachment; filename=%s.patch' \
258 % revision
256 % revision
259
257
260 c.parent_tmpl = ''.join(['# Parent %s\n' % x.raw_id for x in
258 c.parent_tmpl = ''.join(['# Parent %s\n' % x.raw_id for x in
261 c.changeset.parents])
259 c.changeset.parents])
262
260
263 c.diffs = ''
261 c.diffs = ''
264 for x in c.changes:
262 for x in c.changes:
265 c.diffs += x[2]
263 c.diffs += x[2]
266
264
267 return render('changeset/raw_changeset.html')
265 return render('changeset/raw_changeset.html')
268
266
269 def comment(self, repo_name, revision):
267 def comment(self, repo_name, revision):
270 ccmodel = ChangesetCommentsModel()
268 ccmodel = ChangesetCommentsModel()
271
269
272 ccmodel.create(text=request.POST.get('text'),
270 ccmodel.create(text=request.POST.get('text'),
273 repo_id=c.rhodecode_db_repo.repo_id,
271 repo_id=c.rhodecode_db_repo.repo_id,
274 user_id=c.rhodecode_user.user_id,
272 user_id=c.rhodecode_user.user_id,
275 commit_id=revision, f_path=request.POST.get('f_path'),
273 revision=revision, f_path=request.POST.get('f_path'),
276 line_no = request.POST.get('line'))
274 line_no = request.POST.get('line'))
277
275
278 return redirect(h.url('changeset_home', repo_name=repo_name,
276 return redirect(h.url('changeset_home', repo_name=repo_name,
279 revision=revision))
277 revision=revision))
280
278
281 @jsonify
279 @jsonify
282 def delete_comment(self, comment_id):
280 def delete_comment(self, comment_id):
283 co = ChangesetComment.get(comment_id)
281 co = ChangesetComment.get(comment_id)
284 if (h.HasPermissionAny('hg.admin', 'repository.admin')() or
282 if (h.HasPermissionAny('hg.admin', 'repository.admin')() or
285 co.author.user_id == c.rhodecode_user.user_id):
283 co.author.user_id == c.rhodecode_user.user_id):
286 ccmodel = ChangesetCommentsModel()
284 ccmodel = ChangesetCommentsModel()
287 ccmodel.delete(comment_id=comment_id)
285 ccmodel.delete(comment_id=comment_id)
288 return True
286 return True
289 else:
287 else:
290 raise HTTPForbidden()
288 raise HTTPForbidden()
291
289
@@ -1,73 +1,93 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.model.comment
3 rhodecode.model.comment
4 ~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 comments model for RhodeCode
6 comments model for RhodeCode
7
7
8 :created_on: Nov 11, 2011
8 :created_on: Nov 11, 2011
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software: you can redistribute it and/or 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
26
27 import logging
27 import logging
28 import traceback
28 import traceback
29
29
30 from rhodecode.model import BaseModel
30 from rhodecode.model import BaseModel
31 from rhodecode.model.db import ChangesetComment
31 from rhodecode.model.db import ChangesetComment
32 from sqlalchemy.util.compat import defaultdict
32
33
33 log = logging.getLogger(__name__)
34 log = logging.getLogger(__name__)
34
35
35
36
36 class ChangesetCommentsModel(BaseModel):
37 class ChangesetCommentsModel(BaseModel):
37
38
38
39
39 def create(self, text, repo_id, user_id, commit_id, f_path=None,
40 def create(self, text, repo_id, user_id, revision, f_path=None,
40 line_no=None):
41 line_no=None):
41 """
42 """
42 Creates new comment for changeset
43 Creates new comment for changeset
43
44
44 :param text:
45 :param text:
45 :param repo_id:
46 :param repo_id:
46 :param user_id:
47 :param user_id:
47 :param commit_id:
48 :param revision:
48 :param f_path:
49 :param f_path:
49 :param line_no:
50 :param line_no:
50 """
51 """
51
52
52 comment = ChangesetComment()
53 comment = ChangesetComment()
53 comment.repo_id = repo_id
54 comment.repo_id = repo_id
54 comment.user_id = user_id
55 comment.user_id = user_id
55 comment.commit_id = commit_id
56 comment.revision = revision
56 comment.text = text
57 comment.text = text
57 comment.f_path = f_path
58 comment.f_path = f_path
58 comment.line_no = line_no
59 comment.line_no = line_no
59
60
60 self.sa.add(comment)
61 self.sa.add(comment)
61 self.sa.commit()
62 self.sa.commit()
62 return comment
63 return comment
63
64
64 def delete(self, comment_id):
65 def delete(self, comment_id):
65 """
66 """
66 Deletes given comment
67 Deletes given comment
67
68
68 :param comment_id:
69 :param comment_id:
69 """
70 """
70 comment = ChangesetComment.get(comment_id)
71 comment = ChangesetComment.get(comment_id)
71 self.sa.delete(comment)
72 self.sa.delete(comment)
72 self.sa.commit()
73 self.sa.commit()
73 return comment
74 return comment
75
76
77 def get_comments(self, repo_id, revision):
78 return ChangesetComment.query()\
79 .filter(ChangesetComment.repo_id == repo_id)\
80 .filter(ChangesetComment.revision == revision)\
81 .filter(ChangesetComment.line_no == None)\
82 .filter(ChangesetComment.f_path == None).all()
83
84 def get_comments_for_file(self, repo_id, f_path, raw_id):
85 comments = self.sa.query(ChangesetComment)\
86 .filter(ChangesetComment.repo_id == repo_id)\
87 .filter(ChangesetComment.commit_id == raw_id)\
88 .filter(ChangesetComment.f_path == f_path).all()
89
90 d = defaultdict(list)
91 for co in comments:
92 d[co.line_no].append(co)
93 return d.items()
@@ -1,1120 +1,1120 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) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software: you can redistribute it and/or 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 from datetime import date
30 from datetime import date
31
31
32 from sqlalchemy import *
32 from sqlalchemy import *
33 from sqlalchemy.exc import DatabaseError
33 from sqlalchemy.exc import DatabaseError
34 from sqlalchemy.ext.hybrid import hybrid_property
34 from sqlalchemy.ext.hybrid import hybrid_property
35 from sqlalchemy.orm import relationship, joinedload, class_mapper, validates
35 from sqlalchemy.orm import relationship, joinedload, class_mapper, validates
36 from beaker.cache import cache_region, region_invalidate
36 from beaker.cache import cache_region, region_invalidate
37
37
38 from vcs import get_backend
38 from vcs import get_backend
39 from vcs.utils.helpers import get_scm
39 from vcs.utils.helpers import get_scm
40 from vcs.exceptions import VCSError
40 from vcs.exceptions import VCSError
41 from vcs.utils.lazy import LazyProperty
41 from vcs.utils.lazy import LazyProperty
42
42
43 from rhodecode.lib import str2bool, safe_str, get_changeset_safe, \
43 from rhodecode.lib import str2bool, safe_str, get_changeset_safe, \
44 generate_api_key, safe_unicode
44 generate_api_key, safe_unicode
45 from rhodecode.lib.exceptions import UsersGroupsAssignedException
45 from rhodecode.lib.exceptions import UsersGroupsAssignedException
46 from rhodecode.lib.compat import json
46 from rhodecode.lib.compat import json
47 from rhodecode.lib.caching_query import FromCache
47 from rhodecode.lib.caching_query import FromCache
48
48
49 from rhodecode.model.meta import Base, Session
49 from rhodecode.model.meta import Base, Session
50
50
51
51
52
52
53 log = logging.getLogger(__name__)
53 log = logging.getLogger(__name__)
54
54
55 #==============================================================================
55 #==============================================================================
56 # BASE CLASSES
56 # BASE CLASSES
57 #==============================================================================
57 #==============================================================================
58
58
59 class ModelSerializer(json.JSONEncoder):
59 class ModelSerializer(json.JSONEncoder):
60 """
60 """
61 Simple Serializer for JSON,
61 Simple Serializer for JSON,
62
62
63 usage::
63 usage::
64
64
65 to make object customized for serialization implement a __json__
65 to make object customized for serialization implement a __json__
66 method that will return a dict for serialization into json
66 method that will return a dict for serialization into json
67
67
68 example::
68 example::
69
69
70 class Task(object):
70 class Task(object):
71
71
72 def __init__(self, name, value):
72 def __init__(self, name, value):
73 self.name = name
73 self.name = name
74 self.value = value
74 self.value = value
75
75
76 def __json__(self):
76 def __json__(self):
77 return dict(name=self.name,
77 return dict(name=self.name,
78 value=self.value)
78 value=self.value)
79
79
80 """
80 """
81
81
82 def default(self, obj):
82 def default(self, obj):
83
83
84 if hasattr(obj, '__json__'):
84 if hasattr(obj, '__json__'):
85 return obj.__json__()
85 return obj.__json__()
86 else:
86 else:
87 return json.JSONEncoder.default(self, obj)
87 return json.JSONEncoder.default(self, obj)
88
88
89 class BaseModel(object):
89 class BaseModel(object):
90 """Base Model for all classess
90 """Base Model for all classess
91
91
92 """
92 """
93
93
94 @classmethod
94 @classmethod
95 def _get_keys(cls):
95 def _get_keys(cls):
96 """return column names for this model """
96 """return column names for this model """
97 return class_mapper(cls).c.keys()
97 return class_mapper(cls).c.keys()
98
98
99 def get_dict(self):
99 def get_dict(self):
100 """return dict with keys and values corresponding
100 """return dict with keys and values corresponding
101 to this model data """
101 to this model data """
102
102
103 d = {}
103 d = {}
104 for k in self._get_keys():
104 for k in self._get_keys():
105 d[k] = getattr(self, k)
105 d[k] = getattr(self, k)
106 return d
106 return d
107
107
108 def get_appstruct(self):
108 def get_appstruct(self):
109 """return list with keys and values tupples corresponding
109 """return list with keys and values tupples corresponding
110 to this model data """
110 to this model data """
111
111
112 l = []
112 l = []
113 for k in self._get_keys():
113 for k in self._get_keys():
114 l.append((k, getattr(self, k),))
114 l.append((k, getattr(self, k),))
115 return l
115 return l
116
116
117 def populate_obj(self, populate_dict):
117 def populate_obj(self, populate_dict):
118 """populate model with data from given populate_dict"""
118 """populate model with data from given populate_dict"""
119
119
120 for k in self._get_keys():
120 for k in self._get_keys():
121 if k in populate_dict:
121 if k in populate_dict:
122 setattr(self, k, populate_dict[k])
122 setattr(self, k, populate_dict[k])
123
123
124 @classmethod
124 @classmethod
125 def query(cls):
125 def query(cls):
126 return Session.query(cls)
126 return Session.query(cls)
127
127
128 @classmethod
128 @classmethod
129 def get(cls, id_):
129 def get(cls, id_):
130 if id_:
130 if id_:
131 return cls.query().get(id_)
131 return cls.query().get(id_)
132
132
133 @classmethod
133 @classmethod
134 def getAll(cls):
134 def getAll(cls):
135 return cls.query().all()
135 return cls.query().all()
136
136
137 @classmethod
137 @classmethod
138 def delete(cls, id_):
138 def delete(cls, id_):
139 obj = cls.query().get(id_)
139 obj = cls.query().get(id_)
140 Session.delete(obj)
140 Session.delete(obj)
141 Session.commit()
141 Session.commit()
142
142
143
143
144 class RhodeCodeSetting(Base, BaseModel):
144 class RhodeCodeSetting(Base, BaseModel):
145 __tablename__ = 'rhodecode_settings'
145 __tablename__ = 'rhodecode_settings'
146 __table_args__ = (UniqueConstraint('app_settings_name'), {'extend_existing':True})
146 __table_args__ = (UniqueConstraint('app_settings_name'), {'extend_existing':True})
147 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
147 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
148 app_settings_name = Column("app_settings_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
148 app_settings_name = Column("app_settings_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
149 _app_settings_value = Column("app_settings_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
149 _app_settings_value = Column("app_settings_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
150
150
151 def __init__(self, k='', v=''):
151 def __init__(self, k='', v=''):
152 self.app_settings_name = k
152 self.app_settings_name = k
153 self.app_settings_value = v
153 self.app_settings_value = v
154
154
155
155
156 @validates('_app_settings_value')
156 @validates('_app_settings_value')
157 def validate_settings_value(self, key, val):
157 def validate_settings_value(self, key, val):
158 assert type(val) == unicode
158 assert type(val) == unicode
159 return val
159 return val
160
160
161 @hybrid_property
161 @hybrid_property
162 def app_settings_value(self):
162 def app_settings_value(self):
163 v = self._app_settings_value
163 v = self._app_settings_value
164 if v == 'ldap_active':
164 if v == 'ldap_active':
165 v = str2bool(v)
165 v = str2bool(v)
166 return v
166 return v
167
167
168 @app_settings_value.setter
168 @app_settings_value.setter
169 def app_settings_value(self, val):
169 def app_settings_value(self, val):
170 """
170 """
171 Setter that will always make sure we use unicode in app_settings_value
171 Setter that will always make sure we use unicode in app_settings_value
172
172
173 :param val:
173 :param val:
174 """
174 """
175 self._app_settings_value = safe_unicode(val)
175 self._app_settings_value = safe_unicode(val)
176
176
177 def __repr__(self):
177 def __repr__(self):
178 return "<%s('%s:%s')>" % (self.__class__.__name__,
178 return "<%s('%s:%s')>" % (self.__class__.__name__,
179 self.app_settings_name, self.app_settings_value)
179 self.app_settings_name, self.app_settings_value)
180
180
181
181
182 @classmethod
182 @classmethod
183 def get_by_name(cls, ldap_key):
183 def get_by_name(cls, ldap_key):
184 return cls.query()\
184 return cls.query()\
185 .filter(cls.app_settings_name == ldap_key).scalar()
185 .filter(cls.app_settings_name == ldap_key).scalar()
186
186
187 @classmethod
187 @classmethod
188 def get_app_settings(cls, cache=False):
188 def get_app_settings(cls, cache=False):
189
189
190 ret = cls.query()
190 ret = cls.query()
191
191
192 if cache:
192 if cache:
193 ret = ret.options(FromCache("sql_cache_short", "get_hg_settings"))
193 ret = ret.options(FromCache("sql_cache_short", "get_hg_settings"))
194
194
195 if not ret:
195 if not ret:
196 raise Exception('Could not get application settings !')
196 raise Exception('Could not get application settings !')
197 settings = {}
197 settings = {}
198 for each in ret:
198 for each in ret:
199 settings['rhodecode_' + each.app_settings_name] = \
199 settings['rhodecode_' + each.app_settings_name] = \
200 each.app_settings_value
200 each.app_settings_value
201
201
202 return settings
202 return settings
203
203
204 @classmethod
204 @classmethod
205 def get_ldap_settings(cls, cache=False):
205 def get_ldap_settings(cls, cache=False):
206 ret = cls.query()\
206 ret = cls.query()\
207 .filter(cls.app_settings_name.startswith('ldap_')).all()
207 .filter(cls.app_settings_name.startswith('ldap_')).all()
208 fd = {}
208 fd = {}
209 for row in ret:
209 for row in ret:
210 fd.update({row.app_settings_name:row.app_settings_value})
210 fd.update({row.app_settings_name:row.app_settings_value})
211
211
212 return fd
212 return fd
213
213
214
214
215 class RhodeCodeUi(Base, BaseModel):
215 class RhodeCodeUi(Base, BaseModel):
216 __tablename__ = 'rhodecode_ui'
216 __tablename__ = 'rhodecode_ui'
217 __table_args__ = (UniqueConstraint('ui_key'), {'extend_existing':True})
217 __table_args__ = (UniqueConstraint('ui_key'), {'extend_existing':True})
218
218
219 HOOK_UPDATE = 'changegroup.update'
219 HOOK_UPDATE = 'changegroup.update'
220 HOOK_REPO_SIZE = 'changegroup.repo_size'
220 HOOK_REPO_SIZE = 'changegroup.repo_size'
221 HOOK_PUSH = 'pretxnchangegroup.push_logger'
221 HOOK_PUSH = 'pretxnchangegroup.push_logger'
222 HOOK_PULL = 'preoutgoing.pull_logger'
222 HOOK_PULL = 'preoutgoing.pull_logger'
223
223
224 ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
224 ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
225 ui_section = Column("ui_section", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
225 ui_section = Column("ui_section", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
226 ui_key = Column("ui_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
226 ui_key = Column("ui_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
227 ui_value = Column("ui_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
227 ui_value = Column("ui_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
228 ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
228 ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
229
229
230
230
231 @classmethod
231 @classmethod
232 def get_by_key(cls, key):
232 def get_by_key(cls, key):
233 return cls.query().filter(cls.ui_key == key)
233 return cls.query().filter(cls.ui_key == key)
234
234
235
235
236 @classmethod
236 @classmethod
237 def get_builtin_hooks(cls):
237 def get_builtin_hooks(cls):
238 q = cls.query()
238 q = cls.query()
239 q = q.filter(cls.ui_key.in_([cls.HOOK_UPDATE,
239 q = q.filter(cls.ui_key.in_([cls.HOOK_UPDATE,
240 cls.HOOK_REPO_SIZE,
240 cls.HOOK_REPO_SIZE,
241 cls.HOOK_PUSH, cls.HOOK_PULL]))
241 cls.HOOK_PUSH, cls.HOOK_PULL]))
242 return q.all()
242 return q.all()
243
243
244 @classmethod
244 @classmethod
245 def get_custom_hooks(cls):
245 def get_custom_hooks(cls):
246 q = cls.query()
246 q = cls.query()
247 q = q.filter(~cls.ui_key.in_([cls.HOOK_UPDATE,
247 q = q.filter(~cls.ui_key.in_([cls.HOOK_UPDATE,
248 cls.HOOK_REPO_SIZE,
248 cls.HOOK_REPO_SIZE,
249 cls.HOOK_PUSH, cls.HOOK_PULL]))
249 cls.HOOK_PUSH, cls.HOOK_PULL]))
250 q = q.filter(cls.ui_section == 'hooks')
250 q = q.filter(cls.ui_section == 'hooks')
251 return q.all()
251 return q.all()
252
252
253 @classmethod
253 @classmethod
254 def create_or_update_hook(cls, key, val):
254 def create_or_update_hook(cls, key, val):
255 new_ui = cls.get_by_key(key).scalar() or cls()
255 new_ui = cls.get_by_key(key).scalar() or cls()
256 new_ui.ui_section = 'hooks'
256 new_ui.ui_section = 'hooks'
257 new_ui.ui_active = True
257 new_ui.ui_active = True
258 new_ui.ui_key = key
258 new_ui.ui_key = key
259 new_ui.ui_value = val
259 new_ui.ui_value = val
260
260
261 Session.add(new_ui)
261 Session.add(new_ui)
262 Session.commit()
262 Session.commit()
263
263
264
264
265 class User(Base, BaseModel):
265 class User(Base, BaseModel):
266 __tablename__ = 'users'
266 __tablename__ = 'users'
267 __table_args__ = (UniqueConstraint('username'), UniqueConstraint('email'), {'extend_existing':True})
267 __table_args__ = (UniqueConstraint('username'), UniqueConstraint('email'), {'extend_existing':True})
268 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
268 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
269 username = Column("username", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
269 username = Column("username", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
270 password = Column("password", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
270 password = Column("password", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
271 active = Column("active", Boolean(), nullable=True, unique=None, default=None)
271 active = Column("active", Boolean(), nullable=True, unique=None, default=None)
272 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
272 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
273 name = Column("name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
273 name = Column("name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
274 lastname = Column("lastname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
274 lastname = Column("lastname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
275 email = Column("email", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
275 email = Column("email", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
276 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
276 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
277 ldap_dn = Column("ldap_dn", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
277 ldap_dn = Column("ldap_dn", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
278 api_key = Column("api_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
278 api_key = Column("api_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
279
279
280 user_log = relationship('UserLog', cascade='all')
280 user_log = relationship('UserLog', cascade='all')
281 user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
281 user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
282
282
283 repositories = relationship('Repository')
283 repositories = relationship('Repository')
284 user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
284 user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
285 repo_to_perm = relationship('UserRepoToPerm', primaryjoin='UserRepoToPerm.user_id==User.user_id', cascade='all')
285 repo_to_perm = relationship('UserRepoToPerm', primaryjoin='UserRepoToPerm.user_id==User.user_id', cascade='all')
286
286
287 group_member = relationship('UsersGroupMember', cascade='all')
287 group_member = relationship('UsersGroupMember', cascade='all')
288
288
289 @property
289 @property
290 def full_contact(self):
290 def full_contact(self):
291 return '%s %s <%s>' % (self.name, self.lastname, self.email)
291 return '%s %s <%s>' % (self.name, self.lastname, self.email)
292
292
293 @property
293 @property
294 def short_contact(self):
294 def short_contact(self):
295 return '%s %s' % (self.name, self.lastname)
295 return '%s %s' % (self.name, self.lastname)
296
296
297 @property
297 @property
298 def is_admin(self):
298 def is_admin(self):
299 return self.admin
299 return self.admin
300
300
301 def __repr__(self):
301 def __repr__(self):
302 try:
302 try:
303 return "<%s('id:%s:%s')>" % (self.__class__.__name__,
303 return "<%s('id:%s:%s')>" % (self.__class__.__name__,
304 self.user_id, self.username)
304 self.user_id, self.username)
305 except:
305 except:
306 return self.__class__.__name__
306 return self.__class__.__name__
307
307
308 @classmethod
308 @classmethod
309 def get_by_username(cls, username, case_insensitive=False, cache=False):
309 def get_by_username(cls, username, case_insensitive=False, cache=False):
310 if case_insensitive:
310 if case_insensitive:
311 q = cls.query().filter(cls.username.ilike(username))
311 q = cls.query().filter(cls.username.ilike(username))
312 else:
312 else:
313 q = cls.query().filter(cls.username == username)
313 q = cls.query().filter(cls.username == username)
314
314
315 if cache:
315 if cache:
316 q = q.options(FromCache("sql_cache_short",
316 q = q.options(FromCache("sql_cache_short",
317 "get_user_%s" % username))
317 "get_user_%s" % username))
318 return q.scalar()
318 return q.scalar()
319
319
320 @classmethod
320 @classmethod
321 def get_by_api_key(cls, api_key, cache=False):
321 def get_by_api_key(cls, api_key, cache=False):
322 q = cls.query().filter(cls.api_key == api_key)
322 q = cls.query().filter(cls.api_key == api_key)
323
323
324 if cache:
324 if cache:
325 q = q.options(FromCache("sql_cache_short",
325 q = q.options(FromCache("sql_cache_short",
326 "get_api_key_%s" % api_key))
326 "get_api_key_%s" % api_key))
327 q.one()
327 q.one()
328
328
329 def update_lastlogin(self):
329 def update_lastlogin(self):
330 """Update user lastlogin"""
330 """Update user lastlogin"""
331
331
332 self.last_login = datetime.datetime.now()
332 self.last_login = datetime.datetime.now()
333 Session.add(self)
333 Session.add(self)
334 Session.commit()
334 Session.commit()
335 log.debug('updated user %s lastlogin', self.username)
335 log.debug('updated user %s lastlogin', self.username)
336
336
337 class UserLog(Base, BaseModel):
337 class UserLog(Base, BaseModel):
338 __tablename__ = 'user_logs'
338 __tablename__ = 'user_logs'
339 __table_args__ = {'extend_existing':True}
339 __table_args__ = {'extend_existing':True}
340 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
340 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
341 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
341 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
342 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
342 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
343 repository_name = Column("repository_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
343 repository_name = Column("repository_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
344 user_ip = Column("user_ip", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
344 user_ip = Column("user_ip", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
345 action = Column("action", UnicodeText(length=1200000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
345 action = Column("action", UnicodeText(length=1200000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
346 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
346 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
347
347
348 @property
348 @property
349 def action_as_day(self):
349 def action_as_day(self):
350 return date(*self.action_date.timetuple()[:3])
350 return date(*self.action_date.timetuple()[:3])
351
351
352 user = relationship('User')
352 user = relationship('User')
353 repository = relationship('Repository')
353 repository = relationship('Repository')
354
354
355
355
356 class UsersGroup(Base, BaseModel):
356 class UsersGroup(Base, BaseModel):
357 __tablename__ = 'users_groups'
357 __tablename__ = 'users_groups'
358 __table_args__ = {'extend_existing':True}
358 __table_args__ = {'extend_existing':True}
359
359
360 users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
360 users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
361 users_group_name = Column("users_group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
361 users_group_name = Column("users_group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
362 users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
362 users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
363
363
364 members = relationship('UsersGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
364 members = relationship('UsersGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
365
365
366 def __repr__(self):
366 def __repr__(self):
367 return '<userGroup(%s)>' % (self.users_group_name)
367 return '<userGroup(%s)>' % (self.users_group_name)
368
368
369 @classmethod
369 @classmethod
370 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
370 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
371 if case_insensitive:
371 if case_insensitive:
372 gr = cls.query()\
372 gr = cls.query()\
373 .filter(cls.users_group_name.ilike(group_name))
373 .filter(cls.users_group_name.ilike(group_name))
374 else:
374 else:
375 gr = cls.query()\
375 gr = cls.query()\
376 .filter(cls.users_group_name == group_name)
376 .filter(cls.users_group_name == group_name)
377 if cache:
377 if cache:
378 gr = gr.options(FromCache("sql_cache_short",
378 gr = gr.options(FromCache("sql_cache_short",
379 "get_user_%s" % group_name))
379 "get_user_%s" % group_name))
380 return gr.scalar()
380 return gr.scalar()
381
381
382
382
383 @classmethod
383 @classmethod
384 def get(cls, users_group_id, cache=False):
384 def get(cls, users_group_id, cache=False):
385 users_group = cls.query()
385 users_group = cls.query()
386 if cache:
386 if cache:
387 users_group = users_group.options(FromCache("sql_cache_short",
387 users_group = users_group.options(FromCache("sql_cache_short",
388 "get_users_group_%s" % users_group_id))
388 "get_users_group_%s" % users_group_id))
389 return users_group.get(users_group_id)
389 return users_group.get(users_group_id)
390
390
391 @classmethod
391 @classmethod
392 def create(cls, form_data):
392 def create(cls, form_data):
393 try:
393 try:
394 new_users_group = cls()
394 new_users_group = cls()
395 for k, v in form_data.items():
395 for k, v in form_data.items():
396 setattr(new_users_group, k, v)
396 setattr(new_users_group, k, v)
397
397
398 Session.add(new_users_group)
398 Session.add(new_users_group)
399 Session.commit()
399 Session.commit()
400 return new_users_group
400 return new_users_group
401 except:
401 except:
402 log.error(traceback.format_exc())
402 log.error(traceback.format_exc())
403 Session.rollback()
403 Session.rollback()
404 raise
404 raise
405
405
406 @classmethod
406 @classmethod
407 def update(cls, users_group_id, form_data):
407 def update(cls, users_group_id, form_data):
408
408
409 try:
409 try:
410 users_group = cls.get(users_group_id, cache=False)
410 users_group = cls.get(users_group_id, cache=False)
411
411
412 for k, v in form_data.items():
412 for k, v in form_data.items():
413 if k == 'users_group_members':
413 if k == 'users_group_members':
414 users_group.members = []
414 users_group.members = []
415 Session.flush()
415 Session.flush()
416 members_list = []
416 members_list = []
417 if v:
417 if v:
418 v = [v] if isinstance(v, basestring) else v
418 v = [v] if isinstance(v, basestring) else v
419 for u_id in set(v):
419 for u_id in set(v):
420 member = UsersGroupMember(users_group_id, u_id)
420 member = UsersGroupMember(users_group_id, u_id)
421 members_list.append(member)
421 members_list.append(member)
422 setattr(users_group, 'members', members_list)
422 setattr(users_group, 'members', members_list)
423 setattr(users_group, k, v)
423 setattr(users_group, k, v)
424
424
425 Session.add(users_group)
425 Session.add(users_group)
426 Session.commit()
426 Session.commit()
427 except:
427 except:
428 log.error(traceback.format_exc())
428 log.error(traceback.format_exc())
429 Session.rollback()
429 Session.rollback()
430 raise
430 raise
431
431
432 @classmethod
432 @classmethod
433 def delete(cls, users_group_id):
433 def delete(cls, users_group_id):
434 try:
434 try:
435
435
436 # check if this group is not assigned to repo
436 # check if this group is not assigned to repo
437 assigned_groups = UsersGroupRepoToPerm.query()\
437 assigned_groups = UsersGroupRepoToPerm.query()\
438 .filter(UsersGroupRepoToPerm.users_group_id ==
438 .filter(UsersGroupRepoToPerm.users_group_id ==
439 users_group_id).all()
439 users_group_id).all()
440
440
441 if assigned_groups:
441 if assigned_groups:
442 raise UsersGroupsAssignedException('RepoGroup assigned to %s' %
442 raise UsersGroupsAssignedException('RepoGroup assigned to %s' %
443 assigned_groups)
443 assigned_groups)
444
444
445 users_group = cls.get(users_group_id, cache=False)
445 users_group = cls.get(users_group_id, cache=False)
446 Session.delete(users_group)
446 Session.delete(users_group)
447 Session.commit()
447 Session.commit()
448 except:
448 except:
449 log.error(traceback.format_exc())
449 log.error(traceback.format_exc())
450 Session.rollback()
450 Session.rollback()
451 raise
451 raise
452
452
453 class UsersGroupMember(Base, BaseModel):
453 class UsersGroupMember(Base, BaseModel):
454 __tablename__ = 'users_groups_members'
454 __tablename__ = 'users_groups_members'
455 __table_args__ = {'extend_existing':True}
455 __table_args__ = {'extend_existing':True}
456
456
457 users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
457 users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
458 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
458 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
459 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
459 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
460
460
461 user = relationship('User', lazy='joined')
461 user = relationship('User', lazy='joined')
462 users_group = relationship('UsersGroup')
462 users_group = relationship('UsersGroup')
463
463
464 def __init__(self, gr_id='', u_id=''):
464 def __init__(self, gr_id='', u_id=''):
465 self.users_group_id = gr_id
465 self.users_group_id = gr_id
466 self.user_id = u_id
466 self.user_id = u_id
467
467
468 @staticmethod
468 @staticmethod
469 def add_user_to_group(group, user):
469 def add_user_to_group(group, user):
470 ugm = UsersGroupMember()
470 ugm = UsersGroupMember()
471 ugm.users_group = group
471 ugm.users_group = group
472 ugm.user = user
472 ugm.user = user
473 Session.add(ugm)
473 Session.add(ugm)
474 Session.commit()
474 Session.commit()
475 return ugm
475 return ugm
476
476
477 class Repository(Base, BaseModel):
477 class Repository(Base, BaseModel):
478 __tablename__ = 'repositories'
478 __tablename__ = 'repositories'
479 __table_args__ = (UniqueConstraint('repo_name'), {'extend_existing':True},)
479 __table_args__ = (UniqueConstraint('repo_name'), {'extend_existing':True},)
480
480
481 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
481 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
482 repo_name = Column("repo_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
482 repo_name = Column("repo_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
483 clone_uri = Column("clone_uri", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
483 clone_uri = Column("clone_uri", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
484 repo_type = Column("repo_type", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default='hg')
484 repo_type = Column("repo_type", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default='hg')
485 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
485 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
486 private = Column("private", Boolean(), nullable=True, unique=None, default=None)
486 private = Column("private", Boolean(), nullable=True, unique=None, default=None)
487 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
487 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
488 enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True)
488 enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True)
489 description = Column("description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
489 description = Column("description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
490 created_on = Column('created_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
490 created_on = Column('created_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
491
491
492 fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
492 fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
493 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
493 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
494
494
495
495
496 user = relationship('User')
496 user = relationship('User')
497 fork = relationship('Repository', remote_side=repo_id)
497 fork = relationship('Repository', remote_side=repo_id)
498 group = relationship('RepoGroup')
498 group = relationship('RepoGroup')
499 repo_to_perm = relationship('UserRepoToPerm', cascade='all', order_by='UserRepoToPerm.repo_to_perm_id')
499 repo_to_perm = relationship('UserRepoToPerm', cascade='all', order_by='UserRepoToPerm.repo_to_perm_id')
500 users_group_to_perm = relationship('UsersGroupRepoToPerm', cascade='all')
500 users_group_to_perm = relationship('UsersGroupRepoToPerm', cascade='all')
501 stats = relationship('Statistics', cascade='all', uselist=False)
501 stats = relationship('Statistics', cascade='all', uselist=False)
502
502
503 followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id', cascade='all')
503 followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id', cascade='all')
504
504
505 logs = relationship('UserLog', cascade='all')
505 logs = relationship('UserLog', cascade='all')
506
506
507 def __repr__(self):
507 def __repr__(self):
508 return "<%s('%s:%s')>" % (self.__class__.__name__,
508 return "<%s('%s:%s')>" % (self.__class__.__name__,
509 self.repo_id, self.repo_name)
509 self.repo_id, self.repo_name)
510
510
511 @classmethod
511 @classmethod
512 def url_sep(cls):
512 def url_sep(cls):
513 return '/'
513 return '/'
514
514
515 @classmethod
515 @classmethod
516 def get_by_repo_name(cls, repo_name):
516 def get_by_repo_name(cls, repo_name):
517 q = Session.query(cls).filter(cls.repo_name == repo_name)
517 q = Session.query(cls).filter(cls.repo_name == repo_name)
518 q = q.options(joinedload(Repository.fork))\
518 q = q.options(joinedload(Repository.fork))\
519 .options(joinedload(Repository.user))\
519 .options(joinedload(Repository.user))\
520 .options(joinedload(Repository.group))
520 .options(joinedload(Repository.group))
521 return q.one()
521 return q.one()
522
522
523 @classmethod
523 @classmethod
524 def get_repo_forks(cls, repo_id):
524 def get_repo_forks(cls, repo_id):
525 return cls.query().filter(Repository.fork_id == repo_id)
525 return cls.query().filter(Repository.fork_id == repo_id)
526
526
527 @classmethod
527 @classmethod
528 def base_path(cls):
528 def base_path(cls):
529 """
529 """
530 Returns base path when all repos are stored
530 Returns base path when all repos are stored
531
531
532 :param cls:
532 :param cls:
533 """
533 """
534 q = Session.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key ==
534 q = Session.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key ==
535 cls.url_sep())
535 cls.url_sep())
536 q.options(FromCache("sql_cache_short", "repository_repo_path"))
536 q.options(FromCache("sql_cache_short", "repository_repo_path"))
537 return q.one().ui_value
537 return q.one().ui_value
538
538
539 @property
539 @property
540 def just_name(self):
540 def just_name(self):
541 return self.repo_name.split(Repository.url_sep())[-1]
541 return self.repo_name.split(Repository.url_sep())[-1]
542
542
543 @property
543 @property
544 def groups_with_parents(self):
544 def groups_with_parents(self):
545 groups = []
545 groups = []
546 if self.group is None:
546 if self.group is None:
547 return groups
547 return groups
548
548
549 cur_gr = self.group
549 cur_gr = self.group
550 groups.insert(0, cur_gr)
550 groups.insert(0, cur_gr)
551 while 1:
551 while 1:
552 gr = getattr(cur_gr, 'parent_group', None)
552 gr = getattr(cur_gr, 'parent_group', None)
553 cur_gr = cur_gr.parent_group
553 cur_gr = cur_gr.parent_group
554 if gr is None:
554 if gr is None:
555 break
555 break
556 groups.insert(0, gr)
556 groups.insert(0, gr)
557
557
558 return groups
558 return groups
559
559
560 @property
560 @property
561 def groups_and_repo(self):
561 def groups_and_repo(self):
562 return self.groups_with_parents, self.just_name
562 return self.groups_with_parents, self.just_name
563
563
564 @LazyProperty
564 @LazyProperty
565 def repo_path(self):
565 def repo_path(self):
566 """
566 """
567 Returns base full path for that repository means where it actually
567 Returns base full path for that repository means where it actually
568 exists on a filesystem
568 exists on a filesystem
569 """
569 """
570 q = Session.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key ==
570 q = Session.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key ==
571 Repository.url_sep())
571 Repository.url_sep())
572 q.options(FromCache("sql_cache_short", "repository_repo_path"))
572 q.options(FromCache("sql_cache_short", "repository_repo_path"))
573 return q.one().ui_value
573 return q.one().ui_value
574
574
575 @property
575 @property
576 def repo_full_path(self):
576 def repo_full_path(self):
577 p = [self.repo_path]
577 p = [self.repo_path]
578 # we need to split the name by / since this is how we store the
578 # we need to split the name by / since this is how we store the
579 # names in the database, but that eventually needs to be converted
579 # names in the database, but that eventually needs to be converted
580 # into a valid system path
580 # into a valid system path
581 p += self.repo_name.split(Repository.url_sep())
581 p += self.repo_name.split(Repository.url_sep())
582 return os.path.join(*p)
582 return os.path.join(*p)
583
583
584 def get_new_name(self, repo_name):
584 def get_new_name(self, repo_name):
585 """
585 """
586 returns new full repository name based on assigned group and new new
586 returns new full repository name based on assigned group and new new
587
587
588 :param group_name:
588 :param group_name:
589 """
589 """
590 path_prefix = self.group.full_path_splitted if self.group else []
590 path_prefix = self.group.full_path_splitted if self.group else []
591 return Repository.url_sep().join(path_prefix + [repo_name])
591 return Repository.url_sep().join(path_prefix + [repo_name])
592
592
593 @property
593 @property
594 def _ui(self):
594 def _ui(self):
595 """
595 """
596 Creates an db based ui object for this repository
596 Creates an db based ui object for this repository
597 """
597 """
598 from mercurial import ui
598 from mercurial import ui
599 from mercurial import config
599 from mercurial import config
600 baseui = ui.ui()
600 baseui = ui.ui()
601
601
602 #clean the baseui object
602 #clean the baseui object
603 baseui._ocfg = config.config()
603 baseui._ocfg = config.config()
604 baseui._ucfg = config.config()
604 baseui._ucfg = config.config()
605 baseui._tcfg = config.config()
605 baseui._tcfg = config.config()
606
606
607
607
608 ret = RhodeCodeUi.query()\
608 ret = RhodeCodeUi.query()\
609 .options(FromCache("sql_cache_short", "repository_repo_ui")).all()
609 .options(FromCache("sql_cache_short", "repository_repo_ui")).all()
610
610
611 hg_ui = ret
611 hg_ui = ret
612 for ui_ in hg_ui:
612 for ui_ in hg_ui:
613 if ui_.ui_active:
613 if ui_.ui_active:
614 log.debug('settings ui from db[%s]%s:%s', ui_.ui_section,
614 log.debug('settings ui from db[%s]%s:%s', ui_.ui_section,
615 ui_.ui_key, ui_.ui_value)
615 ui_.ui_key, ui_.ui_value)
616 baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
616 baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
617
617
618 return baseui
618 return baseui
619
619
620 @classmethod
620 @classmethod
621 def is_valid(cls, repo_name):
621 def is_valid(cls, repo_name):
622 """
622 """
623 returns True if given repo name is a valid filesystem repository
623 returns True if given repo name is a valid filesystem repository
624
624
625 @param cls:
625 @param cls:
626 @param repo_name:
626 @param repo_name:
627 """
627 """
628 from rhodecode.lib.utils import is_valid_repo
628 from rhodecode.lib.utils import is_valid_repo
629
629
630 return is_valid_repo(repo_name, cls.base_path())
630 return is_valid_repo(repo_name, cls.base_path())
631
631
632
632
633 #==========================================================================
633 #==========================================================================
634 # SCM PROPERTIES
634 # SCM PROPERTIES
635 #==========================================================================
635 #==========================================================================
636
636
637 def get_changeset(self, rev):
637 def get_changeset(self, rev):
638 return get_changeset_safe(self.scm_instance, rev)
638 return get_changeset_safe(self.scm_instance, rev)
639
639
640 @property
640 @property
641 def tip(self):
641 def tip(self):
642 return self.get_changeset('tip')
642 return self.get_changeset('tip')
643
643
644 @property
644 @property
645 def author(self):
645 def author(self):
646 return self.tip.author
646 return self.tip.author
647
647
648 @property
648 @property
649 def last_change(self):
649 def last_change(self):
650 return self.scm_instance.last_change
650 return self.scm_instance.last_change
651
651
652 #==========================================================================
652 #==========================================================================
653 # SCM CACHE INSTANCE
653 # SCM CACHE INSTANCE
654 #==========================================================================
654 #==========================================================================
655
655
656 @property
656 @property
657 def invalidate(self):
657 def invalidate(self):
658 return CacheInvalidation.invalidate(self.repo_name)
658 return CacheInvalidation.invalidate(self.repo_name)
659
659
660 def set_invalidate(self):
660 def set_invalidate(self):
661 """
661 """
662 set a cache for invalidation for this instance
662 set a cache for invalidation for this instance
663 """
663 """
664 CacheInvalidation.set_invalidate(self.repo_name)
664 CacheInvalidation.set_invalidate(self.repo_name)
665
665
666 @LazyProperty
666 @LazyProperty
667 def scm_instance(self):
667 def scm_instance(self):
668 return self.__get_instance()
668 return self.__get_instance()
669
669
670 @property
670 @property
671 def scm_instance_cached(self):
671 def scm_instance_cached(self):
672 @cache_region('long_term')
672 @cache_region('long_term')
673 def _c(repo_name):
673 def _c(repo_name):
674 return self.__get_instance()
674 return self.__get_instance()
675 rn = self.repo_name
675 rn = self.repo_name
676
676
677 inv = self.invalidate
677 inv = self.invalidate
678 if inv is not None:
678 if inv is not None:
679 region_invalidate(_c, None, rn)
679 region_invalidate(_c, None, rn)
680 # update our cache
680 # update our cache
681 CacheInvalidation.set_valid(inv.cache_key)
681 CacheInvalidation.set_valid(inv.cache_key)
682 return _c(rn)
682 return _c(rn)
683
683
684 def __get_instance(self):
684 def __get_instance(self):
685
685
686 repo_full_path = self.repo_full_path
686 repo_full_path = self.repo_full_path
687
687
688 try:
688 try:
689 alias = get_scm(repo_full_path)[0]
689 alias = get_scm(repo_full_path)[0]
690 log.debug('Creating instance of %s repository', alias)
690 log.debug('Creating instance of %s repository', alias)
691 backend = get_backend(alias)
691 backend = get_backend(alias)
692 except VCSError:
692 except VCSError:
693 log.error(traceback.format_exc())
693 log.error(traceback.format_exc())
694 log.error('Perhaps this repository is in db and not in '
694 log.error('Perhaps this repository is in db and not in '
695 'filesystem run rescan repositories with '
695 'filesystem run rescan repositories with '
696 '"destroy old data " option from admin panel')
696 '"destroy old data " option from admin panel')
697 return
697 return
698
698
699 if alias == 'hg':
699 if alias == 'hg':
700
700
701 repo = backend(safe_str(repo_full_path), create=False,
701 repo = backend(safe_str(repo_full_path), create=False,
702 baseui=self._ui)
702 baseui=self._ui)
703 # skip hidden web repository
703 # skip hidden web repository
704 if repo._get_hidden():
704 if repo._get_hidden():
705 return
705 return
706 else:
706 else:
707 repo = backend(repo_full_path, create=False)
707 repo = backend(repo_full_path, create=False)
708
708
709 return repo
709 return repo
710
710
711
711
712 class RepoGroup(Base, BaseModel):
712 class RepoGroup(Base, BaseModel):
713 __tablename__ = 'groups'
713 __tablename__ = 'groups'
714 __table_args__ = (UniqueConstraint('group_name', 'group_parent_id'),
714 __table_args__ = (UniqueConstraint('group_name', 'group_parent_id'),
715 CheckConstraint('group_id != group_parent_id'), {'extend_existing':True},)
715 CheckConstraint('group_id != group_parent_id'), {'extend_existing':True},)
716 __mapper_args__ = {'order_by':'group_name'}
716 __mapper_args__ = {'order_by':'group_name'}
717
717
718 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
718 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
719 group_name = Column("group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
719 group_name = Column("group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
720 group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
720 group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
721 group_description = Column("group_description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
721 group_description = Column("group_description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
722
722
723 parent_group = relationship('RepoGroup', remote_side=group_id)
723 parent_group = relationship('RepoGroup', remote_side=group_id)
724
724
725
725
726 def __init__(self, group_name='', parent_group=None):
726 def __init__(self, group_name='', parent_group=None):
727 self.group_name = group_name
727 self.group_name = group_name
728 self.parent_group = parent_group
728 self.parent_group = parent_group
729
729
730 def __repr__(self):
730 def __repr__(self):
731 return "<%s('%s:%s')>" % (self.__class__.__name__, self.group_id,
731 return "<%s('%s:%s')>" % (self.__class__.__name__, self.group_id,
732 self.group_name)
732 self.group_name)
733
733
734 @classmethod
734 @classmethod
735 def groups_choices(cls):
735 def groups_choices(cls):
736 from webhelpers.html import literal as _literal
736 from webhelpers.html import literal as _literal
737 repo_groups = [('', '')]
737 repo_groups = [('', '')]
738 sep = ' &raquo; '
738 sep = ' &raquo; '
739 _name = lambda k: _literal(sep.join(k))
739 _name = lambda k: _literal(sep.join(k))
740
740
741 repo_groups.extend([(x.group_id, _name(x.full_path_splitted))
741 repo_groups.extend([(x.group_id, _name(x.full_path_splitted))
742 for x in cls.query().all()])
742 for x in cls.query().all()])
743
743
744 repo_groups = sorted(repo_groups, key=lambda t: t[1].split(sep)[0])
744 repo_groups = sorted(repo_groups, key=lambda t: t[1].split(sep)[0])
745 return repo_groups
745 return repo_groups
746
746
747 @classmethod
747 @classmethod
748 def url_sep(cls):
748 def url_sep(cls):
749 return '/'
749 return '/'
750
750
751 @classmethod
751 @classmethod
752 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
752 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
753 if case_insensitive:
753 if case_insensitive:
754 gr = cls.query()\
754 gr = cls.query()\
755 .filter(cls.group_name.ilike(group_name))
755 .filter(cls.group_name.ilike(group_name))
756 else:
756 else:
757 gr = cls.query()\
757 gr = cls.query()\
758 .filter(cls.group_name == group_name)
758 .filter(cls.group_name == group_name)
759 if cache:
759 if cache:
760 gr = gr.options(FromCache("sql_cache_short",
760 gr = gr.options(FromCache("sql_cache_short",
761 "get_group_%s" % group_name))
761 "get_group_%s" % group_name))
762 return gr.scalar()
762 return gr.scalar()
763
763
764 @property
764 @property
765 def parents(self):
765 def parents(self):
766 parents_recursion_limit = 5
766 parents_recursion_limit = 5
767 groups = []
767 groups = []
768 if self.parent_group is None:
768 if self.parent_group is None:
769 return groups
769 return groups
770 cur_gr = self.parent_group
770 cur_gr = self.parent_group
771 groups.insert(0, cur_gr)
771 groups.insert(0, cur_gr)
772 cnt = 0
772 cnt = 0
773 while 1:
773 while 1:
774 cnt += 1
774 cnt += 1
775 gr = getattr(cur_gr, 'parent_group', None)
775 gr = getattr(cur_gr, 'parent_group', None)
776 cur_gr = cur_gr.parent_group
776 cur_gr = cur_gr.parent_group
777 if gr is None:
777 if gr is None:
778 break
778 break
779 if cnt == parents_recursion_limit:
779 if cnt == parents_recursion_limit:
780 # this will prevent accidental infinit loops
780 # this will prevent accidental infinit loops
781 log.error('group nested more than %s' %
781 log.error('group nested more than %s' %
782 parents_recursion_limit)
782 parents_recursion_limit)
783 break
783 break
784
784
785 groups.insert(0, gr)
785 groups.insert(0, gr)
786 return groups
786 return groups
787
787
788 @property
788 @property
789 def children(self):
789 def children(self):
790 return RepoGroup.query().filter(RepoGroup.parent_group == self)
790 return RepoGroup.query().filter(RepoGroup.parent_group == self)
791
791
792 @property
792 @property
793 def name(self):
793 def name(self):
794 return self.group_name.split(RepoGroup.url_sep())[-1]
794 return self.group_name.split(RepoGroup.url_sep())[-1]
795
795
796 @property
796 @property
797 def full_path(self):
797 def full_path(self):
798 return self.group_name
798 return self.group_name
799
799
800 @property
800 @property
801 def full_path_splitted(self):
801 def full_path_splitted(self):
802 return self.group_name.split(RepoGroup.url_sep())
802 return self.group_name.split(RepoGroup.url_sep())
803
803
804 @property
804 @property
805 def repositories(self):
805 def repositories(self):
806 return Repository.query().filter(Repository.group == self)
806 return Repository.query().filter(Repository.group == self)
807
807
808 @property
808 @property
809 def repositories_recursive_count(self):
809 def repositories_recursive_count(self):
810 cnt = self.repositories.count()
810 cnt = self.repositories.count()
811
811
812 def children_count(group):
812 def children_count(group):
813 cnt = 0
813 cnt = 0
814 for child in group.children:
814 for child in group.children:
815 cnt += child.repositories.count()
815 cnt += child.repositories.count()
816 cnt += children_count(child)
816 cnt += children_count(child)
817 return cnt
817 return cnt
818
818
819 return cnt + children_count(self)
819 return cnt + children_count(self)
820
820
821
821
822 def get_new_name(self, group_name):
822 def get_new_name(self, group_name):
823 """
823 """
824 returns new full group name based on parent and new name
824 returns new full group name based on parent and new name
825
825
826 :param group_name:
826 :param group_name:
827 """
827 """
828 path_prefix = (self.parent_group.full_path_splitted if
828 path_prefix = (self.parent_group.full_path_splitted if
829 self.parent_group else [])
829 self.parent_group else [])
830 return RepoGroup.url_sep().join(path_prefix + [group_name])
830 return RepoGroup.url_sep().join(path_prefix + [group_name])
831
831
832
832
833 class Permission(Base, BaseModel):
833 class Permission(Base, BaseModel):
834 __tablename__ = 'permissions'
834 __tablename__ = 'permissions'
835 __table_args__ = {'extend_existing':True}
835 __table_args__ = {'extend_existing':True}
836 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
836 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
837 permission_name = Column("permission_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
837 permission_name = Column("permission_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
838 permission_longname = Column("permission_longname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
838 permission_longname = Column("permission_longname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
839
839
840 def __repr__(self):
840 def __repr__(self):
841 return "<%s('%s:%s')>" % (self.__class__.__name__,
841 return "<%s('%s:%s')>" % (self.__class__.__name__,
842 self.permission_id, self.permission_name)
842 self.permission_id, self.permission_name)
843
843
844 @classmethod
844 @classmethod
845 def get_by_key(cls, key):
845 def get_by_key(cls, key):
846 return cls.query().filter(cls.permission_name == key).scalar()
846 return cls.query().filter(cls.permission_name == key).scalar()
847
847
848 class UserRepoToPerm(Base, BaseModel):
848 class UserRepoToPerm(Base, BaseModel):
849 __tablename__ = 'repo_to_perm'
849 __tablename__ = 'repo_to_perm'
850 __table_args__ = (UniqueConstraint('user_id', 'repository_id'), {'extend_existing':True})
850 __table_args__ = (UniqueConstraint('user_id', 'repository_id'), {'extend_existing':True})
851 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
851 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
852 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
852 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
853 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
853 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
854 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
854 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
855
855
856 user = relationship('User')
856 user = relationship('User')
857 permission = relationship('Permission')
857 permission = relationship('Permission')
858 repository = relationship('Repository')
858 repository = relationship('Repository')
859
859
860 class UserToPerm(Base, BaseModel):
860 class UserToPerm(Base, BaseModel):
861 __tablename__ = 'user_to_perm'
861 __tablename__ = 'user_to_perm'
862 __table_args__ = (UniqueConstraint('user_id', 'permission_id'), {'extend_existing':True})
862 __table_args__ = (UniqueConstraint('user_id', 'permission_id'), {'extend_existing':True})
863 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
863 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
864 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
864 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
865 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
865 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
866
866
867 user = relationship('User')
867 user = relationship('User')
868 permission = relationship('Permission')
868 permission = relationship('Permission')
869
869
870 @classmethod
870 @classmethod
871 def has_perm(cls, user_id, perm):
871 def has_perm(cls, user_id, perm):
872 if not isinstance(perm, Permission):
872 if not isinstance(perm, Permission):
873 raise Exception('perm needs to be an instance of Permission class')
873 raise Exception('perm needs to be an instance of Permission class')
874
874
875 return cls.query().filter(cls.user_id == user_id)\
875 return cls.query().filter(cls.user_id == user_id)\
876 .filter(cls.permission == perm).scalar() is not None
876 .filter(cls.permission == perm).scalar() is not None
877
877
878 @classmethod
878 @classmethod
879 def grant_perm(cls, user_id, perm):
879 def grant_perm(cls, user_id, perm):
880 if not isinstance(perm, Permission):
880 if not isinstance(perm, Permission):
881 raise Exception('perm needs to be an instance of Permission class')
881 raise Exception('perm needs to be an instance of Permission class')
882
882
883 new = cls()
883 new = cls()
884 new.user_id = user_id
884 new.user_id = user_id
885 new.permission = perm
885 new.permission = perm
886 try:
886 try:
887 Session.add(new)
887 Session.add(new)
888 Session.commit()
888 Session.commit()
889 except:
889 except:
890 Session.rollback()
890 Session.rollback()
891
891
892
892
893 @classmethod
893 @classmethod
894 def revoke_perm(cls, user_id, perm):
894 def revoke_perm(cls, user_id, perm):
895 if not isinstance(perm, Permission):
895 if not isinstance(perm, Permission):
896 raise Exception('perm needs to be an instance of Permission class')
896 raise Exception('perm needs to be an instance of Permission class')
897
897
898 try:
898 try:
899 cls.query().filter(cls.user_id == user_id)\
899 cls.query().filter(cls.user_id == user_id)\
900 .filter(cls.permission == perm).delete()
900 .filter(cls.permission == perm).delete()
901 Session.commit()
901 Session.commit()
902 except:
902 except:
903 Session.rollback()
903 Session.rollback()
904
904
905 class UsersGroupRepoToPerm(Base, BaseModel):
905 class UsersGroupRepoToPerm(Base, BaseModel):
906 __tablename__ = 'users_group_repo_to_perm'
906 __tablename__ = 'users_group_repo_to_perm'
907 __table_args__ = (UniqueConstraint('repository_id', 'users_group_id', 'permission_id'), {'extend_existing':True})
907 __table_args__ = (UniqueConstraint('repository_id', 'users_group_id', 'permission_id'), {'extend_existing':True})
908 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
908 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
909 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
909 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
910 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
910 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
911 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
911 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
912
912
913 users_group = relationship('UsersGroup')
913 users_group = relationship('UsersGroup')
914 permission = relationship('Permission')
914 permission = relationship('Permission')
915 repository = relationship('Repository')
915 repository = relationship('Repository')
916
916
917 def __repr__(self):
917 def __repr__(self):
918 return '<userGroup:%s => %s >' % (self.users_group, self.repository)
918 return '<userGroup:%s => %s >' % (self.users_group, self.repository)
919
919
920 class UsersGroupToPerm(Base, BaseModel):
920 class UsersGroupToPerm(Base, BaseModel):
921 __tablename__ = 'users_group_to_perm'
921 __tablename__ = 'users_group_to_perm'
922 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
922 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
923 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
923 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
924 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
924 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
925
925
926 users_group = relationship('UsersGroup')
926 users_group = relationship('UsersGroup')
927 permission = relationship('Permission')
927 permission = relationship('Permission')
928
928
929
929
930 @classmethod
930 @classmethod
931 def has_perm(cls, users_group_id, perm):
931 def has_perm(cls, users_group_id, perm):
932 if not isinstance(perm, Permission):
932 if not isinstance(perm, Permission):
933 raise Exception('perm needs to be an instance of Permission class')
933 raise Exception('perm needs to be an instance of Permission class')
934
934
935 return cls.query().filter(cls.users_group_id ==
935 return cls.query().filter(cls.users_group_id ==
936 users_group_id)\
936 users_group_id)\
937 .filter(cls.permission == perm)\
937 .filter(cls.permission == perm)\
938 .scalar() is not None
938 .scalar() is not None
939
939
940 @classmethod
940 @classmethod
941 def grant_perm(cls, users_group_id, perm):
941 def grant_perm(cls, users_group_id, perm):
942 if not isinstance(perm, Permission):
942 if not isinstance(perm, Permission):
943 raise Exception('perm needs to be an instance of Permission class')
943 raise Exception('perm needs to be an instance of Permission class')
944
944
945 new = cls()
945 new = cls()
946 new.users_group_id = users_group_id
946 new.users_group_id = users_group_id
947 new.permission = perm
947 new.permission = perm
948 try:
948 try:
949 Session.add(new)
949 Session.add(new)
950 Session.commit()
950 Session.commit()
951 except:
951 except:
952 Session.rollback()
952 Session.rollback()
953
953
954
954
955 @classmethod
955 @classmethod
956 def revoke_perm(cls, users_group_id, perm):
956 def revoke_perm(cls, users_group_id, perm):
957 if not isinstance(perm, Permission):
957 if not isinstance(perm, Permission):
958 raise Exception('perm needs to be an instance of Permission class')
958 raise Exception('perm needs to be an instance of Permission class')
959
959
960 try:
960 try:
961 cls.query().filter(cls.users_group_id == users_group_id)\
961 cls.query().filter(cls.users_group_id == users_group_id)\
962 .filter(cls.permission == perm).delete()
962 .filter(cls.permission == perm).delete()
963 Session.commit()
963 Session.commit()
964 except:
964 except:
965 Session.rollback()
965 Session.rollback()
966
966
967
967
968 class UserRepoGroupToPerm(Base, BaseModel):
968 class UserRepoGroupToPerm(Base, BaseModel):
969 __tablename__ = 'group_to_perm'
969 __tablename__ = 'group_to_perm'
970 __table_args__ = (UniqueConstraint('group_id', 'permission_id'), {'extend_existing':True})
970 __table_args__ = (UniqueConstraint('group_id', 'permission_id'), {'extend_existing':True})
971
971
972 group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
972 group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
973 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
973 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
974 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
974 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
975 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
975 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
976
976
977 user = relationship('User')
977 user = relationship('User')
978 permission = relationship('Permission')
978 permission = relationship('Permission')
979 group = relationship('RepoGroup')
979 group = relationship('RepoGroup')
980
980
981 class UsersGroupRepoGroupToPerm(Base, BaseModel):
981 class UsersGroupRepoGroupToPerm(Base, BaseModel):
982 __tablename__ = 'users_group_repo_group_to_perm'
982 __tablename__ = 'users_group_repo_group_to_perm'
983 __table_args__ = (UniqueConstraint('group_id', 'permission_id'), {'extend_existing':True})
983 __table_args__ = (UniqueConstraint('group_id', 'permission_id'), {'extend_existing':True})
984
984
985 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)
985 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)
986 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
986 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
987 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
987 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
988 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
988 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
989
989
990 users_group = relationship('UsersGroup')
990 users_group = relationship('UsersGroup')
991 permission = relationship('Permission')
991 permission = relationship('Permission')
992 group = relationship('RepoGroup')
992 group = relationship('RepoGroup')
993
993
994 class Statistics(Base, BaseModel):
994 class Statistics(Base, BaseModel):
995 __tablename__ = 'statistics'
995 __tablename__ = 'statistics'
996 __table_args__ = (UniqueConstraint('repository_id'), {'extend_existing':True})
996 __table_args__ = (UniqueConstraint('repository_id'), {'extend_existing':True})
997 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
997 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
998 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
998 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
999 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
999 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
1000 commit_activity = Column("commit_activity", LargeBinary(1000000), nullable=False)#JSON data
1000 commit_activity = Column("commit_activity", LargeBinary(1000000), nullable=False)#JSON data
1001 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
1001 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
1002 languages = Column("languages", LargeBinary(1000000), nullable=False)#JSON data
1002 languages = Column("languages", LargeBinary(1000000), nullable=False)#JSON data
1003
1003
1004 repository = relationship('Repository', single_parent=True)
1004 repository = relationship('Repository', single_parent=True)
1005
1005
1006 class UserFollowing(Base, BaseModel):
1006 class UserFollowing(Base, BaseModel):
1007 __tablename__ = 'user_followings'
1007 __tablename__ = 'user_followings'
1008 __table_args__ = (UniqueConstraint('user_id', 'follows_repository_id'),
1008 __table_args__ = (UniqueConstraint('user_id', 'follows_repository_id'),
1009 UniqueConstraint('user_id', 'follows_user_id')
1009 UniqueConstraint('user_id', 'follows_user_id')
1010 , {'extend_existing':True})
1010 , {'extend_existing':True})
1011
1011
1012 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1012 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1013 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1013 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1014 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
1014 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
1015 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
1015 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
1016 follows_from = Column('follows_from', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
1016 follows_from = Column('follows_from', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
1017
1017
1018 user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id')
1018 user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id')
1019
1019
1020 follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
1020 follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
1021 follows_repository = relationship('Repository', order_by='Repository.repo_name')
1021 follows_repository = relationship('Repository', order_by='Repository.repo_name')
1022
1022
1023
1023
1024 @classmethod
1024 @classmethod
1025 def get_repo_followers(cls, repo_id):
1025 def get_repo_followers(cls, repo_id):
1026 return cls.query().filter(cls.follows_repo_id == repo_id)
1026 return cls.query().filter(cls.follows_repo_id == repo_id)
1027
1027
1028 class CacheInvalidation(Base, BaseModel):
1028 class CacheInvalidation(Base, BaseModel):
1029 __tablename__ = 'cache_invalidation'
1029 __tablename__ = 'cache_invalidation'
1030 __table_args__ = (UniqueConstraint('cache_key'), {'extend_existing':True})
1030 __table_args__ = (UniqueConstraint('cache_key'), {'extend_existing':True})
1031 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1031 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1032 cache_key = Column("cache_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1032 cache_key = Column("cache_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1033 cache_args = Column("cache_args", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1033 cache_args = Column("cache_args", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1034 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
1034 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
1035
1035
1036
1036
1037 def __init__(self, cache_key, cache_args=''):
1037 def __init__(self, cache_key, cache_args=''):
1038 self.cache_key = cache_key
1038 self.cache_key = cache_key
1039 self.cache_args = cache_args
1039 self.cache_args = cache_args
1040 self.cache_active = False
1040 self.cache_active = False
1041
1041
1042 def __repr__(self):
1042 def __repr__(self):
1043 return "<%s('%s:%s')>" % (self.__class__.__name__,
1043 return "<%s('%s:%s')>" % (self.__class__.__name__,
1044 self.cache_id, self.cache_key)
1044 self.cache_id, self.cache_key)
1045
1045
1046 @classmethod
1046 @classmethod
1047 def invalidate(cls, key):
1047 def invalidate(cls, key):
1048 """
1048 """
1049 Returns Invalidation object if this given key should be invalidated
1049 Returns Invalidation object if this given key should be invalidated
1050 None otherwise. `cache_active = False` means that this cache
1050 None otherwise. `cache_active = False` means that this cache
1051 state is not valid and needs to be invalidated
1051 state is not valid and needs to be invalidated
1052
1052
1053 :param key:
1053 :param key:
1054 """
1054 """
1055 return cls.query()\
1055 return cls.query()\
1056 .filter(CacheInvalidation.cache_key == key)\
1056 .filter(CacheInvalidation.cache_key == key)\
1057 .filter(CacheInvalidation.cache_active == False)\
1057 .filter(CacheInvalidation.cache_active == False)\
1058 .scalar()
1058 .scalar()
1059
1059
1060 @classmethod
1060 @classmethod
1061 def set_invalidate(cls, key):
1061 def set_invalidate(cls, key):
1062 """
1062 """
1063 Mark this Cache key for invalidation
1063 Mark this Cache key for invalidation
1064
1064
1065 :param key:
1065 :param key:
1066 """
1066 """
1067
1067
1068 log.debug('marking %s for invalidation' % key)
1068 log.debug('marking %s for invalidation' % key)
1069 inv_obj = Session().query(cls)\
1069 inv_obj = Session().query(cls)\
1070 .filter(cls.cache_key == key).scalar()
1070 .filter(cls.cache_key == key).scalar()
1071 if inv_obj:
1071 if inv_obj:
1072 inv_obj.cache_active = False
1072 inv_obj.cache_active = False
1073 else:
1073 else:
1074 log.debug('cache key not found in invalidation db -> creating one')
1074 log.debug('cache key not found in invalidation db -> creating one')
1075 inv_obj = CacheInvalidation(key)
1075 inv_obj = CacheInvalidation(key)
1076
1076
1077 try:
1077 try:
1078 Session.add(inv_obj)
1078 Session.add(inv_obj)
1079 Session.commit()
1079 Session.commit()
1080 except Exception:
1080 except Exception:
1081 log.error(traceback.format_exc())
1081 log.error(traceback.format_exc())
1082 Session.rollback()
1082 Session.rollback()
1083
1083
1084 @classmethod
1084 @classmethod
1085 def set_valid(cls, key):
1085 def set_valid(cls, key):
1086 """
1086 """
1087 Mark this cache key as active and currently cached
1087 Mark this cache key as active and currently cached
1088
1088
1089 :param key:
1089 :param key:
1090 """
1090 """
1091 inv_obj = Session().query(CacheInvalidation)\
1091 inv_obj = Session().query(CacheInvalidation)\
1092 .filter(CacheInvalidation.cache_key == key).scalar()
1092 .filter(CacheInvalidation.cache_key == key).scalar()
1093 inv_obj.cache_active = True
1093 inv_obj.cache_active = True
1094 Session.add(inv_obj)
1094 Session.add(inv_obj)
1095 Session.commit()
1095 Session.commit()
1096
1096
1097
1097
1098 class ChangesetComment(Base, BaseModel):
1098 class ChangesetComment(Base, BaseModel):
1099 __tablename__ = 'changeset_comments'
1099 __tablename__ = 'changeset_comments'
1100 __table_args__ = ({'extend_existing':True},)
1100 __table_args__ = ({'extend_existing':True},)
1101 comment_id = Column('comment_id', Integer(), nullable=False, primary_key=True)
1101 comment_id = Column('comment_id', Integer(), nullable=False, primary_key=True)
1102 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1102 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1103 commit_id = Column('commit_id', String(100), nullable=False)
1103 revision = Column('revision', String(40), nullable=False)
1104 line_no = Column('line_no', Integer(), nullable=True)
1104 line_no = Column('line_no', Integer(), nullable=True)
1105 f_path = Column('f_path', String(1000), nullable=True)
1105 f_path = Column('f_path', String(1000), nullable=True)
1106 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=False)
1106 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=False)
1107 text = Column('text', String(25000), nullable=False)
1107 text = Column('text', String(25000), nullable=False)
1108 modified_at = Column('modified_at', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1108 modified_at = Column('modified_at', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1109
1109
1110 author = relationship('User')
1110 author = relationship('User')
1111 repo = relationship('Repository')
1111 repo = relationship('Repository')
1112
1112
1113
1113
1114 class DbMigrateVersion(Base, BaseModel):
1114 class DbMigrateVersion(Base, BaseModel):
1115 __tablename__ = 'db_migrate_version'
1115 __tablename__ = 'db_migrate_version'
1116 __table_args__ = {'extend_existing':True}
1116 __table_args__ = {'extend_existing':True}
1117 repository_id = Column('repository_id', String(250), primary_key=True)
1117 repository_id = Column('repository_id', String(250), primary_key=True)
1118 repository_path = Column('repository_path', Text)
1118 repository_path = Column('repository_path', Text)
1119 version = Column('version', Integer)
1119 version = Column('version', Integer)
1120
1120
@@ -1,31 +1,31 b''
1 ##usage:
1 ##usage:
2 ## <%namespace name="comment" file="/changeset/changeset_file_comment.html"/>
2 ## <%namespace name="comment" file="/changeset/changeset_file_comment.html"/>
3 ## ${comment.comment_block(co)}
3 ## ${comment.comment_block(co)}
4 ##
4 ##
5 <%def name="comment_block(co)">
5 <%def name="comment_block(co)">
6 <div class="comment" id="comment-${co.comment_id}">
6 <div class="comment" id="comment-${co.comment_id}">
7 <div class="meta">
7 <div class="meta">
8 <span class="user">
8 <span class="user">
9 <img src="${h.gravatar_url(co.author.email, 20)}" />
9 <img src="${h.gravatar_url(co.author.email, 20)}" />
10 ${co.author.username}
10 ${co.author.username}
11 </span>
11 </span>
12 <a href="${h.url.current(anchor='comment-%s' % co.comment_id)}"> ${_('commented on')} </a>
12 <a href="${h.url.current(anchor='comment-%s' % co.comment_id)}"> ${_('commented on')} </a>
13 ${h.short_id(co.commit_id)}
13 ${h.short_id(co.revision)}
14 %if co.f_path:
14 %if co.f_path:
15 ${_(' in file ')}
15 ${_(' in file ')}
16 ${co.f_path}:L${co.line_no}
16 ${co.f_path}:L${co.line_no}
17 %endif
17 %endif
18 <span class="date">
18 <span class="date">
19 ${h.age(co.modified_at)}
19 ${h.age(co.modified_at)}
20 </span>
20 </span>
21 </div>
21 </div>
22 <div class="text">
22 <div class="text">
23 %if h.HasPermissionAny('hg.admin', 'repository.admin')() or co.author.user_id == c.rhodecode_user.user_id:
23 %if h.HasPermissionAny('hg.admin', 'repository.admin')() or co.author.user_id == c.rhodecode_user.user_id:
24 <div class="buttons">
24 <div class="buttons">
25 <span onClick="deleteComment(${co.comment_id})" class="delete-comment ui-button-small">${_('Delete')}</span>
25 <span onClick="deleteComment(${co.comment_id})" class="delete-comment ui-button-small">${_('Delete')}</span>
26 </div>
26 </div>
27 %endif
27 %endif
28 ${h.rst(co.text)|n}
28 ${h.rst(co.text)|n}
29 </div>
29 </div>
30 </div>
30 </div>
31 </%def> No newline at end of file
31 </%def>
General Comments 0
You need to be logged in to leave comments. Login now