##// END OF EJS Templates
White space cleanup
marcink -
r2815:acc05c33 beta
parent child Browse files
Show More
@@ -1,447 +1,447 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) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
11 :copyright: (C) 2010-2012 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 from collections import defaultdict
28 from collections import defaultdict
29 from webob.exc import HTTPForbidden
29 from webob.exc import HTTPForbidden
30
30
31 from pylons import tmpl_context as c, url, request, response
31 from pylons import tmpl_context as c, url, request, response
32 from pylons.i18n.translation import _
32 from pylons.i18n.translation import _
33 from pylons.controllers.util import redirect
33 from pylons.controllers.util import redirect
34 from pylons.decorators import jsonify
34 from pylons.decorators import jsonify
35
35
36 from rhodecode.lib.vcs.exceptions import RepositoryError, ChangesetError, \
36 from rhodecode.lib.vcs.exceptions import RepositoryError, ChangesetError, \
37 ChangesetDoesNotExistError
37 ChangesetDoesNotExistError
38 from rhodecode.lib.vcs.nodes import FileNode
38 from rhodecode.lib.vcs.nodes import FileNode
39
39
40 import rhodecode.lib.helpers as h
40 import rhodecode.lib.helpers as h
41 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
41 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
42 from rhodecode.lib.base import BaseRepoController, render
42 from rhodecode.lib.base import BaseRepoController, render
43 from rhodecode.lib.utils import action_logger
43 from rhodecode.lib.utils import action_logger
44 from rhodecode.lib.compat import OrderedDict
44 from rhodecode.lib.compat import OrderedDict
45 from rhodecode.lib import diffs
45 from rhodecode.lib import diffs
46 from rhodecode.model.db import ChangesetComment, ChangesetStatus
46 from rhodecode.model.db import ChangesetComment, ChangesetStatus
47 from rhodecode.model.comment import ChangesetCommentsModel
47 from rhodecode.model.comment import ChangesetCommentsModel
48 from rhodecode.model.changeset_status import ChangesetStatusModel
48 from rhodecode.model.changeset_status import ChangesetStatusModel
49 from rhodecode.model.meta import Session
49 from rhodecode.model.meta import Session
50 from rhodecode.lib.diffs import wrapped_diff
50 from rhodecode.lib.diffs import wrapped_diff
51 from rhodecode.model.repo import RepoModel
51 from rhodecode.model.repo import RepoModel
52 from rhodecode.lib.exceptions import StatusChangeOnClosedPullRequestError
52 from rhodecode.lib.exceptions import StatusChangeOnClosedPullRequestError
53 from rhodecode.lib.vcs.backends.base import EmptyChangeset
53 from rhodecode.lib.vcs.backends.base import EmptyChangeset
54
54
55 log = logging.getLogger(__name__)
55 log = logging.getLogger(__name__)
56
56
57
57
58 def _update_with_GET(params, GET):
58 def _update_with_GET(params, GET):
59 for k in ['diff1', 'diff2', 'diff']:
59 for k in ['diff1', 'diff2', 'diff']:
60 params[k] += GET.getall(k)
60 params[k] += GET.getall(k)
61
61
62
62
63 def anchor_url(revision, path, GET):
63 def anchor_url(revision, path, GET):
64 fid = h.FID(revision, path)
64 fid = h.FID(revision, path)
65 return h.url.current(anchor=fid, **dict(GET))
65 return h.url.current(anchor=fid, **dict(GET))
66
66
67
67
68 def get_ignore_ws(fid, GET):
68 def get_ignore_ws(fid, GET):
69 ig_ws_global = GET.get('ignorews')
69 ig_ws_global = GET.get('ignorews')
70 ig_ws = filter(lambda k: k.startswith('WS'), GET.getall(fid))
70 ig_ws = filter(lambda k: k.startswith('WS'), GET.getall(fid))
71 if ig_ws:
71 if ig_ws:
72 try:
72 try:
73 return int(ig_ws[0].split(':')[-1])
73 return int(ig_ws[0].split(':')[-1])
74 except:
74 except:
75 pass
75 pass
76 return ig_ws_global
76 return ig_ws_global
77
77
78
78
79 def _ignorews_url(GET, fileid=None):
79 def _ignorews_url(GET, fileid=None):
80 fileid = str(fileid) if fileid else None
80 fileid = str(fileid) if fileid else None
81 params = defaultdict(list)
81 params = defaultdict(list)
82 _update_with_GET(params, GET)
82 _update_with_GET(params, GET)
83 lbl = _('show white space')
83 lbl = _('show white space')
84 ig_ws = get_ignore_ws(fileid, GET)
84 ig_ws = get_ignore_ws(fileid, GET)
85 ln_ctx = get_line_ctx(fileid, GET)
85 ln_ctx = get_line_ctx(fileid, GET)
86 # global option
86 # global option
87 if fileid is None:
87 if fileid is None:
88 if ig_ws is None:
88 if ig_ws is None:
89 params['ignorews'] += [1]
89 params['ignorews'] += [1]
90 lbl = _('ignore white space')
90 lbl = _('ignore white space')
91 ctx_key = 'context'
91 ctx_key = 'context'
92 ctx_val = ln_ctx
92 ctx_val = ln_ctx
93 # per file options
93 # per file options
94 else:
94 else:
95 if ig_ws is None:
95 if ig_ws is None:
96 params[fileid] += ['WS:1']
96 params[fileid] += ['WS:1']
97 lbl = _('ignore white space')
97 lbl = _('ignore white space')
98
98
99 ctx_key = fileid
99 ctx_key = fileid
100 ctx_val = 'C:%s' % ln_ctx
100 ctx_val = 'C:%s' % ln_ctx
101 # if we have passed in ln_ctx pass it along to our params
101 # if we have passed in ln_ctx pass it along to our params
102 if ln_ctx:
102 if ln_ctx:
103 params[ctx_key] += [ctx_val]
103 params[ctx_key] += [ctx_val]
104
104
105 params['anchor'] = fileid
105 params['anchor'] = fileid
106 img = h.image(h.url('/images/icons/text_strikethrough.png'), lbl, class_='icon')
106 img = h.image(h.url('/images/icons/text_strikethrough.png'), lbl, class_='icon')
107 return h.link_to(img, h.url.current(**params), title=lbl, class_='tooltip')
107 return h.link_to(img, h.url.current(**params), title=lbl, class_='tooltip')
108
108
109
109
110 def get_line_ctx(fid, GET):
110 def get_line_ctx(fid, GET):
111 ln_ctx_global = GET.get('context')
111 ln_ctx_global = GET.get('context')
112 ln_ctx = filter(lambda k: k.startswith('C'), GET.getall(fid))
112 ln_ctx = filter(lambda k: k.startswith('C'), GET.getall(fid))
113
113
114 if ln_ctx:
114 if ln_ctx:
115 retval = ln_ctx[0].split(':')[-1]
115 retval = ln_ctx[0].split(':')[-1]
116 else:
116 else:
117 retval = ln_ctx_global
117 retval = ln_ctx_global
118
118
119 try:
119 try:
120 return int(retval)
120 return int(retval)
121 except:
121 except:
122 return
122 return
123
123
124
124
125 def _context_url(GET, fileid=None):
125 def _context_url(GET, fileid=None):
126 """
126 """
127 Generates url for context lines
127 Generates url for context lines
128
128
129 :param fileid:
129 :param fileid:
130 """
130 """
131
131
132 fileid = str(fileid) if fileid else None
132 fileid = str(fileid) if fileid else None
133 ig_ws = get_ignore_ws(fileid, GET)
133 ig_ws = get_ignore_ws(fileid, GET)
134 ln_ctx = (get_line_ctx(fileid, GET) or 3) * 2
134 ln_ctx = (get_line_ctx(fileid, GET) or 3) * 2
135
135
136 params = defaultdict(list)
136 params = defaultdict(list)
137 _update_with_GET(params, GET)
137 _update_with_GET(params, GET)
138
138
139 # global option
139 # global option
140 if fileid is None:
140 if fileid is None:
141 if ln_ctx > 0:
141 if ln_ctx > 0:
142 params['context'] += [ln_ctx]
142 params['context'] += [ln_ctx]
143
143
144 if ig_ws:
144 if ig_ws:
145 ig_ws_key = 'ignorews'
145 ig_ws_key = 'ignorews'
146 ig_ws_val = 1
146 ig_ws_val = 1
147
147
148 # per file option
148 # per file option
149 else:
149 else:
150 params[fileid] += ['C:%s' % ln_ctx]
150 params[fileid] += ['C:%s' % ln_ctx]
151 ig_ws_key = fileid
151 ig_ws_key = fileid
152 ig_ws_val = 'WS:%s' % 1
152 ig_ws_val = 'WS:%s' % 1
153
153
154 if ig_ws:
154 if ig_ws:
155 params[ig_ws_key] += [ig_ws_val]
155 params[ig_ws_key] += [ig_ws_val]
156
156
157 lbl = _('%s line context') % ln_ctx
157 lbl = _('%s line context') % ln_ctx
158
158
159 params['anchor'] = fileid
159 params['anchor'] = fileid
160 img = h.image(h.url('/images/icons/table_add.png'), lbl, class_='icon')
160 img = h.image(h.url('/images/icons/table_add.png'), lbl, class_='icon')
161 return h.link_to(img, h.url.current(**params), title=lbl, class_='tooltip')
161 return h.link_to(img, h.url.current(**params), title=lbl, class_='tooltip')
162
162
163
163
164 class ChangesetController(BaseRepoController):
164 class ChangesetController(BaseRepoController):
165
165
166 @LoginRequired()
166 @LoginRequired()
167 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
167 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
168 'repository.admin')
168 'repository.admin')
169 def __before__(self):
169 def __before__(self):
170 super(ChangesetController, self).__before__()
170 super(ChangesetController, self).__before__()
171 c.affected_files_cut_off = 60
171 c.affected_files_cut_off = 60
172 repo_model = RepoModel()
172 repo_model = RepoModel()
173 c.users_array = repo_model.get_users_js()
173 c.users_array = repo_model.get_users_js()
174 c.users_groups_array = repo_model.get_users_groups_js()
174 c.users_groups_array = repo_model.get_users_groups_js()
175
175
176 def index(self, revision):
176 def index(self, revision):
177
177
178 c.anchor_url = anchor_url
178 c.anchor_url = anchor_url
179 c.ignorews_url = _ignorews_url
179 c.ignorews_url = _ignorews_url
180 c.context_url = _context_url
180 c.context_url = _context_url
181 limit_off = request.GET.get('fulldiff')
181 limit_off = request.GET.get('fulldiff')
182 #get ranges of revisions if preset
182 #get ranges of revisions if preset
183 rev_range = revision.split('...')[:2]
183 rev_range = revision.split('...')[:2]
184 enable_comments = True
184 enable_comments = True
185 try:
185 try:
186 if len(rev_range) == 2:
186 if len(rev_range) == 2:
187 enable_comments = False
187 enable_comments = False
188 rev_start = rev_range[0]
188 rev_start = rev_range[0]
189 rev_end = rev_range[1]
189 rev_end = rev_range[1]
190 rev_ranges = c.rhodecode_repo.get_changesets(start=rev_start,
190 rev_ranges = c.rhodecode_repo.get_changesets(start=rev_start,
191 end=rev_end)
191 end=rev_end)
192 else:
192 else:
193 rev_ranges = [c.rhodecode_repo.get_changeset(revision)]
193 rev_ranges = [c.rhodecode_repo.get_changeset(revision)]
194
194
195 c.cs_ranges = list(rev_ranges)
195 c.cs_ranges = list(rev_ranges)
196 if not c.cs_ranges:
196 if not c.cs_ranges:
197 raise RepositoryError('Changeset range returned empty result')
197 raise RepositoryError('Changeset range returned empty result')
198
198
199 except (RepositoryError, ChangesetDoesNotExistError, Exception), e:
199 except (RepositoryError, ChangesetDoesNotExistError, Exception), e:
200 log.error(traceback.format_exc())
200 log.error(traceback.format_exc())
201 h.flash(str(e), category='warning')
201 h.flash(str(e), category='warning')
202 return redirect(url('home'))
202 return redirect(url('home'))
203
203
204 c.changes = OrderedDict()
204 c.changes = OrderedDict()
205
205
206 c.lines_added = 0 # count of lines added
206 c.lines_added = 0 # count of lines added
207 c.lines_deleted = 0 # count of lines removes
207 c.lines_deleted = 0 # count of lines removes
208
208
209 cumulative_diff = 0
209 cumulative_diff = 0
210 c.cut_off = False # defines if cut off limit is reached
210 c.cut_off = False # defines if cut off limit is reached
211 c.changeset_statuses = ChangesetStatus.STATUSES
211 c.changeset_statuses = ChangesetStatus.STATUSES
212 c.comments = []
212 c.comments = []
213 c.statuses = []
213 c.statuses = []
214 c.inline_comments = []
214 c.inline_comments = []
215 c.inline_cnt = 0
215 c.inline_cnt = 0
216 # Iterate over ranges (default changeset view is always one changeset)
216 # Iterate over ranges (default changeset view is always one changeset)
217 for changeset in c.cs_ranges:
217 for changeset in c.cs_ranges:
218
218
219 c.statuses.extend([ChangesetStatusModel()\
219 c.statuses.extend([ChangesetStatusModel()\
220 .get_status(c.rhodecode_db_repo.repo_id,
220 .get_status(c.rhodecode_db_repo.repo_id,
221 changeset.raw_id)])
221 changeset.raw_id)])
222
222
223 c.comments.extend(ChangesetCommentsModel()\
223 c.comments.extend(ChangesetCommentsModel()\
224 .get_comments(c.rhodecode_db_repo.repo_id,
224 .get_comments(c.rhodecode_db_repo.repo_id,
225 revision=changeset.raw_id))
225 revision=changeset.raw_id))
226 inlines = ChangesetCommentsModel()\
226 inlines = ChangesetCommentsModel()\
227 .get_inline_comments(c.rhodecode_db_repo.repo_id,
227 .get_inline_comments(c.rhodecode_db_repo.repo_id,
228 revision=changeset.raw_id)
228 revision=changeset.raw_id)
229 c.inline_comments.extend(inlines)
229 c.inline_comments.extend(inlines)
230 c.changes[changeset.raw_id] = []
230 c.changes[changeset.raw_id] = []
231 try:
231 try:
232 changeset_parent = changeset.parents[0]
232 changeset_parent = changeset.parents[0]
233 except IndexError:
233 except IndexError:
234 changeset_parent = None
234 changeset_parent = None
235
235
236 #==================================================================
236 #==================================================================
237 # ADDED FILES
237 # ADDED FILES
238 #==================================================================
238 #==================================================================
239 for node in changeset.added:
239 for node in changeset.added:
240 fid = h.FID(revision, node.path)
240 fid = h.FID(revision, node.path)
241 line_context_lcl = get_line_ctx(fid, request.GET)
241 line_context_lcl = get_line_ctx(fid, request.GET)
242 ign_whitespace_lcl = get_ignore_ws(fid, request.GET)
242 ign_whitespace_lcl = get_ignore_ws(fid, request.GET)
243 lim = self.cut_off_limit
243 lim = self.cut_off_limit
244 if cumulative_diff > self.cut_off_limit:
244 if cumulative_diff > self.cut_off_limit:
245 lim = -1 if limit_off is None else None
245 lim = -1 if limit_off is None else None
246 size, cs1, cs2, diff, st = wrapped_diff(
246 size, cs1, cs2, diff, st = wrapped_diff(
247 filenode_old=None,
247 filenode_old=None,
248 filenode_new=node,
248 filenode_new=node,
249 cut_off_limit=lim,
249 cut_off_limit=lim,
250 ignore_whitespace=ign_whitespace_lcl,
250 ignore_whitespace=ign_whitespace_lcl,
251 line_context=line_context_lcl,
251 line_context=line_context_lcl,
252 enable_comments=enable_comments
252 enable_comments=enable_comments
253 )
253 )
254 cumulative_diff += size
254 cumulative_diff += size
255 c.lines_added += st[0]
255 c.lines_added += st[0]
256 c.lines_deleted += st[1]
256 c.lines_deleted += st[1]
257 c.changes[changeset.raw_id].append(
257 c.changes[changeset.raw_id].append(
258 ('added', node, diff, cs1, cs2, st)
258 ('added', node, diff, cs1, cs2, st)
259 )
259 )
260
260
261 #==================================================================
261 #==================================================================
262 # CHANGED FILES
262 # CHANGED FILES
263 #==================================================================
263 #==================================================================
264 for node in changeset.changed:
264 for node in changeset.changed:
265 try:
265 try:
266 filenode_old = changeset_parent.get_node(node.path)
266 filenode_old = changeset_parent.get_node(node.path)
267 except ChangesetError:
267 except ChangesetError:
268 log.warning('Unable to fetch parent node for diff')
268 log.warning('Unable to fetch parent node for diff')
269 filenode_old = FileNode(node.path, '', EmptyChangeset())
269 filenode_old = FileNode(node.path, '', EmptyChangeset())
270
270
271 fid = h.FID(revision, node.path)
271 fid = h.FID(revision, node.path)
272 line_context_lcl = get_line_ctx(fid, request.GET)
272 line_context_lcl = get_line_ctx(fid, request.GET)
273 ign_whitespace_lcl = get_ignore_ws(fid, request.GET)
273 ign_whitespace_lcl = get_ignore_ws(fid, request.GET)
274 lim = self.cut_off_limit
274 lim = self.cut_off_limit
275 if cumulative_diff > self.cut_off_limit:
275 if cumulative_diff > self.cut_off_limit:
276 lim = -1 if limit_off is None else None
276 lim = -1 if limit_off is None else None
277 size, cs1, cs2, diff, st = wrapped_diff(
277 size, cs1, cs2, diff, st = wrapped_diff(
278 filenode_old=filenode_old,
278 filenode_old=filenode_old,
279 filenode_new=node,
279 filenode_new=node,
280 cut_off_limit=lim,
280 cut_off_limit=lim,
281 ignore_whitespace=ign_whitespace_lcl,
281 ignore_whitespace=ign_whitespace_lcl,
282 line_context=line_context_lcl,
282 line_context=line_context_lcl,
283 enable_comments=enable_comments
283 enable_comments=enable_comments
284 )
284 )
285 cumulative_diff += size
285 cumulative_diff += size
286 c.lines_added += st[0]
286 c.lines_added += st[0]
287 c.lines_deleted += st[1]
287 c.lines_deleted += st[1]
288 c.changes[changeset.raw_id].append(
288 c.changes[changeset.raw_id].append(
289 ('changed', node, diff, cs1, cs2, st)
289 ('changed', node, diff, cs1, cs2, st)
290 )
290 )
291 #==================================================================
291 #==================================================================
292 # REMOVED FILES
292 # REMOVED FILES
293 #==================================================================
293 #==================================================================
294 for node in changeset.removed:
294 for node in changeset.removed:
295 c.changes[changeset.raw_id].append(
295 c.changes[changeset.raw_id].append(
296 ('removed', node, None, None, None, (0, 0))
296 ('removed', node, None, None, None, (0, 0))
297 )
297 )
298
298
299 # count inline comments
299 # count inline comments
300 for __, lines in c.inline_comments:
300 for __, lines in c.inline_comments:
301 for comments in lines.values():
301 for comments in lines.values():
302 c.inline_cnt += len(comments)
302 c.inline_cnt += len(comments)
303
303
304 if len(c.cs_ranges) == 1:
304 if len(c.cs_ranges) == 1:
305 c.changeset = c.cs_ranges[0]
305 c.changeset = c.cs_ranges[0]
306 c.changes = c.changes[c.changeset.raw_id]
306 c.changes = c.changes[c.changeset.raw_id]
307
307
308 return render('changeset/changeset.html')
308 return render('changeset/changeset.html')
309 else:
309 else:
310 return render('changeset/changeset_range.html')
310 return render('changeset/changeset_range.html')
311
311
312 def raw_changeset(self, revision):
312 def raw_changeset(self, revision):
313
313
314 method = request.GET.get('diff', 'show')
314 method = request.GET.get('diff', 'show')
315 ignore_whitespace = request.GET.get('ignorews') == '1'
315 ignore_whitespace = request.GET.get('ignorews') == '1'
316 line_context = request.GET.get('context', 3)
316 line_context = request.GET.get('context', 3)
317 try:
317 try:
318 c.scm_type = c.rhodecode_repo.alias
318 c.scm_type = c.rhodecode_repo.alias
319 c.changeset = c.rhodecode_repo.get_changeset(revision)
319 c.changeset = c.rhodecode_repo.get_changeset(revision)
320 except RepositoryError:
320 except RepositoryError:
321 log.error(traceback.format_exc())
321 log.error(traceback.format_exc())
322 return redirect(url('home'))
322 return redirect(url('home'))
323 else:
323 else:
324 try:
324 try:
325 c.changeset_parent = c.changeset.parents[0]
325 c.changeset_parent = c.changeset.parents[0]
326 except IndexError:
326 except IndexError:
327 c.changeset_parent = None
327 c.changeset_parent = None
328 c.changes = []
328 c.changes = []
329
329
330 for node in c.changeset.added:
330 for node in c.changeset.added:
331 filenode_old = FileNode(node.path, '')
331 filenode_old = FileNode(node.path, '')
332 if filenode_old.is_binary or node.is_binary:
332 if filenode_old.is_binary or node.is_binary:
333 diff = _('binary file') + '\n'
333 diff = _('binary file') + '\n'
334 else:
334 else:
335 f_gitdiff = diffs.get_gitdiff(filenode_old, node,
335 f_gitdiff = diffs.get_gitdiff(filenode_old, node,
336 ignore_whitespace=ignore_whitespace,
336 ignore_whitespace=ignore_whitespace,
337 context=line_context)
337 context=line_context)
338 diff = diffs.DiffProcessor(f_gitdiff,
338 diff = diffs.DiffProcessor(f_gitdiff,
339 format='gitdiff').raw_diff()
339 format='gitdiff').raw_diff()
340
340
341 cs1 = None
341 cs1 = None
342 cs2 = node.changeset.raw_id
342 cs2 = node.changeset.raw_id
343 c.changes.append(('added', node, diff, cs1, cs2))
343 c.changes.append(('added', node, diff, cs1, cs2))
344
344
345 for node in c.changeset.changed:
345 for node in c.changeset.changed:
346 filenode_old = c.changeset_parent.get_node(node.path)
346 filenode_old = c.changeset_parent.get_node(node.path)
347 if filenode_old.is_binary or node.is_binary:
347 if filenode_old.is_binary or node.is_binary:
348 diff = _('binary file')
348 diff = _('binary file')
349 else:
349 else:
350 f_gitdiff = diffs.get_gitdiff(filenode_old, node,
350 f_gitdiff = diffs.get_gitdiff(filenode_old, node,
351 ignore_whitespace=ignore_whitespace,
351 ignore_whitespace=ignore_whitespace,
352 context=line_context)
352 context=line_context)
353 diff = diffs.DiffProcessor(f_gitdiff,
353 diff = diffs.DiffProcessor(f_gitdiff,
354 format='gitdiff').raw_diff()
354 format='gitdiff').raw_diff()
355
355
356 cs1 = filenode_old.changeset.raw_id
356 cs1 = filenode_old.changeset.raw_id
357 cs2 = node.changeset.raw_id
357 cs2 = node.changeset.raw_id
358 c.changes.append(('changed', node, diff, cs1, cs2))
358 c.changes.append(('changed', node, diff, cs1, cs2))
359
359
360 response.content_type = 'text/plain'
360 response.content_type = 'text/plain'
361
361
362 if method == 'download':
362 if method == 'download':
363 response.content_disposition = 'attachment; filename=%s.patch' \
363 response.content_disposition = 'attachment; filename=%s.patch' \
364 % revision
364 % revision
365
365
366 c.parent_tmpl = ''.join(['# Parent %s\n' % x.raw_id
366 c.parent_tmpl = ''.join(['# Parent %s\n' % x.raw_id
367 for x in c.changeset.parents])
367 for x in c.changeset.parents])
368
368
369 c.diffs = ''
369 c.diffs = ''
370 for x in c.changes:
370 for x in c.changes:
371 c.diffs += x[2]
371 c.diffs += x[2]
372
372
373 return render('changeset/raw_changeset.html')
373 return render('changeset/raw_changeset.html')
374
374
375 @jsonify
375 @jsonify
376 def comment(self, repo_name, revision):
376 def comment(self, repo_name, revision):
377 status = request.POST.get('changeset_status')
377 status = request.POST.get('changeset_status')
378 change_status = request.POST.get('change_changeset_status')
378 change_status = request.POST.get('change_changeset_status')
379 text = request.POST.get('text')
379 text = request.POST.get('text')
380 if status and change_status:
380 if status and change_status:
381 text = text or (_('Status change -> %s')
381 text = text or (_('Status change -> %s')
382 % ChangesetStatus.get_status_lbl(status))
382 % ChangesetStatus.get_status_lbl(status))
383
383
384 comm = ChangesetCommentsModel().create(
384 comm = ChangesetCommentsModel().create(
385 text=text,
385 text=text,
386 repo=c.rhodecode_db_repo.repo_id,
386 repo=c.rhodecode_db_repo.repo_id,
387 user=c.rhodecode_user.user_id,
387 user=c.rhodecode_user.user_id,
388 revision=revision,
388 revision=revision,
389 f_path=request.POST.get('f_path'),
389 f_path=request.POST.get('f_path'),
390 line_no=request.POST.get('line'),
390 line_no=request.POST.get('line'),
391 status_change=(ChangesetStatus.get_status_lbl(status)
391 status_change=(ChangesetStatus.get_status_lbl(status)
392 if status and change_status else None)
392 if status and change_status else None)
393 )
393 )
394
394
395 # get status if set !
395 # get status if set !
396 if status and change_status:
396 if status and change_status:
397 # if latest status was from pull request and it's closed
397 # if latest status was from pull request and it's closed
398 # disallow changing status !
398 # disallow changing status !
399 # dont_allow_on_closed_pull_request = True !
399 # dont_allow_on_closed_pull_request = True !
400
400
401 try:
401 try:
402 ChangesetStatusModel().set_status(
402 ChangesetStatusModel().set_status(
403 c.rhodecode_db_repo.repo_id,
403 c.rhodecode_db_repo.repo_id,
404 status,
404 status,
405 c.rhodecode_user.user_id,
405 c.rhodecode_user.user_id,
406 comm,
406 comm,
407 revision=revision,
407 revision=revision,
408 dont_allow_on_closed_pull_request=True
408 dont_allow_on_closed_pull_request=True
409 )
409 )
410 except StatusChangeOnClosedPullRequestError:
410 except StatusChangeOnClosedPullRequestError:
411 log.error(traceback.format_exc())
411 log.error(traceback.format_exc())
412 msg = _('Changing status on a changeset associated with'
412 msg = _('Changing status on a changeset associated with'
413 'a closed pull request is not allowed')
413 'a closed pull request is not allowed')
414 h.flash(msg, category='warning')
414 h.flash(msg, category='warning')
415 return redirect(h.url('changeset_home', repo_name=repo_name,
415 return redirect(h.url('changeset_home', repo_name=repo_name,
416 revision=revision))
416 revision=revision))
417 action_logger(self.rhodecode_user,
417 action_logger(self.rhodecode_user,
418 'user_commented_revision:%s' % revision,
418 'user_commented_revision:%s' % revision,
419 c.rhodecode_db_repo, self.ip_addr, self.sa)
419 c.rhodecode_db_repo, self.ip_addr, self.sa)
420
420
421 Session().commit()
421 Session().commit()
422
422
423 if not request.environ.get('HTTP_X_PARTIAL_XHR'):
423 if not request.environ.get('HTTP_X_PARTIAL_XHR'):
424 return redirect(h.url('changeset_home', repo_name=repo_name,
424 return redirect(h.url('changeset_home', repo_name=repo_name,
425 revision=revision))
425 revision=revision))
426
426
427 data = {
427 data = {
428 'target_id': h.safeid(h.safe_unicode(request.POST.get('f_path'))),
428 'target_id': h.safeid(h.safe_unicode(request.POST.get('f_path'))),
429 }
429 }
430 if comm:
430 if comm:
431 c.co = comm
431 c.co = comm
432 data.update(comm.get_dict())
432 data.update(comm.get_dict())
433 data.update({'rendered_text':
433 data.update({'rendered_text':
434 render('changeset/changeset_comment_block.html')})
434 render('changeset/changeset_comment_block.html')})
435
435
436 return data
436 return data
437
437
438 @jsonify
438 @jsonify
439 def delete_comment(self, repo_name, comment_id):
439 def delete_comment(self, repo_name, comment_id):
440 co = ChangesetComment.get(comment_id)
440 co = ChangesetComment.get(comment_id)
441 owner = lambda: co.author.user_id == c.rhodecode_user.user_id
441 owner = lambda: co.author.user_id == c.rhodecode_user.user_id
442 if h.HasPermissionAny('hg.admin', 'repository.admin')() or owner:
442 if h.HasPermissionAny('hg.admin', 'repository.admin')() or owner:
443 ChangesetCommentsModel().delete(comment=co)
443 ChangesetCommentsModel().delete(comment=co)
444 Session().commit()
444 Session().commit()
445 return True
445 return True
446 else:
446 else:
447 raise HTTPForbidden()
447 raise HTTPForbidden()
@@ -1,594 +1,591 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.lib.compat
3 rhodecode.lib.compat
4 ~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~
5
5
6 Python backward compatibility functions and common libs
6 Python backward compatibility functions and common libs
7
7
8
8
9 :created_on: Oct 7, 2011
9 :created_on: Oct 7, 2011
10 :author: marcink
10 :author: marcink
11 :copyright: (C) 2010-2010 Marcin Kuzminski <marcin@python-works.com>
11 :copyright: (C) 2010-2010 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
26
27 import os
27 import os
28 from rhodecode import __platform__, PLATFORM_WIN, __py_version__
28 from rhodecode import __platform__, PLATFORM_WIN, __py_version__
29
29
30 #==============================================================================
30 #==============================================================================
31 # json
31 # json
32 #==============================================================================
32 #==============================================================================
33 from rhodecode.lib.ext_json import json
33 from rhodecode.lib.ext_json import json
34
34
35
35
36 #==============================================================================
36 #==============================================================================
37 # izip_longest
37 # izip_longest
38 #==============================================================================
38 #==============================================================================
39 try:
39 try:
40 from itertools import izip_longest
40 from itertools import izip_longest
41 except ImportError:
41 except ImportError:
42 import itertools
42 import itertools
43
43
44 def izip_longest(*args, **kwds):
44 def izip_longest(*args, **kwds):
45 fillvalue = kwds.get("fillvalue")
45 fillvalue = kwds.get("fillvalue")
46
46
47 def sentinel(counter=([fillvalue] * (len(args) - 1)).pop):
47 def sentinel(counter=([fillvalue] * (len(args) - 1)).pop):
48 yield counter() # yields the fillvalue, or raises IndexError
48 yield counter() # yields the fillvalue, or raises IndexError
49
49
50 fillers = itertools.repeat(fillvalue)
50 fillers = itertools.repeat(fillvalue)
51 iters = [itertools.chain(it, sentinel(), fillers)
51 iters = [itertools.chain(it, sentinel(), fillers)
52 for it in args]
52 for it in args]
53 try:
53 try:
54 for tup in itertools.izip(*iters):
54 for tup in itertools.izip(*iters):
55 yield tup
55 yield tup
56 except IndexError:
56 except IndexError:
57 pass
57 pass
58
58
59
59
60 #==============================================================================
60 #==============================================================================
61 # OrderedDict
61 # OrderedDict
62 #==============================================================================
62 #==============================================================================
63
63
64 # Python Software Foundation License
64 # Python Software Foundation License
65
65
66 # XXX: it feels like using the class with "is" and "is not" instead of "==" and
66 # XXX: it feels like using the class with "is" and "is not" instead of "==" and
67 # "!=" should be faster.
67 # "!=" should be faster.
68 class _Nil(object):
68 class _Nil(object):
69
69
70 def __repr__(self):
70 def __repr__(self):
71 return "nil"
71 return "nil"
72
72
73 def __eq__(self, other):
73 def __eq__(self, other):
74 if (isinstance(other, _Nil)):
74 if (isinstance(other, _Nil)):
75 return True
75 return True
76 else:
76 else:
77 return NotImplemented
77 return NotImplemented
78
78
79 def __ne__(self, other):
79 def __ne__(self, other):
80 if (isinstance(other, _Nil)):
80 if (isinstance(other, _Nil)):
81 return False
81 return False
82 else:
82 else:
83 return NotImplemented
83 return NotImplemented
84
84
85 _nil = _Nil()
85 _nil = _Nil()
86
86
87
87
88 class _odict(object):
88 class _odict(object):
89 """Ordered dict data structure, with O(1) complexity for dict operations
89 """Ordered dict data structure, with O(1) complexity for dict operations
90 that modify one element.
90 that modify one element.
91
91
92 Overwriting values doesn't change their original sequential order.
92 Overwriting values doesn't change their original sequential order.
93 """
93 """
94
94
95 def _dict_impl(self):
95 def _dict_impl(self):
96 return None
96 return None
97
97
98 def __init__(self, data=(), **kwds):
98 def __init__(self, data=(), **kwds):
99 """This doesn't accept keyword initialization as normal dicts to avoid
99 """This doesn't accept keyword initialization as normal dicts to avoid
100 a trap - inside a function or method the keyword args are accessible
100 a trap - inside a function or method the keyword args are accessible
101 only as a dict, without a defined order, so their original order is
101 only as a dict, without a defined order, so their original order is
102 lost.
102 lost.
103 """
103 """
104 if kwds:
104 if kwds:
105 raise TypeError("__init__() of ordered dict takes no keyword "
105 raise TypeError("__init__() of ordered dict takes no keyword "
106 "arguments to avoid an ordering trap.")
106 "arguments to avoid an ordering trap.")
107 self._dict_impl().__init__(self)
107 self._dict_impl().__init__(self)
108 # If you give a normal dict, then the order of elements is undefined
108 # If you give a normal dict, then the order of elements is undefined
109 if hasattr(data, "iteritems"):
109 if hasattr(data, "iteritems"):
110 for key, val in data.iteritems():
110 for key, val in data.iteritems():
111 self[key] = val
111 self[key] = val
112 else:
112 else:
113 for key, val in data:
113 for key, val in data:
114 self[key] = val
114 self[key] = val
115
115
116 # Double-linked list header
116 # Double-linked list header
117 def _get_lh(self):
117 def _get_lh(self):
118 dict_impl = self._dict_impl()
118 dict_impl = self._dict_impl()
119 if not hasattr(self, '_lh'):
119 if not hasattr(self, '_lh'):
120 dict_impl.__setattr__(self, '_lh', _nil)
120 dict_impl.__setattr__(self, '_lh', _nil)
121 return dict_impl.__getattribute__(self, '_lh')
121 return dict_impl.__getattribute__(self, '_lh')
122
122
123 def _set_lh(self, val):
123 def _set_lh(self, val):
124 self._dict_impl().__setattr__(self, '_lh', val)
124 self._dict_impl().__setattr__(self, '_lh', val)
125
125
126 lh = property(_get_lh, _set_lh)
126 lh = property(_get_lh, _set_lh)
127
127
128 # Double-linked list tail
128 # Double-linked list tail
129 def _get_lt(self):
129 def _get_lt(self):
130 dict_impl = self._dict_impl()
130 dict_impl = self._dict_impl()
131 if not hasattr(self, '_lt'):
131 if not hasattr(self, '_lt'):
132 dict_impl.__setattr__(self, '_lt', _nil)
132 dict_impl.__setattr__(self, '_lt', _nil)
133 return dict_impl.__getattribute__(self, '_lt')
133 return dict_impl.__getattribute__(self, '_lt')
134
134
135 def _set_lt(self, val):
135 def _set_lt(self, val):
136 self._dict_impl().__setattr__(self, '_lt', val)
136 self._dict_impl().__setattr__(self, '_lt', val)
137
137
138 lt = property(_get_lt, _set_lt)
138 lt = property(_get_lt, _set_lt)
139
139
140 def __getitem__(self, key):
140 def __getitem__(self, key):
141 return self._dict_impl().__getitem__(self, key)[1]
141 return self._dict_impl().__getitem__(self, key)[1]
142
142
143 def __setitem__(self, key, val):
143 def __setitem__(self, key, val):
144 dict_impl = self._dict_impl()
144 dict_impl = self._dict_impl()
145 try:
145 try:
146 dict_impl.__getitem__(self, key)[1] = val
146 dict_impl.__getitem__(self, key)[1] = val
147 except KeyError:
147 except KeyError:
148 new = [dict_impl.__getattribute__(self, 'lt'), val, _nil]
148 new = [dict_impl.__getattribute__(self, 'lt'), val, _nil]
149 dict_impl.__setitem__(self, key, new)
149 dict_impl.__setitem__(self, key, new)
150 if dict_impl.__getattribute__(self, 'lt') == _nil:
150 if dict_impl.__getattribute__(self, 'lt') == _nil:
151 dict_impl.__setattr__(self, 'lh', key)
151 dict_impl.__setattr__(self, 'lh', key)
152 else:
152 else:
153 dict_impl.__getitem__(
153 dict_impl.__getitem__(
154 self, dict_impl.__getattribute__(self, 'lt'))[2] = key
154 self, dict_impl.__getattribute__(self, 'lt'))[2] = key
155 dict_impl.__setattr__(self, 'lt', key)
155 dict_impl.__setattr__(self, 'lt', key)
156
156
157 def __delitem__(self, key):
157 def __delitem__(self, key):
158 dict_impl = self._dict_impl()
158 dict_impl = self._dict_impl()
159 pred, _, succ = self._dict_impl().__getitem__(self, key)
159 pred, _, succ = self._dict_impl().__getitem__(self, key)
160 if pred == _nil:
160 if pred == _nil:
161 dict_impl.__setattr__(self, 'lh', succ)
161 dict_impl.__setattr__(self, 'lh', succ)
162 else:
162 else:
163 dict_impl.__getitem__(self, pred)[2] = succ
163 dict_impl.__getitem__(self, pred)[2] = succ
164 if succ == _nil:
164 if succ == _nil:
165 dict_impl.__setattr__(self, 'lt', pred)
165 dict_impl.__setattr__(self, 'lt', pred)
166 else:
166 else:
167 dict_impl.__getitem__(self, succ)[0] = pred
167 dict_impl.__getitem__(self, succ)[0] = pred
168 dict_impl.__delitem__(self, key)
168 dict_impl.__delitem__(self, key)
169
169
170 def __contains__(self, key):
170 def __contains__(self, key):
171 return key in self.keys()
171 return key in self.keys()
172
172
173 def __len__(self):
173 def __len__(self):
174 return len(self.keys())
174 return len(self.keys())
175
175
176 def __str__(self):
176 def __str__(self):
177 pairs = ("%r: %r" % (k, v) for k, v in self.iteritems())
177 pairs = ("%r: %r" % (k, v) for k, v in self.iteritems())
178 return "{%s}" % ", ".join(pairs)
178 return "{%s}" % ", ".join(pairs)
179
179
180 def __repr__(self):
180 def __repr__(self):
181 if self:
181 if self:
182 pairs = ("(%r, %r)" % (k, v) for k, v in self.iteritems())
182 pairs = ("(%r, %r)" % (k, v) for k, v in self.iteritems())
183 return "odict([%s])" % ", ".join(pairs)
183 return "odict([%s])" % ", ".join(pairs)
184 else:
184 else:
185 return "odict()"
185 return "odict()"
186
186
187 def get(self, k, x=None):
187 def get(self, k, x=None):
188 if k in self:
188 if k in self:
189 return self._dict_impl().__getitem__(self, k)[1]
189 return self._dict_impl().__getitem__(self, k)[1]
190 else:
190 else:
191 return x
191 return x
192
192
193 def __iter__(self):
193 def __iter__(self):
194 dict_impl = self._dict_impl()
194 dict_impl = self._dict_impl()
195 curr_key = dict_impl.__getattribute__(self, 'lh')
195 curr_key = dict_impl.__getattribute__(self, 'lh')
196 while curr_key != _nil:
196 while curr_key != _nil:
197 yield curr_key
197 yield curr_key
198 curr_key = dict_impl.__getitem__(self, curr_key)[2]
198 curr_key = dict_impl.__getitem__(self, curr_key)[2]
199
199
200 iterkeys = __iter__
200 iterkeys = __iter__
201
201
202 def keys(self):
202 def keys(self):
203 return list(self.iterkeys())
203 return list(self.iterkeys())
204
204
205 def itervalues(self):
205 def itervalues(self):
206 dict_impl = self._dict_impl()
206 dict_impl = self._dict_impl()
207 curr_key = dict_impl.__getattribute__(self, 'lh')
207 curr_key = dict_impl.__getattribute__(self, 'lh')
208 while curr_key != _nil:
208 while curr_key != _nil:
209 _, val, curr_key = dict_impl.__getitem__(self, curr_key)
209 _, val, curr_key = dict_impl.__getitem__(self, curr_key)
210 yield val
210 yield val
211
211
212 def values(self):
212 def values(self):
213 return list(self.itervalues())
213 return list(self.itervalues())
214
214
215 def iteritems(self):
215 def iteritems(self):
216 dict_impl = self._dict_impl()
216 dict_impl = self._dict_impl()
217 curr_key = dict_impl.__getattribute__(self, 'lh')
217 curr_key = dict_impl.__getattribute__(self, 'lh')
218 while curr_key != _nil:
218 while curr_key != _nil:
219 _, val, next_key = dict_impl.__getitem__(self, curr_key)
219 _, val, next_key = dict_impl.__getitem__(self, curr_key)
220 yield curr_key, val
220 yield curr_key, val
221 curr_key = next_key
221 curr_key = next_key
222
222
223 def items(self):
223 def items(self):
224 return list(self.iteritems())
224 return list(self.iteritems())
225
225
226 def sort(self, cmp=None, key=None, reverse=False):
226 def sort(self, cmp=None, key=None, reverse=False):
227 items = [(k, v) for k, v in self.items()]
227 items = [(k, v) for k, v in self.items()]
228 if cmp is not None:
228 if cmp is not None:
229 items = sorted(items, cmp=cmp)
229 items = sorted(items, cmp=cmp)
230 elif key is not None:
230 elif key is not None:
231 items = sorted(items, key=key)
231 items = sorted(items, key=key)
232 else:
232 else:
233 items = sorted(items, key=lambda x: x[1])
233 items = sorted(items, key=lambda x: x[1])
234 if reverse:
234 if reverse:
235 items.reverse()
235 items.reverse()
236 self.clear()
236 self.clear()
237 self.__init__(items)
237 self.__init__(items)
238
238
239 def clear(self):
239 def clear(self):
240 dict_impl = self._dict_impl()
240 dict_impl = self._dict_impl()
241 dict_impl.clear(self)
241 dict_impl.clear(self)
242 dict_impl.__setattr__(self, 'lh', _nil)
242 dict_impl.__setattr__(self, 'lh', _nil)
243 dict_impl.__setattr__(self, 'lt', _nil)
243 dict_impl.__setattr__(self, 'lt', _nil)
244
244
245 def copy(self):
245 def copy(self):
246 return self.__class__(self)
246 return self.__class__(self)
247
247
248 def update(self, data=(), **kwds):
248 def update(self, data=(), **kwds):
249 if kwds:
249 if kwds:
250 raise TypeError("update() of ordered dict takes no keyword "
250 raise TypeError("update() of ordered dict takes no keyword "
251 "arguments to avoid an ordering trap.")
251 "arguments to avoid an ordering trap.")
252 if hasattr(data, "iteritems"):
252 if hasattr(data, "iteritems"):
253 data = data.iteritems()
253 data = data.iteritems()
254 for key, val in data:
254 for key, val in data:
255 self[key] = val
255 self[key] = val
256
256
257 def setdefault(self, k, x=None):
257 def setdefault(self, k, x=None):
258 try:
258 try:
259 return self[k]
259 return self[k]
260 except KeyError:
260 except KeyError:
261 self[k] = x
261 self[k] = x
262 return x
262 return x
263
263
264 def pop(self, k, x=_nil):
264 def pop(self, k, x=_nil):
265 try:
265 try:
266 val = self[k]
266 val = self[k]
267 del self[k]
267 del self[k]
268 return val
268 return val
269 except KeyError:
269 except KeyError:
270 if x == _nil:
270 if x == _nil:
271 raise
271 raise
272 return x
272 return x
273
273
274 def popitem(self):
274 def popitem(self):
275 try:
275 try:
276 dict_impl = self._dict_impl()
276 dict_impl = self._dict_impl()
277 key = dict_impl.__getattribute__(self, 'lt')
277 key = dict_impl.__getattribute__(self, 'lt')
278 return key, self.pop(key)
278 return key, self.pop(key)
279 except KeyError:
279 except KeyError:
280 raise KeyError("'popitem(): ordered dictionary is empty'")
280 raise KeyError("'popitem(): ordered dictionary is empty'")
281
281
282 def riterkeys(self):
282 def riterkeys(self):
283 """To iterate on keys in reversed order.
283 """To iterate on keys in reversed order.
284 """
284 """
285 dict_impl = self._dict_impl()
285 dict_impl = self._dict_impl()
286 curr_key = dict_impl.__getattribute__(self, 'lt')
286 curr_key = dict_impl.__getattribute__(self, 'lt')
287 while curr_key != _nil:
287 while curr_key != _nil:
288 yield curr_key
288 yield curr_key
289 curr_key = dict_impl.__getitem__(self, curr_key)[0]
289 curr_key = dict_impl.__getitem__(self, curr_key)[0]
290
290
291 __reversed__ = riterkeys
291 __reversed__ = riterkeys
292
292
293 def rkeys(self):
293 def rkeys(self):
294 """List of the keys in reversed order.
294 """List of the keys in reversed order.
295 """
295 """
296 return list(self.riterkeys())
296 return list(self.riterkeys())
297
297
298 def ritervalues(self):
298 def ritervalues(self):
299 """To iterate on values in reversed order.
299 """To iterate on values in reversed order.
300 """
300 """
301 dict_impl = self._dict_impl()
301 dict_impl = self._dict_impl()
302 curr_key = dict_impl.__getattribute__(self, 'lt')
302 curr_key = dict_impl.__getattribute__(self, 'lt')
303 while curr_key != _nil:
303 while curr_key != _nil:
304 curr_key, val, _ = dict_impl.__getitem__(self, curr_key)
304 curr_key, val, _ = dict_impl.__getitem__(self, curr_key)
305 yield val
305 yield val
306
306
307 def rvalues(self):
307 def rvalues(self):
308 """List of the values in reversed order.
308 """List of the values in reversed order.
309 """
309 """
310 return list(self.ritervalues())
310 return list(self.ritervalues())
311
311
312 def riteritems(self):
312 def riteritems(self):
313 """To iterate on (key, value) in reversed order.
313 """To iterate on (key, value) in reversed order.
314 """
314 """
315 dict_impl = self._dict_impl()
315 dict_impl = self._dict_impl()
316 curr_key = dict_impl.__getattribute__(self, 'lt')
316 curr_key = dict_impl.__getattribute__(self, 'lt')
317 while curr_key != _nil:
317 while curr_key != _nil:
318 pred_key, val, _ = dict_impl.__getitem__(self, curr_key)
318 pred_key, val, _ = dict_impl.__getitem__(self, curr_key)
319 yield curr_key, val
319 yield curr_key, val
320 curr_key = pred_key
320 curr_key = pred_key
321
321
322 def ritems(self):
322 def ritems(self):
323 """List of the (key, value) in reversed order.
323 """List of the (key, value) in reversed order.
324 """
324 """
325 return list(self.riteritems())
325 return list(self.riteritems())
326
326
327 def firstkey(self):
327 def firstkey(self):
328 if self:
328 if self:
329 return self._dict_impl().__getattribute__(self, 'lh')
329 return self._dict_impl().__getattribute__(self, 'lh')
330 else:
330 else:
331 raise KeyError("'firstkey(): ordered dictionary is empty'")
331 raise KeyError("'firstkey(): ordered dictionary is empty'")
332
332
333 def lastkey(self):
333 def lastkey(self):
334 if self:
334 if self:
335 return self._dict_impl().__getattribute__(self, 'lt')
335 return self._dict_impl().__getattribute__(self, 'lt')
336 else:
336 else:
337 raise KeyError("'lastkey(): ordered dictionary is empty'")
337 raise KeyError("'lastkey(): ordered dictionary is empty'")
338
338
339 def as_dict(self):
339 def as_dict(self):
340 return self._dict_impl()(self.items())
340 return self._dict_impl()(self.items())
341
341
342 def _repr(self):
342 def _repr(self):
343 """_repr(): low level repr of the whole data contained in the odict.
343 """_repr(): low level repr of the whole data contained in the odict.
344 Useful for debugging.
344 Useful for debugging.
345 """
345 """
346 dict_impl = self._dict_impl()
346 dict_impl = self._dict_impl()
347 form = "odict low level repr lh,lt,data: %r, %r, %s"
347 form = "odict low level repr lh,lt,data: %r, %r, %s"
348 return form % (dict_impl.__getattribute__(self, 'lh'),
348 return form % (dict_impl.__getattribute__(self, 'lh'),
349 dict_impl.__getattribute__(self, 'lt'),
349 dict_impl.__getattribute__(self, 'lt'),
350 dict_impl.__repr__(self))
350 dict_impl.__repr__(self))
351
351
352
352
353 class OrderedDict(_odict, dict):
353 class OrderedDict(_odict, dict):
354
354
355 def _dict_impl(self):
355 def _dict_impl(self):
356 return dict
356 return dict
357
357
358
358
359 #==============================================================================
359 #==============================================================================
360 # OrderedSet
360 # OrderedSet
361 #==============================================================================
361 #==============================================================================
362 from sqlalchemy.util import OrderedSet
362 from sqlalchemy.util import OrderedSet
363
363
364
364
365 #==============================================================================
365 #==============================================================================
366 # kill FUNCTIONS
366 # kill FUNCTIONS
367 #==============================================================================
367 #==============================================================================
368 if __platform__ in PLATFORM_WIN:
368 if __platform__ in PLATFORM_WIN:
369 import ctypes
369 import ctypes
370
370
371 def kill(pid, sig):
371 def kill(pid, sig):
372 """kill function for Win32"""
372 """kill function for Win32"""
373 kernel32 = ctypes.windll.kernel32
373 kernel32 = ctypes.windll.kernel32
374 handle = kernel32.OpenProcess(1, 0, pid)
374 handle = kernel32.OpenProcess(1, 0, pid)
375 return (0 != kernel32.TerminateProcess(handle, 0))
375 return (0 != kernel32.TerminateProcess(handle, 0))
376
376
377 else:
377 else:
378 kill = os.kill
378 kill = os.kill
379
379
380
380
381 #==============================================================================
381 #==============================================================================
382 # itertools.product
382 # itertools.product
383 #==============================================================================
383 #==============================================================================
384
384
385 try:
385 try:
386 from itertools import product
386 from itertools import product
387 except ImportError:
387 except ImportError:
388 def product(*args, **kwds):
388 def product(*args, **kwds):
389 # product('ABCD', 'xy') --> Ax Ay Bx By Cx Cy Dx Dy
389 # product('ABCD', 'xy') --> Ax Ay Bx By Cx Cy Dx Dy
390 # product(range(2), repeat=3) --> 000 001 010 011 100 101 110 111
390 # product(range(2), repeat=3) --> 000 001 010 011 100 101 110 111
391 pools = map(tuple, args) * kwds.get('repeat', 1)
391 pools = map(tuple, args) * kwds.get('repeat', 1)
392 result = [[]]
392 result = [[]]
393 for pool in pools:
393 for pool in pools:
394 result = [x + [y] for x in result for y in pool]
394 result = [x + [y] for x in result for y in pool]
395 for prod in result:
395 for prod in result:
396 yield tuple(prod)
396 yield tuple(prod)
397
397
398
398
399 #==============================================================================
399 #==============================================================================
400 # BytesIO
400 # BytesIO
401 #==============================================================================
401 #==============================================================================
402
402
403 try:
403 try:
404 from io import BytesIO
404 from io import BytesIO
405 except ImportError:
405 except ImportError:
406 from cStringIO import StringIO as BytesIO
406 from cStringIO import StringIO as BytesIO
407
407
408
408
409 #==============================================================================
409 #==============================================================================
410 # bytes
410 # bytes
411 #==============================================================================
411 #==============================================================================
412 if __py_version__ >= (2, 6):
412 if __py_version__ >= (2, 6):
413 _bytes = bytes
413 _bytes = bytes
414 else:
414 else:
415 # in py2.6 bytes is a synonim for str
415 # in py2.6 bytes is a synonim for str
416 _bytes = str
416 _bytes = str
417
417
418 #==============================================================================
418 #==============================================================================
419 # deque
419 # deque
420 #==============================================================================
420 #==============================================================================
421
421
422 if __py_version__ >= (2, 6):
422 if __py_version__ >= (2, 6):
423 from collections import deque
423 from collections import deque
424 else:
424 else:
425 #need to implement our own deque with maxlen
425 #need to implement our own deque with maxlen
426 class deque(object):
426 class deque(object):
427
427
428 def __init__(self, iterable=(), maxlen= -1):
428 def __init__(self, iterable=(), maxlen= -1):
429 if not hasattr(self, 'data'):
429 if not hasattr(self, 'data'):
430 self.left = self.right = 0
430 self.left = self.right = 0
431 self.data = {}
431 self.data = {}
432 self.maxlen = maxlen or -1
432 self.maxlen = maxlen or -1
433 self.extend(iterable)
433 self.extend(iterable)
434
434
435 def append(self, x):
435 def append(self, x):
436 self.data[self.right] = x
436 self.data[self.right] = x
437 self.right += 1
437 self.right += 1
438 if self.maxlen != -1 and len(self) > self.maxlen:
438 if self.maxlen != -1 and len(self) > self.maxlen:
439 self.popleft()
439 self.popleft()
440
440
441 def appendleft(self, x):
441 def appendleft(self, x):
442 self.left -= 1
442 self.left -= 1
443 self.data[self.left] = x
443 self.data[self.left] = x
444 if self.maxlen != -1 and len(self) > self.maxlen:
444 if self.maxlen != -1 and len(self) > self.maxlen:
445 self.pop()
445 self.pop()
446
446
447 def pop(self):
447 def pop(self):
448 if self.left == self.right:
448 if self.left == self.right:
449 raise IndexError('cannot pop from empty deque')
449 raise IndexError('cannot pop from empty deque')
450 self.right -= 1
450 self.right -= 1
451 elem = self.data[self.right]
451 elem = self.data[self.right]
452 del self.data[self.right]
452 del self.data[self.right]
453 return elem
453 return elem
454
454
455 def popleft(self):
455 def popleft(self):
456 if self.left == self.right:
456 if self.left == self.right:
457 raise IndexError('cannot pop from empty deque')
457 raise IndexError('cannot pop from empty deque')
458 elem = self.data[self.left]
458 elem = self.data[self.left]
459 del self.data[self.left]
459 del self.data[self.left]
460 self.left += 1
460 self.left += 1
461 return elem
461 return elem
462
462
463 def clear(self):
463 def clear(self):
464 self.data.clear()
464 self.data.clear()
465 self.left = self.right = 0
465 self.left = self.right = 0
466
466
467 def extend(self, iterable):
467 def extend(self, iterable):
468 for elem in iterable:
468 for elem in iterable:
469 self.append(elem)
469 self.append(elem)
470
470
471 def extendleft(self, iterable):
471 def extendleft(self, iterable):
472 for elem in iterable:
472 for elem in iterable:
473 self.appendleft(elem)
473 self.appendleft(elem)
474
474
475 def rotate(self, n=1):
475 def rotate(self, n=1):
476 if self:
476 if self:
477 n %= len(self)
477 n %= len(self)
478 for i in xrange(n):
478 for i in xrange(n):
479 self.appendleft(self.pop())
479 self.appendleft(self.pop())
480
480
481 def __getitem__(self, i):
481 def __getitem__(self, i):
482 if i < 0:
482 if i < 0:
483 i += len(self)
483 i += len(self)
484 try:
484 try:
485 return self.data[i + self.left]
485 return self.data[i + self.left]
486 except KeyError:
486 except KeyError:
487 raise IndexError
487 raise IndexError
488
488
489 def __setitem__(self, i, value):
489 def __setitem__(self, i, value):
490 if i < 0:
490 if i < 0:
491 i += len(self)
491 i += len(self)
492 try:
492 try:
493 self.data[i + self.left] = value
493 self.data[i + self.left] = value
494 except KeyError:
494 except KeyError:
495 raise IndexError
495 raise IndexError
496
496
497 def __delitem__(self, i):
497 def __delitem__(self, i):
498 size = len(self)
498 size = len(self)
499 if not (-size <= i < size):
499 if not (-size <= i < size):
500 raise IndexError
500 raise IndexError
501 data = self.data
501 data = self.data
502 if i < 0:
502 if i < 0:
503 i += size
503 i += size
504 for j in xrange(self.left + i, self.right - 1):
504 for j in xrange(self.left + i, self.right - 1):
505 data[j] = data[j + 1]
505 data[j] = data[j + 1]
506 self.pop()
506 self.pop()
507
507
508 def __len__(self):
508 def __len__(self):
509 return self.right - self.left
509 return self.right - self.left
510
510
511 def __cmp__(self, other):
511 def __cmp__(self, other):
512 if type(self) != type(other):
512 if type(self) != type(other):
513 return cmp(type(self), type(other))
513 return cmp(type(self), type(other))
514 return cmp(list(self), list(other))
514 return cmp(list(self), list(other))
515
515
516 def __repr__(self, _track=[]):
516 def __repr__(self, _track=[]):
517 if id(self) in _track:
517 if id(self) in _track:
518 return '...'
518 return '...'
519 _track.append(id(self))
519 _track.append(id(self))
520 r = 'deque(%r, maxlen=%s)' % (list(self), self.maxlen)
520 r = 'deque(%r, maxlen=%s)' % (list(self), self.maxlen)
521 _track.remove(id(self))
521 _track.remove(id(self))
522 return r
522 return r
523
523
524 def __getstate__(self):
524 def __getstate__(self):
525 return (tuple(self),)
525 return (tuple(self),)
526
526
527 def __setstate__(self, s):
527 def __setstate__(self, s):
528 self.__init__(s[0])
528 self.__init__(s[0])
529
529
530 def __hash__(self):
530 def __hash__(self):
531 raise TypeError
531 raise TypeError
532
532
533 def __copy__(self):
533 def __copy__(self):
534 return self.__class__(self)
534 return self.__class__(self)
535
535
536 def __deepcopy__(self, memo={}):
536 def __deepcopy__(self, memo={}):
537 from copy import deepcopy
537 from copy import deepcopy
538 result = self.__class__()
538 result = self.__class__()
539 memo[id(self)] = result
539 memo[id(self)] = result
540 result.__init__(deepcopy(tuple(self), memo))
540 result.__init__(deepcopy(tuple(self), memo))
541 return result
541 return result
542
542
543
543
544 #==============================================================================
544 #==============================================================================
545 # threading.Event
545 # threading.Event
546 #==============================================================================
546 #==============================================================================
547
547
548 if __py_version__ >= (2, 6):
548 if __py_version__ >= (2, 6):
549 from threading import Event, Thread
549 from threading import Event, Thread
550 else:
550 else:
551 from threading import _Verbose, Condition, Lock, Thread
551 from threading import _Verbose, Condition, Lock, Thread
552
552
553 def Event(*args, **kwargs):
553 def Event(*args, **kwargs):
554 return _Event(*args, **kwargs)
554 return _Event(*args, **kwargs)
555
555
556 class _Event(_Verbose):
556 class _Event(_Verbose):
557
557
558 # After Tim Peters' event class (without is_posted())
558 # After Tim Peters' event class (without is_posted())
559
559
560 def __init__(self, verbose=None):
560 def __init__(self, verbose=None):
561 _Verbose.__init__(self, verbose)
561 _Verbose.__init__(self, verbose)
562 self.__cond = Condition(Lock())
562 self.__cond = Condition(Lock())
563 self.__flag = False
563 self.__flag = False
564
564
565 def isSet(self):
565 def isSet(self):
566 return self.__flag
566 return self.__flag
567
567
568 is_set = isSet
568 is_set = isSet
569
569
570 def set(self):
570 def set(self):
571 self.__cond.acquire()
571 self.__cond.acquire()
572 try:
572 try:
573 self.__flag = True
573 self.__flag = True
574 self.__cond.notify_all()
574 self.__cond.notify_all()
575 finally:
575 finally:
576 self.__cond.release()
576 self.__cond.release()
577
577
578 def clear(self):
578 def clear(self):
579 self.__cond.acquire()
579 self.__cond.acquire()
580 try:
580 try:
581 self.__flag = False
581 self.__flag = False
582 finally:
582 finally:
583 self.__cond.release()
583 self.__cond.release()
584
584
585 def wait(self, timeout=None):
585 def wait(self, timeout=None):
586 self.__cond.acquire()
586 self.__cond.acquire()
587 try:
587 try:
588 if not self.__flag:
588 if not self.__flag:
589 self.__cond.wait(timeout)
589 self.__cond.wait(timeout)
590 finally:
590 finally:
591 self.__cond.release()
591 self.__cond.release()
592
593
594
@@ -1,1320 +1,1320 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.model.db_1_3_0
3 rhodecode.model.db_1_3_0
4 ~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 Database Models for RhodeCode <=1.3.X
6 Database Models for RhodeCode <=1.3.X
7
7
8 :created_on: Apr 08, 2010
8 :created_on: Apr 08, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software: you can redistribute it and/or modify
13 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 3 of the License, or
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25
25
26 import os
26 import os
27 import logging
27 import logging
28 import datetime
28 import datetime
29 import traceback
29 import traceback
30 from collections import defaultdict
30 from collections import defaultdict
31
31
32 from sqlalchemy import *
32 from sqlalchemy import *
33 from sqlalchemy.ext.hybrid import hybrid_property
33 from sqlalchemy.ext.hybrid import hybrid_property
34 from sqlalchemy.orm import relationship, joinedload, class_mapper, validates
34 from sqlalchemy.orm import relationship, joinedload, class_mapper, validates
35 from beaker.cache import cache_region, region_invalidate
35 from beaker.cache import cache_region, region_invalidate
36
36
37 from rhodecode.lib.vcs import get_backend
37 from rhodecode.lib.vcs import get_backend
38 from rhodecode.lib.vcs.utils.helpers import get_scm
38 from rhodecode.lib.vcs.utils.helpers import get_scm
39 from rhodecode.lib.vcs.exceptions import VCSError
39 from rhodecode.lib.vcs.exceptions import VCSError
40 from rhodecode.lib.vcs.utils.lazy import LazyProperty
40 from rhodecode.lib.vcs.utils.lazy import LazyProperty
41
41
42 from rhodecode.lib.utils2 import str2bool, safe_str, get_changeset_safe, \
42 from rhodecode.lib.utils2 import str2bool, safe_str, get_changeset_safe, \
43 safe_unicode
43 safe_unicode
44 from rhodecode.lib.compat import json
44 from rhodecode.lib.compat import json
45 from rhodecode.lib.caching_query import FromCache
45 from rhodecode.lib.caching_query import FromCache
46
46
47 from rhodecode.model.meta import Base, Session
47 from rhodecode.model.meta import Base, Session
48 import hashlib
48 import hashlib
49
49
50
50
51 log = logging.getLogger(__name__)
51 log = logging.getLogger(__name__)
52
52
53 #==============================================================================
53 #==============================================================================
54 # BASE CLASSES
54 # BASE CLASSES
55 #==============================================================================
55 #==============================================================================
56
56
57 _hash_key = lambda k: hashlib.md5(safe_str(k)).hexdigest()
57 _hash_key = lambda k: hashlib.md5(safe_str(k)).hexdigest()
58
58
59
59
60 class ModelSerializer(json.JSONEncoder):
60 class ModelSerializer(json.JSONEncoder):
61 """
61 """
62 Simple Serializer for JSON,
62 Simple Serializer for JSON,
63
63
64 usage::
64 usage::
65
65
66 to make object customized for serialization implement a __json__
66 to make object customized for serialization implement a __json__
67 method that will return a dict for serialization into json
67 method that will return a dict for serialization into json
68
68
69 example::
69 example::
70
70
71 class Task(object):
71 class Task(object):
72
72
73 def __init__(self, name, value):
73 def __init__(self, name, value):
74 self.name = name
74 self.name = name
75 self.value = value
75 self.value = value
76
76
77 def __json__(self):
77 def __json__(self):
78 return dict(name=self.name,
78 return dict(name=self.name,
79 value=self.value)
79 value=self.value)
80
80
81 """
81 """
82
82
83 def default(self, obj):
83 def default(self, obj):
84
84
85 if hasattr(obj, '__json__'):
85 if hasattr(obj, '__json__'):
86 return obj.__json__()
86 return obj.__json__()
87 else:
87 else:
88 return json.JSONEncoder.default(self, obj)
88 return json.JSONEncoder.default(self, obj)
89
89
90
90
91 class BaseModel(object):
91 class BaseModel(object):
92 """
92 """
93 Base Model for all classess
93 Base Model for all classess
94 """
94 """
95
95
96 @classmethod
96 @classmethod
97 def _get_keys(cls):
97 def _get_keys(cls):
98 """return column names for this model """
98 """return column names for this model """
99 return class_mapper(cls).c.keys()
99 return class_mapper(cls).c.keys()
100
100
101 def get_dict(self):
101 def get_dict(self):
102 """
102 """
103 return dict with keys and values corresponding
103 return dict with keys and values corresponding
104 to this model data """
104 to this model data """
105
105
106 d = {}
106 d = {}
107 for k in self._get_keys():
107 for k in self._get_keys():
108 d[k] = getattr(self, k)
108 d[k] = getattr(self, k)
109
109
110 # also use __json__() if present to get additional fields
110 # also use __json__() if present to get additional fields
111 for k, val in getattr(self, '__json__', lambda: {})().iteritems():
111 for k, val in getattr(self, '__json__', lambda: {})().iteritems():
112 d[k] = val
112 d[k] = val
113 return d
113 return d
114
114
115 def get_appstruct(self):
115 def get_appstruct(self):
116 """return list with keys and values tupples corresponding
116 """return list with keys and values tupples corresponding
117 to this model data """
117 to this model data """
118
118
119 l = []
119 l = []
120 for k in self._get_keys():
120 for k in self._get_keys():
121 l.append((k, getattr(self, k),))
121 l.append((k, getattr(self, k),))
122 return l
122 return l
123
123
124 def populate_obj(self, populate_dict):
124 def populate_obj(self, populate_dict):
125 """populate model with data from given populate_dict"""
125 """populate model with data from given populate_dict"""
126
126
127 for k in self._get_keys():
127 for k in self._get_keys():
128 if k in populate_dict:
128 if k in populate_dict:
129 setattr(self, k, populate_dict[k])
129 setattr(self, k, populate_dict[k])
130
130
131 @classmethod
131 @classmethod
132 def query(cls):
132 def query(cls):
133 return Session.query(cls)
133 return Session.query(cls)
134
134
135 @classmethod
135 @classmethod
136 def get(cls, id_):
136 def get(cls, id_):
137 if id_:
137 if id_:
138 return cls.query().get(id_)
138 return cls.query().get(id_)
139
139
140 @classmethod
140 @classmethod
141 def getAll(cls):
141 def getAll(cls):
142 return cls.query().all()
142 return cls.query().all()
143
143
144 @classmethod
144 @classmethod
145 def delete(cls, id_):
145 def delete(cls, id_):
146 obj = cls.query().get(id_)
146 obj = cls.query().get(id_)
147 Session.delete(obj)
147 Session.delete(obj)
148
148
149 def __repr__(self):
149 def __repr__(self):
150 if hasattr(self, '__unicode__'):
150 if hasattr(self, '__unicode__'):
151 # python repr needs to return str
151 # python repr needs to return str
152 return safe_str(self.__unicode__())
152 return safe_str(self.__unicode__())
153 return '<DB:%s>' % (self.__class__.__name__)
153 return '<DB:%s>' % (self.__class__.__name__)
154
154
155 class RhodeCodeSetting(Base, BaseModel):
155 class RhodeCodeSetting(Base, BaseModel):
156 __tablename__ = 'rhodecode_settings'
156 __tablename__ = 'rhodecode_settings'
157 __table_args__ = (
157 __table_args__ = (
158 UniqueConstraint('app_settings_name'),
158 UniqueConstraint('app_settings_name'),
159 {'extend_existing': True, 'mysql_engine':'InnoDB',
159 {'extend_existing': True, 'mysql_engine':'InnoDB',
160 'mysql_charset': 'utf8'}
160 'mysql_charset': 'utf8'}
161 )
161 )
162 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
162 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
163 app_settings_name = Column("app_settings_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
163 app_settings_name = Column("app_settings_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
164 _app_settings_value = Column("app_settings_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
164 _app_settings_value = Column("app_settings_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
165
165
166 def __init__(self, k='', v=''):
166 def __init__(self, k='', v=''):
167 self.app_settings_name = k
167 self.app_settings_name = k
168 self.app_settings_value = v
168 self.app_settings_value = v
169
169
170 @validates('_app_settings_value')
170 @validates('_app_settings_value')
171 def validate_settings_value(self, key, val):
171 def validate_settings_value(self, key, val):
172 assert type(val) == unicode
172 assert type(val) == unicode
173 return val
173 return val
174
174
175 @hybrid_property
175 @hybrid_property
176 def app_settings_value(self):
176 def app_settings_value(self):
177 v = self._app_settings_value
177 v = self._app_settings_value
178 if self.app_settings_name == 'ldap_active':
178 if self.app_settings_name == 'ldap_active':
179 v = str2bool(v)
179 v = str2bool(v)
180 return v
180 return v
181
181
182 @app_settings_value.setter
182 @app_settings_value.setter
183 def app_settings_value(self, val):
183 def app_settings_value(self, val):
184 """
184 """
185 Setter that will always make sure we use unicode in app_settings_value
185 Setter that will always make sure we use unicode in app_settings_value
186
186
187 :param val:
187 :param val:
188 """
188 """
189 self._app_settings_value = safe_unicode(val)
189 self._app_settings_value = safe_unicode(val)
190
190
191 def __unicode__(self):
191 def __unicode__(self):
192 return u"<%s('%s:%s')>" % (
192 return u"<%s('%s:%s')>" % (
193 self.__class__.__name__,
193 self.__class__.__name__,
194 self.app_settings_name, self.app_settings_value
194 self.app_settings_name, self.app_settings_value
195 )
195 )
196
196
197 @classmethod
197 @classmethod
198 def get_by_name(cls, ldap_key):
198 def get_by_name(cls, ldap_key):
199 return cls.query()\
199 return cls.query()\
200 .filter(cls.app_settings_name == ldap_key).scalar()
200 .filter(cls.app_settings_name == ldap_key).scalar()
201
201
202 @classmethod
202 @classmethod
203 def get_app_settings(cls, cache=False):
203 def get_app_settings(cls, cache=False):
204
204
205 ret = cls.query()
205 ret = cls.query()
206
206
207 if cache:
207 if cache:
208 ret = ret.options(FromCache("sql_cache_short", "get_hg_settings"))
208 ret = ret.options(FromCache("sql_cache_short", "get_hg_settings"))
209
209
210 if not ret:
210 if not ret:
211 raise Exception('Could not get application settings !')
211 raise Exception('Could not get application settings !')
212 settings = {}
212 settings = {}
213 for each in ret:
213 for each in ret:
214 settings['rhodecode_' + each.app_settings_name] = \
214 settings['rhodecode_' + each.app_settings_name] = \
215 each.app_settings_value
215 each.app_settings_value
216
216
217 return settings
217 return settings
218
218
219 @classmethod
219 @classmethod
220 def get_ldap_settings(cls, cache=False):
220 def get_ldap_settings(cls, cache=False):
221 ret = cls.query()\
221 ret = cls.query()\
222 .filter(cls.app_settings_name.startswith('ldap_')).all()
222 .filter(cls.app_settings_name.startswith('ldap_')).all()
223 fd = {}
223 fd = {}
224 for row in ret:
224 for row in ret:
225 fd.update({row.app_settings_name:row.app_settings_value})
225 fd.update({row.app_settings_name:row.app_settings_value})
226
226
227 return fd
227 return fd
228
228
229
229
230 class RhodeCodeUi(Base, BaseModel):
230 class RhodeCodeUi(Base, BaseModel):
231 __tablename__ = 'rhodecode_ui'
231 __tablename__ = 'rhodecode_ui'
232 __table_args__ = (
232 __table_args__ = (
233 UniqueConstraint('ui_key'),
233 UniqueConstraint('ui_key'),
234 {'extend_existing': True, 'mysql_engine':'InnoDB',
234 {'extend_existing': True, 'mysql_engine':'InnoDB',
235 'mysql_charset': 'utf8'}
235 'mysql_charset': 'utf8'}
236 )
236 )
237
237
238 HOOK_UPDATE = 'changegroup.update'
238 HOOK_UPDATE = 'changegroup.update'
239 HOOK_REPO_SIZE = 'changegroup.repo_size'
239 HOOK_REPO_SIZE = 'changegroup.repo_size'
240 HOOK_PUSH = 'pretxnchangegroup.push_logger'
240 HOOK_PUSH = 'pretxnchangegroup.push_logger'
241 HOOK_PULL = 'preoutgoing.pull_logger'
241 HOOK_PULL = 'preoutgoing.pull_logger'
242
242
243 ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
243 ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
244 ui_section = Column("ui_section", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
244 ui_section = Column("ui_section", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
245 ui_key = Column("ui_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
245 ui_key = Column("ui_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
246 ui_value = Column("ui_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
246 ui_value = Column("ui_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
247 ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
247 ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
248
248
249 @classmethod
249 @classmethod
250 def get_by_key(cls, key):
250 def get_by_key(cls, key):
251 return cls.query().filter(cls.ui_key == key)
251 return cls.query().filter(cls.ui_key == key)
252
252
253 @classmethod
253 @classmethod
254 def get_builtin_hooks(cls):
254 def get_builtin_hooks(cls):
255 q = cls.query()
255 q = cls.query()
256 q = q.filter(cls.ui_key.in_([cls.HOOK_UPDATE,
256 q = q.filter(cls.ui_key.in_([cls.HOOK_UPDATE,
257 cls.HOOK_REPO_SIZE,
257 cls.HOOK_REPO_SIZE,
258 cls.HOOK_PUSH, cls.HOOK_PULL]))
258 cls.HOOK_PUSH, cls.HOOK_PULL]))
259 return q.all()
259 return q.all()
260
260
261 @classmethod
261 @classmethod
262 def get_custom_hooks(cls):
262 def get_custom_hooks(cls):
263 q = cls.query()
263 q = cls.query()
264 q = q.filter(~cls.ui_key.in_([cls.HOOK_UPDATE,
264 q = q.filter(~cls.ui_key.in_([cls.HOOK_UPDATE,
265 cls.HOOK_REPO_SIZE,
265 cls.HOOK_REPO_SIZE,
266 cls.HOOK_PUSH, cls.HOOK_PULL]))
266 cls.HOOK_PUSH, cls.HOOK_PULL]))
267 q = q.filter(cls.ui_section == 'hooks')
267 q = q.filter(cls.ui_section == 'hooks')
268 return q.all()
268 return q.all()
269
269
270 @classmethod
270 @classmethod
271 def create_or_update_hook(cls, key, val):
271 def create_or_update_hook(cls, key, val):
272 new_ui = cls.get_by_key(key).scalar() or cls()
272 new_ui = cls.get_by_key(key).scalar() or cls()
273 new_ui.ui_section = 'hooks'
273 new_ui.ui_section = 'hooks'
274 new_ui.ui_active = True
274 new_ui.ui_active = True
275 new_ui.ui_key = key
275 new_ui.ui_key = key
276 new_ui.ui_value = val
276 new_ui.ui_value = val
277
277
278 Session.add(new_ui)
278 Session.add(new_ui)
279
279
280
280
281 class User(Base, BaseModel):
281 class User(Base, BaseModel):
282 __tablename__ = 'users'
282 __tablename__ = 'users'
283 __table_args__ = (
283 __table_args__ = (
284 UniqueConstraint('username'), UniqueConstraint('email'),
284 UniqueConstraint('username'), UniqueConstraint('email'),
285 {'extend_existing': True, 'mysql_engine':'InnoDB',
285 {'extend_existing': True, 'mysql_engine':'InnoDB',
286 'mysql_charset': 'utf8'}
286 'mysql_charset': 'utf8'}
287 )
287 )
288 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
288 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
289 username = Column("username", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
289 username = Column("username", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
290 password = Column("password", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
290 password = Column("password", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
291 active = Column("active", Boolean(), nullable=True, unique=None, default=None)
291 active = Column("active", Boolean(), nullable=True, unique=None, default=None)
292 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
292 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
293 name = Column("name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
293 name = Column("name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
294 lastname = Column("lastname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
294 lastname = Column("lastname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
295 _email = Column("email", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
295 _email = Column("email", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
296 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
296 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
297 ldap_dn = Column("ldap_dn", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
297 ldap_dn = Column("ldap_dn", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
298 api_key = Column("api_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
298 api_key = Column("api_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
299
299
300 user_log = relationship('UserLog', cascade='all')
300 user_log = relationship('UserLog', cascade='all')
301 user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
301 user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
302
302
303 repositories = relationship('Repository')
303 repositories = relationship('Repository')
304 user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
304 user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
305 repo_to_perm = relationship('UserRepoToPerm', primaryjoin='UserRepoToPerm.user_id==User.user_id', cascade='all')
305 repo_to_perm = relationship('UserRepoToPerm', primaryjoin='UserRepoToPerm.user_id==User.user_id', cascade='all')
306 repo_group_to_perm = relationship('UserRepoGroupToPerm', primaryjoin='UserRepoGroupToPerm.user_id==User.user_id', cascade='all')
306 repo_group_to_perm = relationship('UserRepoGroupToPerm', primaryjoin='UserRepoGroupToPerm.user_id==User.user_id', cascade='all')
307
307
308 group_member = relationship('UsersGroupMember', cascade='all')
308 group_member = relationship('UsersGroupMember', cascade='all')
309
309
310 notifications = relationship('UserNotification', cascade='all')
310 notifications = relationship('UserNotification', cascade='all')
311 # notifications assigned to this user
311 # notifications assigned to this user
312 user_created_notifications = relationship('Notification', cascade='all')
312 user_created_notifications = relationship('Notification', cascade='all')
313 # comments created by this user
313 # comments created by this user
314 user_comments = relationship('ChangesetComment', cascade='all')
314 user_comments = relationship('ChangesetComment', cascade='all')
315
315
316 @hybrid_property
316 @hybrid_property
317 def email(self):
317 def email(self):
318 return self._email
318 return self._email
319
319
320 @email.setter
320 @email.setter
321 def email(self, val):
321 def email(self, val):
322 self._email = val.lower() if val else None
322 self._email = val.lower() if val else None
323
323
324 @property
324 @property
325 def full_name(self):
325 def full_name(self):
326 return '%s %s' % (self.name, self.lastname)
326 return '%s %s' % (self.name, self.lastname)
327
327
328 @property
328 @property
329 def full_name_or_username(self):
329 def full_name_or_username(self):
330 return ('%s %s' % (self.name, self.lastname)
330 return ('%s %s' % (self.name, self.lastname)
331 if (self.name and self.lastname) else self.username)
331 if (self.name and self.lastname) else self.username)
332
332
333 @property
333 @property
334 def full_contact(self):
334 def full_contact(self):
335 return '%s %s <%s>' % (self.name, self.lastname, self.email)
335 return '%s %s <%s>' % (self.name, self.lastname, self.email)
336
336
337 @property
337 @property
338 def short_contact(self):
338 def short_contact(self):
339 return '%s %s' % (self.name, self.lastname)
339 return '%s %s' % (self.name, self.lastname)
340
340
341 @property
341 @property
342 def is_admin(self):
342 def is_admin(self):
343 return self.admin
343 return self.admin
344
344
345 def __unicode__(self):
345 def __unicode__(self):
346 return u"<%s('id:%s:%s')>" % (self.__class__.__name__,
346 return u"<%s('id:%s:%s')>" % (self.__class__.__name__,
347 self.user_id, self.username)
347 self.user_id, self.username)
348
348
349 @classmethod
349 @classmethod
350 def get_by_username(cls, username, case_insensitive=False, cache=False):
350 def get_by_username(cls, username, case_insensitive=False, cache=False):
351 if case_insensitive:
351 if case_insensitive:
352 q = cls.query().filter(cls.username.ilike(username))
352 q = cls.query().filter(cls.username.ilike(username))
353 else:
353 else:
354 q = cls.query().filter(cls.username == username)
354 q = cls.query().filter(cls.username == username)
355
355
356 if cache:
356 if cache:
357 q = q.options(FromCache(
357 q = q.options(FromCache(
358 "sql_cache_short",
358 "sql_cache_short",
359 "get_user_%s" % _hash_key(username)
359 "get_user_%s" % _hash_key(username)
360 )
360 )
361 )
361 )
362 return q.scalar()
362 return q.scalar()
363
363
364 @classmethod
364 @classmethod
365 def get_by_api_key(cls, api_key, cache=False):
365 def get_by_api_key(cls, api_key, cache=False):
366 q = cls.query().filter(cls.api_key == api_key)
366 q = cls.query().filter(cls.api_key == api_key)
367
367
368 if cache:
368 if cache:
369 q = q.options(FromCache("sql_cache_short",
369 q = q.options(FromCache("sql_cache_short",
370 "get_api_key_%s" % api_key))
370 "get_api_key_%s" % api_key))
371 return q.scalar()
371 return q.scalar()
372
372
373 @classmethod
373 @classmethod
374 def get_by_email(cls, email, case_insensitive=False, cache=False):
374 def get_by_email(cls, email, case_insensitive=False, cache=False):
375 if case_insensitive:
375 if case_insensitive:
376 q = cls.query().filter(cls.email.ilike(email))
376 q = cls.query().filter(cls.email.ilike(email))
377 else:
377 else:
378 q = cls.query().filter(cls.email == email)
378 q = cls.query().filter(cls.email == email)
379
379
380 if cache:
380 if cache:
381 q = q.options(FromCache("sql_cache_short",
381 q = q.options(FromCache("sql_cache_short",
382 "get_api_key_%s" % email))
382 "get_api_key_%s" % email))
383 return q.scalar()
383 return q.scalar()
384
384
385 def update_lastlogin(self):
385 def update_lastlogin(self):
386 """Update user lastlogin"""
386 """Update user lastlogin"""
387 self.last_login = datetime.datetime.now()
387 self.last_login = datetime.datetime.now()
388 Session.add(self)
388 Session.add(self)
389 log.debug('updated user %s lastlogin' % self.username)
389 log.debug('updated user %s lastlogin' % self.username)
390
390
391 def __json__(self):
391 def __json__(self):
392 return dict(
392 return dict(
393 user_id=self.user_id,
393 user_id=self.user_id,
394 first_name=self.name,
394 first_name=self.name,
395 last_name=self.lastname,
395 last_name=self.lastname,
396 email=self.email,
396 email=self.email,
397 full_name=self.full_name,
397 full_name=self.full_name,
398 full_name_or_username=self.full_name_or_username,
398 full_name_or_username=self.full_name_or_username,
399 short_contact=self.short_contact,
399 short_contact=self.short_contact,
400 full_contact=self.full_contact
400 full_contact=self.full_contact
401 )
401 )
402
402
403
403
404 class UserLog(Base, BaseModel):
404 class UserLog(Base, BaseModel):
405 __tablename__ = 'user_logs'
405 __tablename__ = 'user_logs'
406 __table_args__ = (
406 __table_args__ = (
407 {'extend_existing': True, 'mysql_engine':'InnoDB',
407 {'extend_existing': True, 'mysql_engine':'InnoDB',
408 'mysql_charset': 'utf8'},
408 'mysql_charset': 'utf8'},
409 )
409 )
410 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
410 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
411 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
411 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
412 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True)
412 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True)
413 repository_name = Column("repository_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
413 repository_name = Column("repository_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
414 user_ip = Column("user_ip", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
414 user_ip = Column("user_ip", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
415 action = Column("action", UnicodeText(length=1200000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
415 action = Column("action", UnicodeText(length=1200000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
416 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
416 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
417
417
418 @property
418 @property
419 def action_as_day(self):
419 def action_as_day(self):
420 return datetime.date(*self.action_date.timetuple()[:3])
420 return datetime.date(*self.action_date.timetuple()[:3])
421
421
422 user = relationship('User')
422 user = relationship('User')
423 repository = relationship('Repository', cascade='')
423 repository = relationship('Repository', cascade='')
424
424
425
425
426 class UsersGroup(Base, BaseModel):
426 class UsersGroup(Base, BaseModel):
427 __tablename__ = 'users_groups'
427 __tablename__ = 'users_groups'
428 __table_args__ = (
428 __table_args__ = (
429 {'extend_existing': True, 'mysql_engine':'InnoDB',
429 {'extend_existing': True, 'mysql_engine':'InnoDB',
430 'mysql_charset': 'utf8'},
430 'mysql_charset': 'utf8'},
431 )
431 )
432
432
433 users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
433 users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
434 users_group_name = Column("users_group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
434 users_group_name = Column("users_group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
435 users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
435 users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
436
436
437 members = relationship('UsersGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
437 members = relationship('UsersGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
438 users_group_to_perm = relationship('UsersGroupToPerm', cascade='all')
438 users_group_to_perm = relationship('UsersGroupToPerm', cascade='all')
439 users_group_repo_to_perm = relationship('UsersGroupRepoToPerm', cascade='all')
439 users_group_repo_to_perm = relationship('UsersGroupRepoToPerm', cascade='all')
440
440
441 def __unicode__(self):
441 def __unicode__(self):
442 return u'<userGroup(%s)>' % (self.users_group_name)
442 return u'<userGroup(%s)>' % (self.users_group_name)
443
443
444 @classmethod
444 @classmethod
445 def get_by_group_name(cls, group_name, cache=False,
445 def get_by_group_name(cls, group_name, cache=False,
446 case_insensitive=False):
446 case_insensitive=False):
447 if case_insensitive:
447 if case_insensitive:
448 q = cls.query().filter(cls.users_group_name.ilike(group_name))
448 q = cls.query().filter(cls.users_group_name.ilike(group_name))
449 else:
449 else:
450 q = cls.query().filter(cls.users_group_name == group_name)
450 q = cls.query().filter(cls.users_group_name == group_name)
451 if cache:
451 if cache:
452 q = q.options(FromCache(
452 q = q.options(FromCache(
453 "sql_cache_short",
453 "sql_cache_short",
454 "get_user_%s" % _hash_key(group_name)
454 "get_user_%s" % _hash_key(group_name)
455 )
455 )
456 )
456 )
457 return q.scalar()
457 return q.scalar()
458
458
459 @classmethod
459 @classmethod
460 def get(cls, users_group_id, cache=False):
460 def get(cls, users_group_id, cache=False):
461 users_group = cls.query()
461 users_group = cls.query()
462 if cache:
462 if cache:
463 users_group = users_group.options(FromCache("sql_cache_short",
463 users_group = users_group.options(FromCache("sql_cache_short",
464 "get_users_group_%s" % users_group_id))
464 "get_users_group_%s" % users_group_id))
465 return users_group.get(users_group_id)
465 return users_group.get(users_group_id)
466
466
467
467
468 class UsersGroupMember(Base, BaseModel):
468 class UsersGroupMember(Base, BaseModel):
469 __tablename__ = 'users_groups_members'
469 __tablename__ = 'users_groups_members'
470 __table_args__ = (
470 __table_args__ = (
471 {'extend_existing': True, 'mysql_engine':'InnoDB',
471 {'extend_existing': True, 'mysql_engine':'InnoDB',
472 'mysql_charset': 'utf8'},
472 'mysql_charset': 'utf8'},
473 )
473 )
474
474
475 users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
475 users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
476 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
476 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
477 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
477 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
478
478
479 user = relationship('User', lazy='joined')
479 user = relationship('User', lazy='joined')
480 users_group = relationship('UsersGroup')
480 users_group = relationship('UsersGroup')
481
481
482 def __init__(self, gr_id='', u_id=''):
482 def __init__(self, gr_id='', u_id=''):
483 self.users_group_id = gr_id
483 self.users_group_id = gr_id
484 self.user_id = u_id
484 self.user_id = u_id
485
485
486
486
487 class Repository(Base, BaseModel):
487 class Repository(Base, BaseModel):
488 __tablename__ = 'repositories'
488 __tablename__ = 'repositories'
489 __table_args__ = (
489 __table_args__ = (
490 UniqueConstraint('repo_name'),
490 UniqueConstraint('repo_name'),
491 {'extend_existing': True, 'mysql_engine':'InnoDB',
491 {'extend_existing': True, 'mysql_engine':'InnoDB',
492 'mysql_charset': 'utf8'},
492 'mysql_charset': 'utf8'},
493 )
493 )
494
494
495 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
495 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
496 repo_name = Column("repo_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
496 repo_name = Column("repo_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
497 clone_uri = Column("clone_uri", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
497 clone_uri = Column("clone_uri", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
498 repo_type = Column("repo_type", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default='hg')
498 repo_type = Column("repo_type", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default='hg')
499 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
499 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
500 private = Column("private", Boolean(), nullable=True, unique=None, default=None)
500 private = Column("private", Boolean(), nullable=True, unique=None, default=None)
501 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
501 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
502 enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True)
502 enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True)
503 description = Column("description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
503 description = Column("description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
504 created_on = Column('created_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
504 created_on = Column('created_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
505
505
506 fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
506 fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
507 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
507 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
508
508
509 user = relationship('User')
509 user = relationship('User')
510 fork = relationship('Repository', remote_side=repo_id)
510 fork = relationship('Repository', remote_side=repo_id)
511 group = relationship('RepoGroup')
511 group = relationship('RepoGroup')
512 repo_to_perm = relationship('UserRepoToPerm', cascade='all', order_by='UserRepoToPerm.repo_to_perm_id')
512 repo_to_perm = relationship('UserRepoToPerm', cascade='all', order_by='UserRepoToPerm.repo_to_perm_id')
513 users_group_to_perm = relationship('UsersGroupRepoToPerm', cascade='all')
513 users_group_to_perm = relationship('UsersGroupRepoToPerm', cascade='all')
514 stats = relationship('Statistics', cascade='all', uselist=False)
514 stats = relationship('Statistics', cascade='all', uselist=False)
515
515
516 followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id', cascade='all')
516 followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id', cascade='all')
517
517
518 logs = relationship('UserLog')
518 logs = relationship('UserLog')
519
519
520 def __unicode__(self):
520 def __unicode__(self):
521 return u"<%s('%s:%s')>" % (self.__class__.__name__,self.repo_id,
521 return u"<%s('%s:%s')>" % (self.__class__.__name__,self.repo_id,
522 self.repo_name)
522 self.repo_name)
523
523
524 @classmethod
524 @classmethod
525 def url_sep(cls):
525 def url_sep(cls):
526 return '/'
526 return '/'
527
527
528 @classmethod
528 @classmethod
529 def get_by_repo_name(cls, repo_name):
529 def get_by_repo_name(cls, repo_name):
530 q = Session.query(cls).filter(cls.repo_name == repo_name)
530 q = Session.query(cls).filter(cls.repo_name == repo_name)
531 q = q.options(joinedload(Repository.fork))\
531 q = q.options(joinedload(Repository.fork))\
532 .options(joinedload(Repository.user))\
532 .options(joinedload(Repository.user))\
533 .options(joinedload(Repository.group))
533 .options(joinedload(Repository.group))
534 return q.scalar()
534 return q.scalar()
535
535
536 @classmethod
536 @classmethod
537 def get_repo_forks(cls, repo_id):
537 def get_repo_forks(cls, repo_id):
538 return cls.query().filter(Repository.fork_id == repo_id)
538 return cls.query().filter(Repository.fork_id == repo_id)
539
539
540 @classmethod
540 @classmethod
541 def base_path(cls):
541 def base_path(cls):
542 """
542 """
543 Returns base path when all repos are stored
543 Returns base path when all repos are stored
544
544
545 :param cls:
545 :param cls:
546 """
546 """
547 q = Session.query(RhodeCodeUi)\
547 q = Session.query(RhodeCodeUi)\
548 .filter(RhodeCodeUi.ui_key == cls.url_sep())
548 .filter(RhodeCodeUi.ui_key == cls.url_sep())
549 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
549 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
550 return q.one().ui_value
550 return q.one().ui_value
551
551
552 @property
552 @property
553 def just_name(self):
553 def just_name(self):
554 return self.repo_name.split(Repository.url_sep())[-1]
554 return self.repo_name.split(Repository.url_sep())[-1]
555
555
556 @property
556 @property
557 def groups_with_parents(self):
557 def groups_with_parents(self):
558 groups = []
558 groups = []
559 if self.group is None:
559 if self.group is None:
560 return groups
560 return groups
561
561
562 cur_gr = self.group
562 cur_gr = self.group
563 groups.insert(0, cur_gr)
563 groups.insert(0, cur_gr)
564 while 1:
564 while 1:
565 gr = getattr(cur_gr, 'parent_group', None)
565 gr = getattr(cur_gr, 'parent_group', None)
566 cur_gr = cur_gr.parent_group
566 cur_gr = cur_gr.parent_group
567 if gr is None:
567 if gr is None:
568 break
568 break
569 groups.insert(0, gr)
569 groups.insert(0, gr)
570
570
571 return groups
571 return groups
572
572
573 @property
573 @property
574 def groups_and_repo(self):
574 def groups_and_repo(self):
575 return self.groups_with_parents, self.just_name
575 return self.groups_with_parents, self.just_name
576
576
577 @LazyProperty
577 @LazyProperty
578 def repo_path(self):
578 def repo_path(self):
579 """
579 """
580 Returns base full path for that repository means where it actually
580 Returns base full path for that repository means where it actually
581 exists on a filesystem
581 exists on a filesystem
582 """
582 """
583 q = Session.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key ==
583 q = Session.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key ==
584 Repository.url_sep())
584 Repository.url_sep())
585 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
585 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
586 return q.one().ui_value
586 return q.one().ui_value
587
587
588 @property
588 @property
589 def repo_full_path(self):
589 def repo_full_path(self):
590 p = [self.repo_path]
590 p = [self.repo_path]
591 # we need to split the name by / since this is how we store the
591 # we need to split the name by / since this is how we store the
592 # names in the database, but that eventually needs to be converted
592 # names in the database, but that eventually needs to be converted
593 # into a valid system path
593 # into a valid system path
594 p += self.repo_name.split(Repository.url_sep())
594 p += self.repo_name.split(Repository.url_sep())
595 return os.path.join(*p)
595 return os.path.join(*p)
596
596
597 def get_new_name(self, repo_name):
597 def get_new_name(self, repo_name):
598 """
598 """
599 returns new full repository name based on assigned group and new new
599 returns new full repository name based on assigned group and new new
600
600
601 :param group_name:
601 :param group_name:
602 """
602 """
603 path_prefix = self.group.full_path_splitted if self.group else []
603 path_prefix = self.group.full_path_splitted if self.group else []
604 return Repository.url_sep().join(path_prefix + [repo_name])
604 return Repository.url_sep().join(path_prefix + [repo_name])
605
605
606 @property
606 @property
607 def _ui(self):
607 def _ui(self):
608 """
608 """
609 Creates an db based ui object for this repository
609 Creates an db based ui object for this repository
610 """
610 """
611 from mercurial import ui
611 from mercurial import ui
612 from mercurial import config
612 from mercurial import config
613 baseui = ui.ui()
613 baseui = ui.ui()
614
614
615 #clean the baseui object
615 #clean the baseui object
616 baseui._ocfg = config.config()
616 baseui._ocfg = config.config()
617 baseui._ucfg = config.config()
617 baseui._ucfg = config.config()
618 baseui._tcfg = config.config()
618 baseui._tcfg = config.config()
619
619
620 ret = RhodeCodeUi.query()\
620 ret = RhodeCodeUi.query()\
621 .options(FromCache("sql_cache_short", "repository_repo_ui")).all()
621 .options(FromCache("sql_cache_short", "repository_repo_ui")).all()
622
622
623 hg_ui = ret
623 hg_ui = ret
624 for ui_ in hg_ui:
624 for ui_ in hg_ui:
625 if ui_.ui_active:
625 if ui_.ui_active:
626 log.debug('settings ui from db[%s]%s:%s', ui_.ui_section,
626 log.debug('settings ui from db[%s]%s:%s', ui_.ui_section,
627 ui_.ui_key, ui_.ui_value)
627 ui_.ui_key, ui_.ui_value)
628 baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
628 baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
629
629
630 return baseui
630 return baseui
631
631
632 @classmethod
632 @classmethod
633 def is_valid(cls, repo_name):
633 def is_valid(cls, repo_name):
634 """
634 """
635 returns True if given repo name is a valid filesystem repository
635 returns True if given repo name is a valid filesystem repository
636
636
637 :param cls:
637 :param cls:
638 :param repo_name:
638 :param repo_name:
639 """
639 """
640 from rhodecode.lib.utils import is_valid_repo
640 from rhodecode.lib.utils import is_valid_repo
641
641
642 return is_valid_repo(repo_name, cls.base_path())
642 return is_valid_repo(repo_name, cls.base_path())
643
643
644 #==========================================================================
644 #==========================================================================
645 # SCM PROPERTIES
645 # SCM PROPERTIES
646 #==========================================================================
646 #==========================================================================
647
647
648 def get_changeset(self, rev):
648 def get_changeset(self, rev):
649 return get_changeset_safe(self.scm_instance, rev)
649 return get_changeset_safe(self.scm_instance, rev)
650
650
651 @property
651 @property
652 def tip(self):
652 def tip(self):
653 return self.get_changeset('tip')
653 return self.get_changeset('tip')
654
654
655 @property
655 @property
656 def author(self):
656 def author(self):
657 return self.tip.author
657 return self.tip.author
658
658
659 @property
659 @property
660 def last_change(self):
660 def last_change(self):
661 return self.scm_instance.last_change
661 return self.scm_instance.last_change
662
662
663 def comments(self, revisions=None):
663 def comments(self, revisions=None):
664 """
664 """
665 Returns comments for this repository grouped by revisions
665 Returns comments for this repository grouped by revisions
666
666
667 :param revisions: filter query by revisions only
667 :param revisions: filter query by revisions only
668 """
668 """
669 cmts = ChangesetComment.query()\
669 cmts = ChangesetComment.query()\
670 .filter(ChangesetComment.repo == self)
670 .filter(ChangesetComment.repo == self)
671 if revisions:
671 if revisions:
672 cmts = cmts.filter(ChangesetComment.revision.in_(revisions))
672 cmts = cmts.filter(ChangesetComment.revision.in_(revisions))
673 grouped = defaultdict(list)
673 grouped = defaultdict(list)
674 for cmt in cmts.all():
674 for cmt in cmts.all():
675 grouped[cmt.revision].append(cmt)
675 grouped[cmt.revision].append(cmt)
676 return grouped
676 return grouped
677
677
678 #==========================================================================
678 #==========================================================================
679 # SCM CACHE INSTANCE
679 # SCM CACHE INSTANCE
680 #==========================================================================
680 #==========================================================================
681
681
682 @property
682 @property
683 def invalidate(self):
683 def invalidate(self):
684 return CacheInvalidation.invalidate(self.repo_name)
684 return CacheInvalidation.invalidate(self.repo_name)
685
685
686 def set_invalidate(self):
686 def set_invalidate(self):
687 """
687 """
688 set a cache for invalidation for this instance
688 set a cache for invalidation for this instance
689 """
689 """
690 CacheInvalidation.set_invalidate(self.repo_name)
690 CacheInvalidation.set_invalidate(self.repo_name)
691
691
692 @LazyProperty
692 @LazyProperty
693 def scm_instance(self):
693 def scm_instance(self):
694 return self.__get_instance()
694 return self.__get_instance()
695
695
696 @property
696 @property
697 def scm_instance_cached(self):
697 def scm_instance_cached(self):
698 @cache_region('long_term')
698 @cache_region('long_term')
699 def _c(repo_name):
699 def _c(repo_name):
700 return self.__get_instance()
700 return self.__get_instance()
701 rn = self.repo_name
701 rn = self.repo_name
702 log.debug('Getting cached instance of repo')
702 log.debug('Getting cached instance of repo')
703 inv = self.invalidate
703 inv = self.invalidate
704 if inv is not None:
704 if inv is not None:
705 region_invalidate(_c, None, rn)
705 region_invalidate(_c, None, rn)
706 # update our cache
706 # update our cache
707 CacheInvalidation.set_valid(inv.cache_key)
707 CacheInvalidation.set_valid(inv.cache_key)
708 return _c(rn)
708 return _c(rn)
709
709
710 def __get_instance(self):
710 def __get_instance(self):
711 repo_full_path = self.repo_full_path
711 repo_full_path = self.repo_full_path
712 try:
712 try:
713 alias = get_scm(repo_full_path)[0]
713 alias = get_scm(repo_full_path)[0]
714 log.debug('Creating instance of %s repository' % alias)
714 log.debug('Creating instance of %s repository' % alias)
715 backend = get_backend(alias)
715 backend = get_backend(alias)
716 except VCSError:
716 except VCSError:
717 log.error(traceback.format_exc())
717 log.error(traceback.format_exc())
718 log.error('Perhaps this repository is in db and not in '
718 log.error('Perhaps this repository is in db and not in '
719 'filesystem run rescan repositories with '
719 'filesystem run rescan repositories with '
720 '"destroy old data " option from admin panel')
720 '"destroy old data " option from admin panel')
721 return
721 return
722
722
723 if alias == 'hg':
723 if alias == 'hg':
724
724
725 repo = backend(safe_str(repo_full_path), create=False,
725 repo = backend(safe_str(repo_full_path), create=False,
726 baseui=self._ui)
726 baseui=self._ui)
727 # skip hidden web repository
727 # skip hidden web repository
728 if repo._get_hidden():
728 if repo._get_hidden():
729 return
729 return
730 else:
730 else:
731 repo = backend(repo_full_path, create=False)
731 repo = backend(repo_full_path, create=False)
732
732
733 return repo
733 return repo
734
734
735
735
736 class RepoGroup(Base, BaseModel):
736 class RepoGroup(Base, BaseModel):
737 __tablename__ = 'groups'
737 __tablename__ = 'groups'
738 __table_args__ = (
738 __table_args__ = (
739 UniqueConstraint('group_name', 'group_parent_id'),
739 UniqueConstraint('group_name', 'group_parent_id'),
740 CheckConstraint('group_id != group_parent_id'),
740 CheckConstraint('group_id != group_parent_id'),
741 {'extend_existing': True, 'mysql_engine':'InnoDB',
741 {'extend_existing': True, 'mysql_engine':'InnoDB',
742 'mysql_charset': 'utf8'},
742 'mysql_charset': 'utf8'},
743 )
743 )
744 __mapper_args__ = {'order_by': 'group_name'}
744 __mapper_args__ = {'order_by': 'group_name'}
745
745
746 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
746 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
747 group_name = Column("group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
747 group_name = Column("group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
748 group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
748 group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
749 group_description = Column("group_description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
749 group_description = Column("group_description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
750
750
751 repo_group_to_perm = relationship('UserRepoGroupToPerm', cascade='all', order_by='UserRepoGroupToPerm.group_to_perm_id')
751 repo_group_to_perm = relationship('UserRepoGroupToPerm', cascade='all', order_by='UserRepoGroupToPerm.group_to_perm_id')
752 users_group_to_perm = relationship('UsersGroupRepoGroupToPerm', cascade='all')
752 users_group_to_perm = relationship('UsersGroupRepoGroupToPerm', cascade='all')
753
753
754 parent_group = relationship('RepoGroup', remote_side=group_id)
754 parent_group = relationship('RepoGroup', remote_side=group_id)
755
755
756 def __init__(self, group_name='', parent_group=None):
756 def __init__(self, group_name='', parent_group=None):
757 self.group_name = group_name
757 self.group_name = group_name
758 self.parent_group = parent_group
758 self.parent_group = parent_group
759
759
760 def __unicode__(self):
760 def __unicode__(self):
761 return u"<%s('%s:%s')>" % (self.__class__.__name__, self.group_id,
761 return u"<%s('%s:%s')>" % (self.__class__.__name__, self.group_id,
762 self.group_name)
762 self.group_name)
763
763
764 @classmethod
764 @classmethod
765 def groups_choices(cls):
765 def groups_choices(cls):
766 from webhelpers.html import literal as _literal
766 from webhelpers.html import literal as _literal
767 repo_groups = [('', '')]
767 repo_groups = [('', '')]
768 sep = ' &raquo; '
768 sep = ' &raquo; '
769 _name = lambda k: _literal(sep.join(k))
769 _name = lambda k: _literal(sep.join(k))
770
770
771 repo_groups.extend([(x.group_id, _name(x.full_path_splitted))
771 repo_groups.extend([(x.group_id, _name(x.full_path_splitted))
772 for x in cls.query().all()])
772 for x in cls.query().all()])
773
773
774 repo_groups = sorted(repo_groups, key=lambda t: t[1].split(sep)[0])
774 repo_groups = sorted(repo_groups, key=lambda t: t[1].split(sep)[0])
775 return repo_groups
775 return repo_groups
776
776
777 @classmethod
777 @classmethod
778 def url_sep(cls):
778 def url_sep(cls):
779 return '/'
779 return '/'
780
780
781 @classmethod
781 @classmethod
782 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
782 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
783 if case_insensitive:
783 if case_insensitive:
784 gr = cls.query()\
784 gr = cls.query()\
785 .filter(cls.group_name.ilike(group_name))
785 .filter(cls.group_name.ilike(group_name))
786 else:
786 else:
787 gr = cls.query()\
787 gr = cls.query()\
788 .filter(cls.group_name == group_name)
788 .filter(cls.group_name == group_name)
789 if cache:
789 if cache:
790 gr = gr.options(FromCache(
790 gr = gr.options(FromCache(
791 "sql_cache_short",
791 "sql_cache_short",
792 "get_group_%s" % _hash_key(group_name)
792 "get_group_%s" % _hash_key(group_name)
793 )
793 )
794 )
794 )
795 return gr.scalar()
795 return gr.scalar()
796
796
797 @property
797 @property
798 def parents(self):
798 def parents(self):
799 parents_recursion_limit = 5
799 parents_recursion_limit = 5
800 groups = []
800 groups = []
801 if self.parent_group is None:
801 if self.parent_group is None:
802 return groups
802 return groups
803 cur_gr = self.parent_group
803 cur_gr = self.parent_group
804 groups.insert(0, cur_gr)
804 groups.insert(0, cur_gr)
805 cnt = 0
805 cnt = 0
806 while 1:
806 while 1:
807 cnt += 1
807 cnt += 1
808 gr = getattr(cur_gr, 'parent_group', None)
808 gr = getattr(cur_gr, 'parent_group', None)
809 cur_gr = cur_gr.parent_group
809 cur_gr = cur_gr.parent_group
810 if gr is None:
810 if gr is None:
811 break
811 break
812 if cnt == parents_recursion_limit:
812 if cnt == parents_recursion_limit:
813 # this will prevent accidental infinit loops
813 # this will prevent accidental infinit loops
814 log.error('group nested more than %s' %
814 log.error('group nested more than %s' %
815 parents_recursion_limit)
815 parents_recursion_limit)
816 break
816 break
817
817
818 groups.insert(0, gr)
818 groups.insert(0, gr)
819 return groups
819 return groups
820
820
821 @property
821 @property
822 def children(self):
822 def children(self):
823 return RepoGroup.query().filter(RepoGroup.parent_group == self)
823 return RepoGroup.query().filter(RepoGroup.parent_group == self)
824
824
825 @property
825 @property
826 def name(self):
826 def name(self):
827 return self.group_name.split(RepoGroup.url_sep())[-1]
827 return self.group_name.split(RepoGroup.url_sep())[-1]
828
828
829 @property
829 @property
830 def full_path(self):
830 def full_path(self):
831 return self.group_name
831 return self.group_name
832
832
833 @property
833 @property
834 def full_path_splitted(self):
834 def full_path_splitted(self):
835 return self.group_name.split(RepoGroup.url_sep())
835 return self.group_name.split(RepoGroup.url_sep())
836
836
837 @property
837 @property
838 def repositories(self):
838 def repositories(self):
839 return Repository.query()\
839 return Repository.query()\
840 .filter(Repository.group == self)\
840 .filter(Repository.group == self)\
841 .order_by(Repository.repo_name)
841 .order_by(Repository.repo_name)
842
842
843 @property
843 @property
844 def repositories_recursive_count(self):
844 def repositories_recursive_count(self):
845 cnt = self.repositories.count()
845 cnt = self.repositories.count()
846
846
847 def children_count(group):
847 def children_count(group):
848 cnt = 0
848 cnt = 0
849 for child in group.children:
849 for child in group.children:
850 cnt += child.repositories.count()
850 cnt += child.repositories.count()
851 cnt += children_count(child)
851 cnt += children_count(child)
852 return cnt
852 return cnt
853
853
854 return cnt + children_count(self)
854 return cnt + children_count(self)
855
855
856 def get_new_name(self, group_name):
856 def get_new_name(self, group_name):
857 """
857 """
858 returns new full group name based on parent and new name
858 returns new full group name based on parent and new name
859
859
860 :param group_name:
860 :param group_name:
861 """
861 """
862 path_prefix = (self.parent_group.full_path_splitted if
862 path_prefix = (self.parent_group.full_path_splitted if
863 self.parent_group else [])
863 self.parent_group else [])
864 return RepoGroup.url_sep().join(path_prefix + [group_name])
864 return RepoGroup.url_sep().join(path_prefix + [group_name])
865
865
866
866
867 class Permission(Base, BaseModel):
867 class Permission(Base, BaseModel):
868 __tablename__ = 'permissions'
868 __tablename__ = 'permissions'
869 __table_args__ = (
869 __table_args__ = (
870 {'extend_existing': True, 'mysql_engine':'InnoDB',
870 {'extend_existing': True, 'mysql_engine':'InnoDB',
871 'mysql_charset': 'utf8'},
871 'mysql_charset': 'utf8'},
872 )
872 )
873 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
873 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
874 permission_name = Column("permission_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
874 permission_name = Column("permission_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
875 permission_longname = Column("permission_longname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
875 permission_longname = Column("permission_longname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
876
876
877 def __unicode__(self):
877 def __unicode__(self):
878 return u"<%s('%s:%s')>" % (
878 return u"<%s('%s:%s')>" % (
879 self.__class__.__name__, self.permission_id, self.permission_name
879 self.__class__.__name__, self.permission_id, self.permission_name
880 )
880 )
881
881
882 @classmethod
882 @classmethod
883 def get_by_key(cls, key):
883 def get_by_key(cls, key):
884 return cls.query().filter(cls.permission_name == key).scalar()
884 return cls.query().filter(cls.permission_name == key).scalar()
885
885
886 @classmethod
886 @classmethod
887 def get_default_perms(cls, default_user_id):
887 def get_default_perms(cls, default_user_id):
888 q = Session.query(UserRepoToPerm, Repository, cls)\
888 q = Session.query(UserRepoToPerm, Repository, cls)\
889 .join((Repository, UserRepoToPerm.repository_id == Repository.repo_id))\
889 .join((Repository, UserRepoToPerm.repository_id == Repository.repo_id))\
890 .join((cls, UserRepoToPerm.permission_id == cls.permission_id))\
890 .join((cls, UserRepoToPerm.permission_id == cls.permission_id))\
891 .filter(UserRepoToPerm.user_id == default_user_id)
891 .filter(UserRepoToPerm.user_id == default_user_id)
892
892
893 return q.all()
893 return q.all()
894
894
895 @classmethod
895 @classmethod
896 def get_default_group_perms(cls, default_user_id):
896 def get_default_group_perms(cls, default_user_id):
897 q = Session.query(UserRepoGroupToPerm, RepoGroup, cls)\
897 q = Session.query(UserRepoGroupToPerm, RepoGroup, cls)\
898 .join((RepoGroup, UserRepoGroupToPerm.group_id == RepoGroup.group_id))\
898 .join((RepoGroup, UserRepoGroupToPerm.group_id == RepoGroup.group_id))\
899 .join((cls, UserRepoGroupToPerm.permission_id == cls.permission_id))\
899 .join((cls, UserRepoGroupToPerm.permission_id == cls.permission_id))\
900 .filter(UserRepoGroupToPerm.user_id == default_user_id)
900 .filter(UserRepoGroupToPerm.user_id == default_user_id)
901
901
902 return q.all()
902 return q.all()
903
903
904
904
905 class UserRepoToPerm(Base, BaseModel):
905 class UserRepoToPerm(Base, BaseModel):
906 __tablename__ = 'repo_to_perm'
906 __tablename__ = 'repo_to_perm'
907 __table_args__ = (
907 __table_args__ = (
908 UniqueConstraint('user_id', 'repository_id', 'permission_id'),
908 UniqueConstraint('user_id', 'repository_id', 'permission_id'),
909 {'extend_existing': True, 'mysql_engine':'InnoDB',
909 {'extend_existing': True, 'mysql_engine':'InnoDB',
910 'mysql_charset': 'utf8'}
910 'mysql_charset': 'utf8'}
911 )
911 )
912 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
912 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
913 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
913 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
914 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
914 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
915 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
915 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
916
916
917 user = relationship('User')
917 user = relationship('User')
918 repository = relationship('Repository')
918 repository = relationship('Repository')
919 permission = relationship('Permission')
919 permission = relationship('Permission')
920
920
921 @classmethod
921 @classmethod
922 def create(cls, user, repository, permission):
922 def create(cls, user, repository, permission):
923 n = cls()
923 n = cls()
924 n.user = user
924 n.user = user
925 n.repository = repository
925 n.repository = repository
926 n.permission = permission
926 n.permission = permission
927 Session.add(n)
927 Session.add(n)
928 return n
928 return n
929
929
930 def __unicode__(self):
930 def __unicode__(self):
931 return u'<user:%s => %s >' % (self.user, self.repository)
931 return u'<user:%s => %s >' % (self.user, self.repository)
932
932
933
933
934 class UserToPerm(Base, BaseModel):
934 class UserToPerm(Base, BaseModel):
935 __tablename__ = 'user_to_perm'
935 __tablename__ = 'user_to_perm'
936 __table_args__ = (
936 __table_args__ = (
937 UniqueConstraint('user_id', 'permission_id'),
937 UniqueConstraint('user_id', 'permission_id'),
938 {'extend_existing': True, 'mysql_engine':'InnoDB',
938 {'extend_existing': True, 'mysql_engine':'InnoDB',
939 'mysql_charset': 'utf8'}
939 'mysql_charset': 'utf8'}
940 )
940 )
941 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
941 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
942 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
942 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
943 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
943 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
944
944
945 user = relationship('User')
945 user = relationship('User')
946 permission = relationship('Permission', lazy='joined')
946 permission = relationship('Permission', lazy='joined')
947
947
948
948
949 class UsersGroupRepoToPerm(Base, BaseModel):
949 class UsersGroupRepoToPerm(Base, BaseModel):
950 __tablename__ = 'users_group_repo_to_perm'
950 __tablename__ = 'users_group_repo_to_perm'
951 __table_args__ = (
951 __table_args__ = (
952 UniqueConstraint('repository_id', 'users_group_id', 'permission_id'),
952 UniqueConstraint('repository_id', 'users_group_id', 'permission_id'),
953 {'extend_existing': True, 'mysql_engine':'InnoDB',
953 {'extend_existing': True, 'mysql_engine':'InnoDB',
954 'mysql_charset': 'utf8'}
954 'mysql_charset': 'utf8'}
955 )
955 )
956 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
956 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
957 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
957 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
958 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
958 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
959 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
959 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
960
960
961 users_group = relationship('UsersGroup')
961 users_group = relationship('UsersGroup')
962 permission = relationship('Permission')
962 permission = relationship('Permission')
963 repository = relationship('Repository')
963 repository = relationship('Repository')
964
964
965 @classmethod
965 @classmethod
966 def create(cls, users_group, repository, permission):
966 def create(cls, users_group, repository, permission):
967 n = cls()
967 n = cls()
968 n.users_group = users_group
968 n.users_group = users_group
969 n.repository = repository
969 n.repository = repository
970 n.permission = permission
970 n.permission = permission
971 Session.add(n)
971 Session.add(n)
972 return n
972 return n
973
973
974 def __unicode__(self):
974 def __unicode__(self):
975 return u'<userGroup:%s => %s >' % (self.users_group, self.repository)
975 return u'<userGroup:%s => %s >' % (self.users_group, self.repository)
976
976
977
977
978 class UsersGroupToPerm(Base, BaseModel):
978 class UsersGroupToPerm(Base, BaseModel):
979 __tablename__ = 'users_group_to_perm'
979 __tablename__ = 'users_group_to_perm'
980 __table_args__ = (
980 __table_args__ = (
981 UniqueConstraint('users_group_id', 'permission_id',),
981 UniqueConstraint('users_group_id', 'permission_id',),
982 {'extend_existing': True, 'mysql_engine':'InnoDB',
982 {'extend_existing': True, 'mysql_engine':'InnoDB',
983 'mysql_charset': 'utf8'}
983 'mysql_charset': 'utf8'}
984 )
984 )
985 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
985 users_group_to_perm_id = Column("users_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
988
989 users_group = relationship('UsersGroup')
989 users_group = relationship('UsersGroup')
990 permission = relationship('Permission')
990 permission = relationship('Permission')
991
991
992
992
993 class UserRepoGroupToPerm(Base, BaseModel):
993 class UserRepoGroupToPerm(Base, BaseModel):
994 __tablename__ = 'user_repo_group_to_perm'
994 __tablename__ = 'user_repo_group_to_perm'
995 __table_args__ = (
995 __table_args__ = (
996 UniqueConstraint('user_id', 'group_id', 'permission_id'),
996 UniqueConstraint('user_id', 'group_id', 'permission_id'),
997 {'extend_existing': True, 'mysql_engine':'InnoDB',
997 {'extend_existing': True, 'mysql_engine':'InnoDB',
998 'mysql_charset': 'utf8'}
998 'mysql_charset': 'utf8'}
999 )
999 )
1000
1000
1001 group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1001 group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1002 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1002 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1003 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
1003 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
1004 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1004 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1005
1005
1006 user = relationship('User')
1006 user = relationship('User')
1007 group = relationship('RepoGroup')
1007 group = relationship('RepoGroup')
1008 permission = relationship('Permission')
1008 permission = relationship('Permission')
1009
1009
1010
1010
1011 class UsersGroupRepoGroupToPerm(Base, BaseModel):
1011 class UsersGroupRepoGroupToPerm(Base, BaseModel):
1012 __tablename__ = 'users_group_repo_group_to_perm'
1012 __tablename__ = 'users_group_repo_group_to_perm'
1013 __table_args__ = (
1013 __table_args__ = (
1014 UniqueConstraint('users_group_id', 'group_id'),
1014 UniqueConstraint('users_group_id', 'group_id'),
1015 {'extend_existing': True, 'mysql_engine':'InnoDB',
1015 {'extend_existing': True, 'mysql_engine':'InnoDB',
1016 'mysql_charset': 'utf8'}
1016 'mysql_charset': 'utf8'}
1017 )
1017 )
1018
1018
1019 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)
1019 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)
1020 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1020 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1021 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
1021 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
1022 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1022 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1023
1023
1024 users_group = relationship('UsersGroup')
1024 users_group = relationship('UsersGroup')
1025 permission = relationship('Permission')
1025 permission = relationship('Permission')
1026 group = relationship('RepoGroup')
1026 group = relationship('RepoGroup')
1027
1027
1028
1028
1029 class Statistics(Base, BaseModel):
1029 class Statistics(Base, BaseModel):
1030 __tablename__ = 'statistics'
1030 __tablename__ = 'statistics'
1031 __table_args__ = (
1031 __table_args__ = (
1032 UniqueConstraint('repository_id'),
1032 UniqueConstraint('repository_id'),
1033 {'extend_existing': True, 'mysql_engine':'InnoDB',
1033 {'extend_existing': True, 'mysql_engine':'InnoDB',
1034 'mysql_charset': 'utf8'}
1034 'mysql_charset': 'utf8'}
1035 )
1035 )
1036 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1036 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1037 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
1037 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
1038 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
1038 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
1039 commit_activity = Column("commit_activity", LargeBinary(1000000), nullable=False)#JSON data
1039 commit_activity = Column("commit_activity", LargeBinary(1000000), nullable=False)#JSON data
1040 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
1040 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
1041 languages = Column("languages", LargeBinary(1000000), nullable=False)#JSON data
1041 languages = Column("languages", LargeBinary(1000000), nullable=False)#JSON data
1042
1042
1043 repository = relationship('Repository', single_parent=True)
1043 repository = relationship('Repository', single_parent=True)
1044
1044
1045
1045
1046 class UserFollowing(Base, BaseModel):
1046 class UserFollowing(Base, BaseModel):
1047 __tablename__ = 'user_followings'
1047 __tablename__ = 'user_followings'
1048 __table_args__ = (
1048 __table_args__ = (
1049 UniqueConstraint('user_id', 'follows_repository_id'),
1049 UniqueConstraint('user_id', 'follows_repository_id'),
1050 UniqueConstraint('user_id', 'follows_user_id'),
1050 UniqueConstraint('user_id', 'follows_user_id'),
1051 {'extend_existing': True, 'mysql_engine':'InnoDB',
1051 {'extend_existing': True, 'mysql_engine':'InnoDB',
1052 'mysql_charset': 'utf8'}
1052 'mysql_charset': 'utf8'}
1053 )
1053 )
1054
1054
1055 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1055 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1056 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1056 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1057 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
1057 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
1058 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
1058 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
1059 follows_from = Column('follows_from', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
1059 follows_from = Column('follows_from', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
1060
1060
1061 user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id')
1061 user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id')
1062
1062
1063 follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
1063 follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
1064 follows_repository = relationship('Repository', order_by='Repository.repo_name')
1064 follows_repository = relationship('Repository', order_by='Repository.repo_name')
1065
1065
1066 @classmethod
1066 @classmethod
1067 def get_repo_followers(cls, repo_id):
1067 def get_repo_followers(cls, repo_id):
1068 return cls.query().filter(cls.follows_repo_id == repo_id)
1068 return cls.query().filter(cls.follows_repo_id == repo_id)
1069
1069
1070
1070
1071 class CacheInvalidation(Base, BaseModel):
1071 class CacheInvalidation(Base, BaseModel):
1072 __tablename__ = 'cache_invalidation'
1072 __tablename__ = 'cache_invalidation'
1073 __table_args__ = (
1073 __table_args__ = (
1074 UniqueConstraint('cache_key'),
1074 UniqueConstraint('cache_key'),
1075 {'extend_existing': True, 'mysql_engine':'InnoDB',
1075 {'extend_existing': True, 'mysql_engine':'InnoDB',
1076 'mysql_charset': 'utf8'},
1076 'mysql_charset': 'utf8'},
1077 )
1077 )
1078 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1078 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1079 cache_key = Column("cache_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1079 cache_key = Column("cache_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1080 cache_args = Column("cache_args", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1080 cache_args = Column("cache_args", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1081 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
1081 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
1082
1082
1083 def __init__(self, cache_key, cache_args=''):
1083 def __init__(self, cache_key, cache_args=''):
1084 self.cache_key = cache_key
1084 self.cache_key = cache_key
1085 self.cache_args = cache_args
1085 self.cache_args = cache_args
1086 self.cache_active = False
1086 self.cache_active = False
1087
1087
1088 def __unicode__(self):
1088 def __unicode__(self):
1089 return u"<%s('%s:%s')>" % (self.__class__.__name__,
1089 return u"<%s('%s:%s')>" % (self.__class__.__name__,
1090 self.cache_id, self.cache_key)
1090 self.cache_id, self.cache_key)
1091 @classmethod
1091 @classmethod
1092 def clear_cache(cls):
1092 def clear_cache(cls):
1093 cls.query().delete()
1093 cls.query().delete()
1094
1094
1095 @classmethod
1095 @classmethod
1096 def _get_key(cls, key):
1096 def _get_key(cls, key):
1097 """
1097 """
1098 Wrapper for generating a key, together with a prefix
1098 Wrapper for generating a key, together with a prefix
1099
1099
1100 :param key:
1100 :param key:
1101 """
1101 """
1102 import rhodecode
1102 import rhodecode
1103 prefix = ''
1103 prefix = ''
1104 iid = rhodecode.CONFIG.get('instance_id')
1104 iid = rhodecode.CONFIG.get('instance_id')
1105 if iid:
1105 if iid:
1106 prefix = iid
1106 prefix = iid
1107 return "%s%s" % (prefix, key), prefix, key.rstrip('_README')
1107 return "%s%s" % (prefix, key), prefix, key.rstrip('_README')
1108
1108
1109 @classmethod
1109 @classmethod
1110 def get_by_key(cls, key):
1110 def get_by_key(cls, key):
1111 return cls.query().filter(cls.cache_key == key).scalar()
1111 return cls.query().filter(cls.cache_key == key).scalar()
1112
1112
1113 @classmethod
1113 @classmethod
1114 def _get_or_create_key(cls, key, prefix, org_key):
1114 def _get_or_create_key(cls, key, prefix, org_key):
1115 inv_obj = Session.query(cls).filter(cls.cache_key == key).scalar()
1115 inv_obj = Session.query(cls).filter(cls.cache_key == key).scalar()
1116 if not inv_obj:
1116 if not inv_obj:
1117 try:
1117 try:
1118 inv_obj = CacheInvalidation(key, org_key)
1118 inv_obj = CacheInvalidation(key, org_key)
1119 Session.add(inv_obj)
1119 Session.add(inv_obj)
1120 Session.commit()
1120 Session.commit()
1121 except Exception:
1121 except Exception:
1122 log.error(traceback.format_exc())
1122 log.error(traceback.format_exc())
1123 Session.rollback()
1123 Session.rollback()
1124 return inv_obj
1124 return inv_obj
1125
1125
1126 @classmethod
1126 @classmethod
1127 def invalidate(cls, key):
1127 def invalidate(cls, key):
1128 """
1128 """
1129 Returns Invalidation object if this given key should be invalidated
1129 Returns Invalidation object if this given key should be invalidated
1130 None otherwise. `cache_active = False` means that this cache
1130 None otherwise. `cache_active = False` means that this cache
1131 state is not valid and needs to be invalidated
1131 state is not valid and needs to be invalidated
1132
1132
1133 :param key:
1133 :param key:
1134 """
1134 """
1135
1135
1136 key, _prefix, _org_key = cls._get_key(key)
1136 key, _prefix, _org_key = cls._get_key(key)
1137 inv = cls._get_or_create_key(key, _prefix, _org_key)
1137 inv = cls._get_or_create_key(key, _prefix, _org_key)
1138
1138
1139 if inv and inv.cache_active is False:
1139 if inv and inv.cache_active is False:
1140 return inv
1140 return inv
1141
1141
1142 @classmethod
1142 @classmethod
1143 def set_invalidate(cls, key):
1143 def set_invalidate(cls, key):
1144 """
1144 """
1145 Mark this Cache key for invalidation
1145 Mark this Cache key for invalidation
1146
1146
1147 :param key:
1147 :param key:
1148 """
1148 """
1149
1149
1150 key, _prefix, _org_key = cls._get_key(key)
1150 key, _prefix, _org_key = cls._get_key(key)
1151 inv_objs = Session.query(cls).filter(cls.cache_args == _org_key).all()
1151 inv_objs = Session.query(cls).filter(cls.cache_args == _org_key).all()
1152 log.debug('marking %s key[s] %s for invalidation' % (len(inv_objs),
1152 log.debug('marking %s key[s] %s for invalidation' % (len(inv_objs),
1153 _org_key))
1153 _org_key))
1154 try:
1154 try:
1155 for inv_obj in inv_objs:
1155 for inv_obj in inv_objs:
1156 if inv_obj:
1156 if inv_obj:
1157 inv_obj.cache_active = False
1157 inv_obj.cache_active = False
1158
1158
1159 Session.add(inv_obj)
1159 Session.add(inv_obj)
1160 Session.commit()
1160 Session.commit()
1161 except Exception:
1161 except Exception:
1162 log.error(traceback.format_exc())
1162 log.error(traceback.format_exc())
1163 Session.rollback()
1163 Session.rollback()
1164
1164
1165 @classmethod
1165 @classmethod
1166 def set_valid(cls, key):
1166 def set_valid(cls, key):
1167 """
1167 """
1168 Mark this cache key as active and currently cached
1168 Mark this cache key as active and currently cached
1169
1169
1170 :param key:
1170 :param key:
1171 """
1171 """
1172 inv_obj = cls.get_by_key(key)
1172 inv_obj = cls.get_by_key(key)
1173 inv_obj.cache_active = True
1173 inv_obj.cache_active = True
1174 Session.add(inv_obj)
1174 Session.add(inv_obj)
1175 Session.commit()
1175 Session.commit()
1176
1176
1177
1177
1178 class ChangesetComment(Base, BaseModel):
1178 class ChangesetComment(Base, BaseModel):
1179 __tablename__ = 'changeset_comments'
1179 __tablename__ = 'changeset_comments'
1180 __table_args__ = (
1180 __table_args__ = (
1181 {'extend_existing': True, 'mysql_engine':'InnoDB',
1181 {'extend_existing': True, 'mysql_engine':'InnoDB',
1182 'mysql_charset': 'utf8'},
1182 'mysql_charset': 'utf8'},
1183 )
1183 )
1184 comment_id = Column('comment_id', Integer(), nullable=False, primary_key=True)
1184 comment_id = Column('comment_id', Integer(), nullable=False, primary_key=True)
1185 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1185 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1186 revision = Column('revision', String(40), nullable=False)
1186 revision = Column('revision', String(40), nullable=False)
1187 line_no = Column('line_no', Unicode(10), nullable=True)
1187 line_no = Column('line_no', Unicode(10), nullable=True)
1188 f_path = Column('f_path', Unicode(1000), nullable=True)
1188 f_path = Column('f_path', Unicode(1000), nullable=True)
1189 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=False)
1189 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=False)
1190 text = Column('text', Unicode(25000), nullable=False)
1190 text = Column('text', Unicode(25000), nullable=False)
1191 modified_at = Column('modified_at', DateTime(), nullable=False, default=datetime.datetime.now)
1191 modified_at = Column('modified_at', DateTime(), nullable=False, default=datetime.datetime.now)
1192
1192
1193 author = relationship('User', lazy='joined')
1193 author = relationship('User', lazy='joined')
1194 repo = relationship('Repository')
1194 repo = relationship('Repository')
1195
1195
1196 @classmethod
1196 @classmethod
1197 def get_users(cls, revision):
1197 def get_users(cls, revision):
1198 """
1198 """
1199 Returns user associated with this changesetComment. ie those
1199 Returns user associated with this changesetComment. ie those
1200 who actually commented
1200 who actually commented
1201
1201
1202 :param cls:
1202 :param cls:
1203 :param revision:
1203 :param revision:
1204 """
1204 """
1205 return Session.query(User)\
1205 return Session.query(User)\
1206 .filter(cls.revision == revision)\
1206 .filter(cls.revision == revision)\
1207 .join(ChangesetComment.author).all()
1207 .join(ChangesetComment.author).all()
1208
1208
1209
1209
1210 class Notification(Base, BaseModel):
1210 class Notification(Base, BaseModel):
1211 __tablename__ = 'notifications'
1211 __tablename__ = 'notifications'
1212 __table_args__ = (
1212 __table_args__ = (
1213 {'extend_existing': True, 'mysql_engine':'InnoDB',
1213 {'extend_existing': True, 'mysql_engine':'InnoDB',
1214 'mysql_charset': 'utf8'},
1214 'mysql_charset': 'utf8'},
1215 )
1215 )
1216
1216
1217 TYPE_CHANGESET_COMMENT = u'cs_comment'
1217 TYPE_CHANGESET_COMMENT = u'cs_comment'
1218 TYPE_MESSAGE = u'message'
1218 TYPE_MESSAGE = u'message'
1219 TYPE_MENTION = u'mention'
1219 TYPE_MENTION = u'mention'
1220 TYPE_REGISTRATION = u'registration'
1220 TYPE_REGISTRATION = u'registration'
1221
1221
1222 notification_id = Column('notification_id', Integer(), nullable=False, primary_key=True)
1222 notification_id = Column('notification_id', Integer(), nullable=False, primary_key=True)
1223 subject = Column('subject', Unicode(512), nullable=True)
1223 subject = Column('subject', Unicode(512), nullable=True)
1224 body = Column('body', Unicode(50000), nullable=True)
1224 body = Column('body', Unicode(50000), nullable=True)
1225 created_by = Column("created_by", Integer(), ForeignKey('users.user_id'), nullable=True)
1225 created_by = Column("created_by", Integer(), ForeignKey('users.user_id'), nullable=True)
1226 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1226 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1227 type_ = Column('type', Unicode(256))
1227 type_ = Column('type', Unicode(256))
1228
1228
1229 created_by_user = relationship('User')
1229 created_by_user = relationship('User')
1230 notifications_to_users = relationship('UserNotification', lazy='joined',
1230 notifications_to_users = relationship('UserNotification', lazy='joined',
1231 cascade="all, delete, delete-orphan")
1231 cascade="all, delete, delete-orphan")
1232
1232
1233 @property
1233 @property
1234 def recipients(self):
1234 def recipients(self):
1235 return [x.user for x in UserNotification.query()\
1235 return [x.user for x in UserNotification.query()\
1236 .filter(UserNotification.notification == self).all()]
1236 .filter(UserNotification.notification == self).all()]
1237
1237
1238 @classmethod
1238 @classmethod
1239 def create(cls, created_by, subject, body, recipients, type_=None):
1239 def create(cls, created_by, subject, body, recipients, type_=None):
1240 if type_ is None:
1240 if type_ is None:
1241 type_ = Notification.TYPE_MESSAGE
1241 type_ = Notification.TYPE_MESSAGE
1242
1242
1243 notification = cls()
1243 notification = cls()
1244 notification.created_by_user = created_by
1244 notification.created_by_user = created_by
1245 notification.subject = subject
1245 notification.subject = subject
1246 notification.body = body
1246 notification.body = body
1247 notification.type_ = type_
1247 notification.type_ = type_
1248 notification.created_on = datetime.datetime.now()
1248 notification.created_on = datetime.datetime.now()
1249
1249
1250 for u in recipients:
1250 for u in recipients:
1251 assoc = UserNotification()
1251 assoc = UserNotification()
1252 assoc.notification = notification
1252 assoc.notification = notification
1253 u.notifications.append(assoc)
1253 u.notifications.append(assoc)
1254 Session.add(notification)
1254 Session.add(notification)
1255 return notification
1255 return notification
1256
1256
1257 @property
1257 @property
1258 def description(self):
1258 def description(self):
1259 from rhodecode.model.notification import NotificationModel
1259 from rhodecode.model.notification import NotificationModel
1260 return NotificationModel().make_description(self)
1260 return NotificationModel().make_description(self)
1261
1261
1262
1262
1263 class UserNotification(Base, BaseModel):
1263 class UserNotification(Base, BaseModel):
1264 __tablename__ = 'user_to_notification'
1264 __tablename__ = 'user_to_notification'
1265 __table_args__ = (
1265 __table_args__ = (
1266 UniqueConstraint('user_id', 'notification_id'),
1266 UniqueConstraint('user_id', 'notification_id'),
1267 {'extend_existing': True, 'mysql_engine':'InnoDB',
1267 {'extend_existing': True, 'mysql_engine':'InnoDB',
1268 'mysql_charset': 'utf8'}
1268 'mysql_charset': 'utf8'}
1269 )
1269 )
1270 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), primary_key=True)
1270 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), primary_key=True)
1271 notification_id = Column("notification_id", Integer(), ForeignKey('notifications.notification_id'), primary_key=True)
1271 notification_id = Column("notification_id", Integer(), ForeignKey('notifications.notification_id'), primary_key=True)
1272 read = Column('read', Boolean, default=False)
1272 read = Column('read', Boolean, default=False)
1273 sent_on = Column('sent_on', DateTime(timezone=False), nullable=True, unique=None)
1273 sent_on = Column('sent_on', DateTime(timezone=False), nullable=True, unique=None)
1274
1274
1275 user = relationship('User', lazy="joined")
1275 user = relationship('User', lazy="joined")
1276 notification = relationship('Notification', lazy="joined",
1276 notification = relationship('Notification', lazy="joined",
1277 order_by=lambda: Notification.created_on.desc(),)
1277 order_by=lambda: Notification.created_on.desc(),)
1278
1278
1279 def mark_as_read(self):
1279 def mark_as_read(self):
1280 self.read = True
1280 self.read = True
1281 Session.add(self)
1281 Session.add(self)
1282
1282
1283
1283
1284 class DbMigrateVersion(Base, BaseModel):
1284 class DbMigrateVersion(Base, BaseModel):
1285 __tablename__ = 'db_migrate_version'
1285 __tablename__ = 'db_migrate_version'
1286 __table_args__ = (
1286 __table_args__ = (
1287 {'extend_existing': True, 'mysql_engine':'InnoDB',
1287 {'extend_existing': True, 'mysql_engine':'InnoDB',
1288 'mysql_charset': 'utf8'},
1288 'mysql_charset': 'utf8'},
1289 )
1289 )
1290 repository_id = Column('repository_id', String(250), primary_key=True)
1290 repository_id = Column('repository_id', String(250), primary_key=True)
1291 repository_path = Column('repository_path', Text)
1291 repository_path = Column('repository_path', Text)
1292 version = Column('version', Integer)
1292 version = Column('version', Integer)
1293
1293
1294 ## this is migration from 1_4_0, but now it's here to overcome a problem of
1294 ## this is migration from 1_4_0, but now it's here to overcome a problem of
1295 ## attaching a FK to this from 1_3_0 !
1295 ## attaching a FK to this from 1_3_0 !
1296
1296
1297
1297
1298 class PullRequest(Base, BaseModel):
1298 class PullRequest(Base, BaseModel):
1299 __tablename__ = 'pull_requests'
1299 __tablename__ = 'pull_requests'
1300 __table_args__ = (
1300 __table_args__ = (
1301 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1301 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1302 'mysql_charset': 'utf8'},
1302 'mysql_charset': 'utf8'},
1303 )
1303 )
1304
1304
1305 STATUS_NEW = u'new'
1305 STATUS_NEW = u'new'
1306 STATUS_OPEN = u'open'
1306 STATUS_OPEN = u'open'
1307 STATUS_CLOSED = u'closed'
1307 STATUS_CLOSED = u'closed'
1308
1308
1309 pull_request_id = Column('pull_request_id', Integer(), nullable=False, primary_key=True)
1309 pull_request_id = Column('pull_request_id', Integer(), nullable=False, primary_key=True)
1310 title = Column('title', Unicode(256), nullable=True)
1310 title = Column('title', Unicode(256), nullable=True)
1311 description = Column('description', UnicodeText(10240), nullable=True)
1311 description = Column('description', UnicodeText(10240), nullable=True)
1312 status = Column('status', Unicode(256), nullable=False, default=STATUS_NEW)
1312 status = Column('status', Unicode(256), nullable=False, default=STATUS_NEW)
1313 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1313 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1314 updated_on = Column('updated_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1314 updated_on = Column('updated_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1315 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
1315 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
1316 _revisions = Column('revisions', UnicodeText(20500)) # 500 revisions max
1316 _revisions = Column('revisions', UnicodeText(20500)) # 500 revisions max
1317 org_repo_id = Column('org_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1317 org_repo_id = Column('org_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1318 org_ref = Column('org_ref', Unicode(256), nullable=False)
1318 org_ref = Column('org_ref', Unicode(256), nullable=False)
1319 other_repo_id = Column('other_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1319 other_repo_id = Column('other_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1320 other_ref = Column('other_ref', Unicode(256), nullable=False) No newline at end of file
1320 other_ref = Column('other_ref', Unicode(256), nullable=False)
@@ -1,186 +1,186 b''
1 import logging
1 import logging
2 import datetime
2 import datetime
3
3
4 from sqlalchemy import *
4 from sqlalchemy import *
5 from sqlalchemy.exc import DatabaseError
5 from sqlalchemy.exc import DatabaseError
6 from sqlalchemy.orm import relation, backref, class_mapper
6 from sqlalchemy.orm import relation, backref, class_mapper
7 from sqlalchemy.orm.session import Session
7 from sqlalchemy.orm.session import Session
8 from sqlalchemy.ext.declarative import declarative_base
8 from sqlalchemy.ext.declarative import declarative_base
9
9
10 from rhodecode.lib.dbmigrate.migrate import *
10 from rhodecode.lib.dbmigrate.migrate import *
11 from rhodecode.lib.dbmigrate.migrate.changeset import *
11 from rhodecode.lib.dbmigrate.migrate.changeset import *
12
12
13 from rhodecode.model.meta import Base
13 from rhodecode.model.meta import Base
14 from rhodecode.model import meta
14 from rhodecode.model import meta
15
15
16 log = logging.getLogger(__name__)
16 log = logging.getLogger(__name__)
17
17
18
18
19 def upgrade(migrate_engine):
19 def upgrade(migrate_engine):
20 """
20 """
21 Upgrade operations go here.
21 Upgrade operations go here.
22 Don't create your own engine; bind migrate_engine to your metadata
22 Don't create your own engine; bind migrate_engine to your metadata
23 """
23 """
24
24
25 #==========================================================================
25 #==========================================================================
26 # USEREMAILMAP
26 # USEREMAILMAP
27 #==========================================================================
27 #==========================================================================
28 from rhodecode.lib.dbmigrate.schema.db_1_4_0 import UserEmailMap
28 from rhodecode.lib.dbmigrate.schema.db_1_4_0 import UserEmailMap
29 tbl = UserEmailMap.__table__
29 tbl = UserEmailMap.__table__
30 tbl.create()
30 tbl.create()
31 #==========================================================================
31 #==========================================================================
32 # PULL REQUEST
32 # PULL REQUEST
33 #==========================================================================
33 #==========================================================================
34 from rhodecode.lib.dbmigrate.schema.db_1_4_0 import PullRequest
34 from rhodecode.lib.dbmigrate.schema.db_1_4_0 import PullRequest
35 tbl = PullRequest.__table__
35 tbl = PullRequest.__table__
36 tbl.create()
36 tbl.create()
37
37
38 #==========================================================================
38 #==========================================================================
39 # PULL REQUEST REVIEWERS
39 # PULL REQUEST REVIEWERS
40 #==========================================================================
40 #==========================================================================
41 from rhodecode.lib.dbmigrate.schema.db_1_4_0 import PullRequestReviewers
41 from rhodecode.lib.dbmigrate.schema.db_1_4_0 import PullRequestReviewers
42 tbl = PullRequestReviewers.__table__
42 tbl = PullRequestReviewers.__table__
43 tbl.create()
43 tbl.create()
44
44
45 #==========================================================================
45 #==========================================================================
46 # CHANGESET STATUS
46 # CHANGESET STATUS
47 #==========================================================================
47 #==========================================================================
48 from rhodecode.lib.dbmigrate.schema.db_1_4_0 import ChangesetStatus
48 from rhodecode.lib.dbmigrate.schema.db_1_4_0 import ChangesetStatus
49 tbl = ChangesetStatus.__table__
49 tbl = ChangesetStatus.__table__
50 tbl.create()
50 tbl.create()
51
51
52 ## RESET COMPLETLY THE metadata for sqlalchemy to use the 1_3_0 Base
52 ## RESET COMPLETLY THE metadata for sqlalchemy to use the 1_3_0 Base
53 Base = declarative_base()
53 Base = declarative_base()
54 Base.metadata.clear()
54 Base.metadata.clear()
55 Base.metadata = MetaData()
55 Base.metadata = MetaData()
56 Base.metadata.bind = migrate_engine
56 Base.metadata.bind = migrate_engine
57 meta.Base = Base
57 meta.Base = Base
58
58
59 #==========================================================================
59 #==========================================================================
60 # USERS TABLE
60 # USERS TABLE
61 #==========================================================================
61 #==========================================================================
62 from rhodecode.lib.dbmigrate.schema.db_1_3_0 import User
62 from rhodecode.lib.dbmigrate.schema.db_1_3_0 import User
63 tbl = User.__table__
63 tbl = User.__table__
64
64
65 # change column name -> firstname
65 # change column name -> firstname
66 col = User.__table__.columns.name
66 col = User.__table__.columns.name
67 col.alter(index=Index('u_username_idx', 'username'))
67 col.alter(index=Index('u_username_idx', 'username'))
68 col.alter(index=Index('u_email_idx', 'email'))
68 col.alter(index=Index('u_email_idx', 'email'))
69 col.alter(name="firstname", table=tbl)
69 col.alter(name="firstname", table=tbl)
70
70
71 # add inherit_default_permission column
71 # add inherit_default_permission column
72 inherit_default_permissions = Column("inherit_default_permissions",
72 inherit_default_permissions = Column("inherit_default_permissions",
73 Boolean(), nullable=True, unique=None,
73 Boolean(), nullable=True, unique=None,
74 default=True)
74 default=True)
75 inherit_default_permissions.create(table=tbl)
75 inherit_default_permissions.create(table=tbl)
76 inherit_default_permissions.alter(nullable=False, default=True, table=tbl)
76 inherit_default_permissions.alter(nullable=False, default=True, table=tbl)
77
77
78 #==========================================================================
78 #==========================================================================
79 # USERS GROUP TABLE
79 # USERS GROUP TABLE
80 #==========================================================================
80 #==========================================================================
81 from rhodecode.lib.dbmigrate.schema.db_1_3_0 import UsersGroup
81 from rhodecode.lib.dbmigrate.schema.db_1_3_0 import UsersGroup
82 tbl = UsersGroup.__table__
82 tbl = UsersGroup.__table__
83 # add inherit_default_permission column
83 # add inherit_default_permission column
84 gr_inherit_default_permissions = Column(
84 gr_inherit_default_permissions = Column(
85 "users_group_inherit_default_permissions",
85 "users_group_inherit_default_permissions",
86 Boolean(), nullable=True, unique=None,
86 Boolean(), nullable=True, unique=None,
87 default=True)
87 default=True)
88 gr_inherit_default_permissions.create(table=tbl)
88 gr_inherit_default_permissions.create(table=tbl)
89 gr_inherit_default_permissions.alter(nullable=False, default=True, table=tbl)
89 gr_inherit_default_permissions.alter(nullable=False, default=True, table=tbl)
90
90
91 #==========================================================================
91 #==========================================================================
92 # REPOSITORIES
92 # REPOSITORIES
93 #==========================================================================
93 #==========================================================================
94 from rhodecode.lib.dbmigrate.schema.db_1_3_0 import Repository
94 from rhodecode.lib.dbmigrate.schema.db_1_3_0 import Repository
95 tbl = Repository.__table__
95 tbl = Repository.__table__
96
96
97 # add enable locking column
97 # add enable locking column
98 enable_locking = Column("enable_locking", Boolean(), nullable=True,
98 enable_locking = Column("enable_locking", Boolean(), nullable=True,
99 unique=None, default=False)
99 unique=None, default=False)
100 enable_locking.create(table=tbl)
100 enable_locking.create(table=tbl)
101 enable_locking.alter(nullable=False, default=False, table=tbl)
101 enable_locking.alter(nullable=False, default=False, table=tbl)
102
102
103 # add locked column
103 # add locked column
104 _locked = Column("locked", String(255), nullable=True, unique=False,
104 _locked = Column("locked", String(255), nullable=True, unique=False,
105 default=None)
105 default=None)
106 _locked.create(table=tbl)
106 _locked.create(table=tbl)
107
107
108 #add langing revision column
108 #add langing revision column
109 landing_rev = Column("landing_revision", String(255), nullable=True,
109 landing_rev = Column("landing_revision", String(255), nullable=True,
110 unique=False, default='tip')
110 unique=False, default='tip')
111 landing_rev.create(table=tbl)
111 landing_rev.create(table=tbl)
112 landing_rev.alter(nullable=False, default='tip', table=tbl)
112 landing_rev.alter(nullable=False, default='tip', table=tbl)
113
113
114 #==========================================================================
114 #==========================================================================
115 # GROUPS
115 # GROUPS
116 #==========================================================================
116 #==========================================================================
117 from rhodecode.lib.dbmigrate.schema.db_1_3_0 import RepoGroup
117 from rhodecode.lib.dbmigrate.schema.db_1_3_0 import RepoGroup
118 tbl = RepoGroup.__table__
118 tbl = RepoGroup.__table__
119
119
120 # add enable locking column
120 # add enable locking column
121 enable_locking = Column("enable_locking", Boolean(), nullable=True,
121 enable_locking = Column("enable_locking", Boolean(), nullable=True,
122 unique=None, default=False)
122 unique=None, default=False)
123 enable_locking.create(table=tbl)
123 enable_locking.create(table=tbl)
124 enable_locking.alter(nullable=False, default=False)
124 enable_locking.alter(nullable=False, default=False)
125
125
126 #==========================================================================
126 #==========================================================================
127 # CACHE INVALIDATION
127 # CACHE INVALIDATION
128 #==========================================================================
128 #==========================================================================
129 from rhodecode.lib.dbmigrate.schema.db_1_3_0 import CacheInvalidation
129 from rhodecode.lib.dbmigrate.schema.db_1_3_0 import CacheInvalidation
130 tbl = CacheInvalidation.__table__
130 tbl = CacheInvalidation.__table__
131
131
132 # add INDEX for cache keys
132 # add INDEX for cache keys
133 col = CacheInvalidation.__table__.columns.cache_key
133 col = CacheInvalidation.__table__.columns.cache_key
134 col.alter(index=Index('key_idx', 'cache_key'))
134 col.alter(index=Index('key_idx', 'cache_key'))
135
135
136 #==========================================================================
136 #==========================================================================
137 # NOTIFICATION
137 # NOTIFICATION
138 #==========================================================================
138 #==========================================================================
139 from rhodecode.lib.dbmigrate.schema.db_1_3_0 import Notification
139 from rhodecode.lib.dbmigrate.schema.db_1_3_0 import Notification
140 tbl = Notification.__table__
140 tbl = Notification.__table__
141
141
142 # add index for notification type
142 # add index for notification type
143 col = Notification.__table__.columns.type
143 col = Notification.__table__.columns.type
144 col.alter(index=Index('notification_type_idx', 'type'),)
144 col.alter(index=Index('notification_type_idx', 'type'),)
145
145
146 #==========================================================================
146 #==========================================================================
147 # CHANGESET_COMMENTS
147 # CHANGESET_COMMENTS
148 #==========================================================================
148 #==========================================================================
149 from rhodecode.lib.dbmigrate.schema.db_1_3_0 import ChangesetComment
149 from rhodecode.lib.dbmigrate.schema.db_1_3_0 import ChangesetComment
150
150
151 tbl = ChangesetComment.__table__
151 tbl = ChangesetComment.__table__
152 col = ChangesetComment.__table__.columns.revision
152 col = ChangesetComment.__table__.columns.revision
153
153
154 # add index for revisions
154 # add index for revisions
155 col.alter(index=Index('cc_revision_idx', 'revision'),)
155 col.alter(index=Index('cc_revision_idx', 'revision'),)
156
156
157 # add hl_lines column
157 # add hl_lines column
158 hl_lines = Column('hl_lines', Unicode(512), nullable=True)
158 hl_lines = Column('hl_lines', Unicode(512), nullable=True)
159 hl_lines.create(table=tbl)
159 hl_lines.create(table=tbl)
160
160
161 # add created_on column
161 # add created_on column
162 created_on = Column('created_on', DateTime(timezone=False), nullable=True,
162 created_on = Column('created_on', DateTime(timezone=False), nullable=True,
163 default=datetime.datetime.now)
163 default=datetime.datetime.now)
164 created_on.create(table=tbl)
164 created_on.create(table=tbl)
165 created_on.alter(nullable=False, default=datetime.datetime.now)
165 created_on.alter(nullable=False, default=datetime.datetime.now)
166
166
167 modified_at = Column('modified_at', DateTime(timezone=False), nullable=False,
167 modified_at = Column('modified_at', DateTime(timezone=False), nullable=False,
168 default=datetime.datetime.now)
168 default=datetime.datetime.now)
169 modified_at.alter(type=DateTime(timezone=False), table=tbl)
169 modified_at.alter(type=DateTime(timezone=False), table=tbl)
170
170
171 # add FK to pull_request
171 # add FK to pull_request
172 pull_request_id = Column("pull_request_id", Integer(),
172 pull_request_id = Column("pull_request_id", Integer(),
173 ForeignKey('pull_requests.pull_request_id'),
173 ForeignKey('pull_requests.pull_request_id'),
174 nullable=True)
174 nullable=True)
175 pull_request_id.create(table=tbl)
175 pull_request_id.create(table=tbl)
176 ## RESET COMPLETLY THE metadata for sqlalchemy back after using 1_3_0
176 ## RESET COMPLETLY THE metadata for sqlalchemy back after using 1_3_0
177 Base = declarative_base()
177 Base = declarative_base()
178 Base.metadata.clear()
178 Base.metadata.clear()
179 Base.metadata = MetaData()
179 Base.metadata = MetaData()
180 Base.metadata.bind = migrate_engine
180 Base.metadata.bind = migrate_engine
181 meta.Base = Base
181 meta.Base = Base
182
182
183
183
184 def downgrade(migrate_engine):
184 def downgrade(migrate_engine):
185 meta = MetaData()
185 meta = MetaData()
186 meta.bind = migrate_engine
186 meta.bind = migrate_engine
@@ -1,674 +1,674 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.lib.utils
3 rhodecode.lib.utils
4 ~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~
5
5
6 Utilities library for RhodeCode
6 Utilities library for RhodeCode
7
7
8 :created_on: Apr 18, 2010
8 :created_on: Apr 18, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software: you can redistribute it and/or modify
13 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 3 of the License, or
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25
25
26 import os
26 import os
27 import re
27 import re
28 import logging
28 import logging
29 import datetime
29 import datetime
30 import traceback
30 import traceback
31 import paste
31 import paste
32 import beaker
32 import beaker
33 import tarfile
33 import tarfile
34 import shutil
34 import shutil
35 from os.path import abspath
35 from os.path import abspath
36 from os.path import dirname as dn, join as jn
36 from os.path import dirname as dn, join as jn
37
37
38 from paste.script.command import Command, BadCommand
38 from paste.script.command import Command, BadCommand
39
39
40 from mercurial import ui, config
40 from mercurial import ui, config
41
41
42 from webhelpers.text import collapse, remove_formatting, strip_tags
42 from webhelpers.text import collapse, remove_formatting, strip_tags
43
43
44 from rhodecode.lib.vcs import get_backend
44 from rhodecode.lib.vcs import get_backend
45 from rhodecode.lib.vcs.backends.base import BaseChangeset
45 from rhodecode.lib.vcs.backends.base import BaseChangeset
46 from rhodecode.lib.vcs.utils.lazy import LazyProperty
46 from rhodecode.lib.vcs.utils.lazy import LazyProperty
47 from rhodecode.lib.vcs.utils.helpers import get_scm
47 from rhodecode.lib.vcs.utils.helpers import get_scm
48 from rhodecode.lib.vcs.exceptions import VCSError
48 from rhodecode.lib.vcs.exceptions import VCSError
49
49
50 from rhodecode.lib.caching_query import FromCache
50 from rhodecode.lib.caching_query import FromCache
51
51
52 from rhodecode.model import meta
52 from rhodecode.model import meta
53 from rhodecode.model.db import Repository, User, RhodeCodeUi, \
53 from rhodecode.model.db import Repository, User, RhodeCodeUi, \
54 UserLog, RepoGroup, RhodeCodeSetting, CacheInvalidation
54 UserLog, RepoGroup, RhodeCodeSetting, CacheInvalidation
55 from rhodecode.model.meta import Session
55 from rhodecode.model.meta import Session
56 from rhodecode.model.repos_group import ReposGroupModel
56 from rhodecode.model.repos_group import ReposGroupModel
57 from rhodecode.lib.utils2 import safe_str, safe_unicode
57 from rhodecode.lib.utils2 import safe_str, safe_unicode
58 from rhodecode.lib.vcs.utils.fakemod import create_module
58 from rhodecode.lib.vcs.utils.fakemod import create_module
59
59
60 log = logging.getLogger(__name__)
60 log = logging.getLogger(__name__)
61
61
62 REMOVED_REPO_PAT = re.compile(r'rm__\d{8}_\d{6}_\d{6}__.*')
62 REMOVED_REPO_PAT = re.compile(r'rm__\d{8}_\d{6}_\d{6}__.*')
63
63
64
64
65 def recursive_replace(str_, replace=' '):
65 def recursive_replace(str_, replace=' '):
66 """
66 """
67 Recursive replace of given sign to just one instance
67 Recursive replace of given sign to just one instance
68
68
69 :param str_: given string
69 :param str_: given string
70 :param replace: char to find and replace multiple instances
70 :param replace: char to find and replace multiple instances
71
71
72 Examples::
72 Examples::
73 >>> recursive_replace("Mighty---Mighty-Bo--sstones",'-')
73 >>> recursive_replace("Mighty---Mighty-Bo--sstones",'-')
74 'Mighty-Mighty-Bo-sstones'
74 'Mighty-Mighty-Bo-sstones'
75 """
75 """
76
76
77 if str_.find(replace * 2) == -1:
77 if str_.find(replace * 2) == -1:
78 return str_
78 return str_
79 else:
79 else:
80 str_ = str_.replace(replace * 2, replace)
80 str_ = str_.replace(replace * 2, replace)
81 return recursive_replace(str_, replace)
81 return recursive_replace(str_, replace)
82
82
83
83
84 def repo_name_slug(value):
84 def repo_name_slug(value):
85 """
85 """
86 Return slug of name of repository
86 Return slug of name of repository
87 This function is called on each creation/modification
87 This function is called on each creation/modification
88 of repository to prevent bad names in repo
88 of repository to prevent bad names in repo
89 """
89 """
90
90
91 slug = remove_formatting(value)
91 slug = remove_formatting(value)
92 slug = strip_tags(slug)
92 slug = strip_tags(slug)
93
93
94 for c in """`?=[]\;'"<>,/~!@#$%^&*()+{}|: """:
94 for c in """`?=[]\;'"<>,/~!@#$%^&*()+{}|: """:
95 slug = slug.replace(c, '-')
95 slug = slug.replace(c, '-')
96 slug = recursive_replace(slug, '-')
96 slug = recursive_replace(slug, '-')
97 slug = collapse(slug, '-')
97 slug = collapse(slug, '-')
98 return slug
98 return slug
99
99
100
100
101 def get_repo_slug(request):
101 def get_repo_slug(request):
102 _repo = request.environ['pylons.routes_dict'].get('repo_name')
102 _repo = request.environ['pylons.routes_dict'].get('repo_name')
103 if _repo:
103 if _repo:
104 _repo = _repo.rstrip('/')
104 _repo = _repo.rstrip('/')
105 return _repo
105 return _repo
106
106
107
107
108 def get_repos_group_slug(request):
108 def get_repos_group_slug(request):
109 _group = request.environ['pylons.routes_dict'].get('group_name')
109 _group = request.environ['pylons.routes_dict'].get('group_name')
110 if _group:
110 if _group:
111 _group = _group.rstrip('/')
111 _group = _group.rstrip('/')
112 return _group
112 return _group
113
113
114
114
115 def action_logger(user, action, repo, ipaddr='', sa=None, commit=False):
115 def action_logger(user, action, repo, ipaddr='', sa=None, commit=False):
116 """
116 """
117 Action logger for various actions made by users
117 Action logger for various actions made by users
118
118
119 :param user: user that made this action, can be a unique username string or
119 :param user: user that made this action, can be a unique username string or
120 object containing user_id attribute
120 object containing user_id attribute
121 :param action: action to log, should be on of predefined unique actions for
121 :param action: action to log, should be on of predefined unique actions for
122 easy translations
122 easy translations
123 :param repo: string name of repository or object containing repo_id,
123 :param repo: string name of repository or object containing repo_id,
124 that action was made on
124 that action was made on
125 :param ipaddr: optional ip address from what the action was made
125 :param ipaddr: optional ip address from what the action was made
126 :param sa: optional sqlalchemy session
126 :param sa: optional sqlalchemy session
127
127
128 """
128 """
129
129
130 if not sa:
130 if not sa:
131 sa = meta.Session()
131 sa = meta.Session()
132
132
133 try:
133 try:
134 if hasattr(user, 'user_id'):
134 if hasattr(user, 'user_id'):
135 user_obj = user
135 user_obj = user
136 elif isinstance(user, basestring):
136 elif isinstance(user, basestring):
137 user_obj = User.get_by_username(user)
137 user_obj = User.get_by_username(user)
138 else:
138 else:
139 raise Exception('You have to provide user object or username')
139 raise Exception('You have to provide user object or username')
140
140
141 if hasattr(repo, 'repo_id'):
141 if hasattr(repo, 'repo_id'):
142 repo_obj = Repository.get(repo.repo_id)
142 repo_obj = Repository.get(repo.repo_id)
143 repo_name = repo_obj.repo_name
143 repo_name = repo_obj.repo_name
144 elif isinstance(repo, basestring):
144 elif isinstance(repo, basestring):
145 repo_name = repo.lstrip('/')
145 repo_name = repo.lstrip('/')
146 repo_obj = Repository.get_by_repo_name(repo_name)
146 repo_obj = Repository.get_by_repo_name(repo_name)
147 else:
147 else:
148 repo_obj = None
148 repo_obj = None
149 repo_name = ''
149 repo_name = ''
150
150
151 user_log = UserLog()
151 user_log = UserLog()
152 user_log.user_id = user_obj.user_id
152 user_log.user_id = user_obj.user_id
153 user_log.action = safe_unicode(action)
153 user_log.action = safe_unicode(action)
154
154
155 user_log.repository = repo_obj
155 user_log.repository = repo_obj
156 user_log.repository_name = repo_name
156 user_log.repository_name = repo_name
157
157
158 user_log.action_date = datetime.datetime.now()
158 user_log.action_date = datetime.datetime.now()
159 user_log.user_ip = ipaddr
159 user_log.user_ip = ipaddr
160 sa.add(user_log)
160 sa.add(user_log)
161
161
162 log.info(
162 log.info(
163 'Adding user %s, action %s on %s' % (user_obj, action,
163 'Adding user %s, action %s on %s' % (user_obj, action,
164 safe_unicode(repo))
164 safe_unicode(repo))
165 )
165 )
166 if commit:
166 if commit:
167 sa.commit()
167 sa.commit()
168 except:
168 except:
169 log.error(traceback.format_exc())
169 log.error(traceback.format_exc())
170 raise
170 raise
171
171
172
172
173 def get_repos(path, recursive=False):
173 def get_repos(path, recursive=False):
174 """
174 """
175 Scans given path for repos and return (name,(type,path)) tuple
175 Scans given path for repos and return (name,(type,path)) tuple
176
176
177 :param path: path to scan for repositories
177 :param path: path to scan for repositories
178 :param recursive: recursive search and return names with subdirs in front
178 :param recursive: recursive search and return names with subdirs in front
179 """
179 """
180
180
181 # remove ending slash for better results
181 # remove ending slash for better results
182 path = path.rstrip(os.sep)
182 path = path.rstrip(os.sep)
183
183
184 def _get_repos(p):
184 def _get_repos(p):
185 if not os.access(p, os.W_OK):
185 if not os.access(p, os.W_OK):
186 return
186 return
187 for dirpath in os.listdir(p):
187 for dirpath in os.listdir(p):
188 if os.path.isfile(os.path.join(p, dirpath)):
188 if os.path.isfile(os.path.join(p, dirpath)):
189 continue
189 continue
190 cur_path = os.path.join(p, dirpath)
190 cur_path = os.path.join(p, dirpath)
191 try:
191 try:
192 scm_info = get_scm(cur_path)
192 scm_info = get_scm(cur_path)
193 yield scm_info[1].split(path, 1)[-1].lstrip(os.sep), scm_info
193 yield scm_info[1].split(path, 1)[-1].lstrip(os.sep), scm_info
194 except VCSError:
194 except VCSError:
195 if not recursive:
195 if not recursive:
196 continue
196 continue
197 #check if this dir containts other repos for recursive scan
197 #check if this dir containts other repos for recursive scan
198 rec_path = os.path.join(p, dirpath)
198 rec_path = os.path.join(p, dirpath)
199 if os.path.isdir(rec_path):
199 if os.path.isdir(rec_path):
200 for inner_scm in _get_repos(rec_path):
200 for inner_scm in _get_repos(rec_path):
201 yield inner_scm
201 yield inner_scm
202
202
203 return _get_repos(path)
203 return _get_repos(path)
204
204
205
205
206 def is_valid_repo(repo_name, base_path, scm=None):
206 def is_valid_repo(repo_name, base_path, scm=None):
207 """
207 """
208 Returns True if given path is a valid repository False otherwise.
208 Returns True if given path is a valid repository False otherwise.
209 If scm param is given also compare if given scm is the same as expected
209 If scm param is given also compare if given scm is the same as expected
210 from scm parameter
210 from scm parameter
211
211
212 :param repo_name:
212 :param repo_name:
213 :param base_path:
213 :param base_path:
214 :param scm:
214 :param scm:
215
215
216 :return True: if given path is a valid repository
216 :return True: if given path is a valid repository
217 """
217 """
218 full_path = os.path.join(safe_str(base_path), safe_str(repo_name))
218 full_path = os.path.join(safe_str(base_path), safe_str(repo_name))
219
219
220 try:
220 try:
221 scm_ = get_scm(full_path)
221 scm_ = get_scm(full_path)
222 if scm:
222 if scm:
223 return scm_[0] == scm
223 return scm_[0] == scm
224 return True
224 return True
225 except VCSError:
225 except VCSError:
226 return False
226 return False
227
227
228
228
229 def is_valid_repos_group(repos_group_name, base_path):
229 def is_valid_repos_group(repos_group_name, base_path):
230 """
230 """
231 Returns True if given path is a repos group False otherwise
231 Returns True if given path is a repos group False otherwise
232
232
233 :param repo_name:
233 :param repo_name:
234 :param base_path:
234 :param base_path:
235 """
235 """
236 full_path = os.path.join(safe_str(base_path), safe_str(repos_group_name))
236 full_path = os.path.join(safe_str(base_path), safe_str(repos_group_name))
237
237
238 # check if it's not a repo
238 # check if it's not a repo
239 if is_valid_repo(repos_group_name, base_path):
239 if is_valid_repo(repos_group_name, base_path):
240 return False
240 return False
241
241
242 try:
242 try:
243 # we need to check bare git repos at higher level
243 # we need to check bare git repos at higher level
244 # since we might match branches/hooks/info/objects or possible
244 # since we might match branches/hooks/info/objects or possible
245 # other things inside bare git repo
245 # other things inside bare git repo
246 get_scm(os.path.dirname(full_path))
246 get_scm(os.path.dirname(full_path))
247 return False
247 return False
248 except VCSError:
248 except VCSError:
249 pass
249 pass
250
250
251 # check if it's a valid path
251 # check if it's a valid path
252 if os.path.isdir(full_path):
252 if os.path.isdir(full_path):
253 return True
253 return True
254
254
255 return False
255 return False
256
256
257
257
258 def ask_ok(prompt, retries=4, complaint='Yes or no, please!'):
258 def ask_ok(prompt, retries=4, complaint='Yes or no, please!'):
259 while True:
259 while True:
260 ok = raw_input(prompt)
260 ok = raw_input(prompt)
261 if ok in ('y', 'ye', 'yes'):
261 if ok in ('y', 'ye', 'yes'):
262 return True
262 return True
263 if ok in ('n', 'no', 'nop', 'nope'):
263 if ok in ('n', 'no', 'nop', 'nope'):
264 return False
264 return False
265 retries = retries - 1
265 retries = retries - 1
266 if retries < 0:
266 if retries < 0:
267 raise IOError
267 raise IOError
268 print complaint
268 print complaint
269
269
270 #propagated from mercurial documentation
270 #propagated from mercurial documentation
271 ui_sections = ['alias', 'auth',
271 ui_sections = ['alias', 'auth',
272 'decode/encode', 'defaults',
272 'decode/encode', 'defaults',
273 'diff', 'email',
273 'diff', 'email',
274 'extensions', 'format',
274 'extensions', 'format',
275 'merge-patterns', 'merge-tools',
275 'merge-patterns', 'merge-tools',
276 'hooks', 'http_proxy',
276 'hooks', 'http_proxy',
277 'smtp', 'patch',
277 'smtp', 'patch',
278 'paths', 'profiling',
278 'paths', 'profiling',
279 'server', 'trusted',
279 'server', 'trusted',
280 'ui', 'web', ]
280 'ui', 'web', ]
281
281
282
282
283 def make_ui(read_from='file', path=None, checkpaths=True, clear_session=True):
283 def make_ui(read_from='file', path=None, checkpaths=True, clear_session=True):
284 """
284 """
285 A function that will read python rc files or database
285 A function that will read python rc files or database
286 and make an mercurial ui object from read options
286 and make an mercurial ui object from read options
287
287
288 :param path: path to mercurial config file
288 :param path: path to mercurial config file
289 :param checkpaths: check the path
289 :param checkpaths: check the path
290 :param read_from: read from 'file' or 'db'
290 :param read_from: read from 'file' or 'db'
291 """
291 """
292
292
293 baseui = ui.ui()
293 baseui = ui.ui()
294
294
295 # clean the baseui object
295 # clean the baseui object
296 baseui._ocfg = config.config()
296 baseui._ocfg = config.config()
297 baseui._ucfg = config.config()
297 baseui._ucfg = config.config()
298 baseui._tcfg = config.config()
298 baseui._tcfg = config.config()
299
299
300 if read_from == 'file':
300 if read_from == 'file':
301 if not os.path.isfile(path):
301 if not os.path.isfile(path):
302 log.debug('hgrc file is not present at %s skipping...' % path)
302 log.debug('hgrc file is not present at %s skipping...' % path)
303 return False
303 return False
304 log.debug('reading hgrc from %s' % path)
304 log.debug('reading hgrc from %s' % path)
305 cfg = config.config()
305 cfg = config.config()
306 cfg.read(path)
306 cfg.read(path)
307 for section in ui_sections:
307 for section in ui_sections:
308 for k, v in cfg.items(section):
308 for k, v in cfg.items(section):
309 log.debug('settings ui from file[%s]%s:%s' % (section, k, v))
309 log.debug('settings ui from file[%s]%s:%s' % (section, k, v))
310 baseui.setconfig(section, k, v)
310 baseui.setconfig(section, k, v)
311
311
312 elif read_from == 'db':
312 elif read_from == 'db':
313 sa = meta.Session()
313 sa = meta.Session()
314 ret = sa.query(RhodeCodeUi)\
314 ret = sa.query(RhodeCodeUi)\
315 .options(FromCache("sql_cache_short", "get_hg_ui_settings"))\
315 .options(FromCache("sql_cache_short", "get_hg_ui_settings"))\
316 .all()
316 .all()
317
317
318 hg_ui = ret
318 hg_ui = ret
319 for ui_ in hg_ui:
319 for ui_ in hg_ui:
320 if ui_.ui_active:
320 if ui_.ui_active:
321 log.debug('settings ui from db[%s]%s:%s', ui_.ui_section,
321 log.debug('settings ui from db[%s]%s:%s', ui_.ui_section,
322 ui_.ui_key, ui_.ui_value)
322 ui_.ui_key, ui_.ui_value)
323 baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
323 baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
324 if ui_.ui_key == 'push_ssl':
324 if ui_.ui_key == 'push_ssl':
325 # force set push_ssl requirement to False, rhodecode
325 # force set push_ssl requirement to False, rhodecode
326 # handles that
326 # handles that
327 baseui.setconfig(ui_.ui_section, ui_.ui_key, False)
327 baseui.setconfig(ui_.ui_section, ui_.ui_key, False)
328 if clear_session:
328 if clear_session:
329 meta.Session.remove()
329 meta.Session.remove()
330 return baseui
330 return baseui
331
331
332
332
333 def set_rhodecode_config(config):
333 def set_rhodecode_config(config):
334 """
334 """
335 Updates pylons config with new settings from database
335 Updates pylons config with new settings from database
336
336
337 :param config:
337 :param config:
338 """
338 """
339 hgsettings = RhodeCodeSetting.get_app_settings()
339 hgsettings = RhodeCodeSetting.get_app_settings()
340
340
341 for k, v in hgsettings.items():
341 for k, v in hgsettings.items():
342 config[k] = v
342 config[k] = v
343
343
344
344
345 def invalidate_cache(cache_key, *args):
345 def invalidate_cache(cache_key, *args):
346 """
346 """
347 Puts cache invalidation task into db for
347 Puts cache invalidation task into db for
348 further global cache invalidation
348 further global cache invalidation
349 """
349 """
350
350
351 from rhodecode.model.scm import ScmModel
351 from rhodecode.model.scm import ScmModel
352
352
353 if cache_key.startswith('get_repo_cached_'):
353 if cache_key.startswith('get_repo_cached_'):
354 name = cache_key.split('get_repo_cached_')[-1]
354 name = cache_key.split('get_repo_cached_')[-1]
355 ScmModel().mark_for_invalidation(name)
355 ScmModel().mark_for_invalidation(name)
356
356
357
357
358 def map_groups(path):
358 def map_groups(path):
359 """
359 """
360 Given a full path to a repository, create all nested groups that this
360 Given a full path to a repository, create all nested groups that this
361 repo is inside. This function creates parent-child relationships between
361 repo is inside. This function creates parent-child relationships between
362 groups and creates default perms for all new groups.
362 groups and creates default perms for all new groups.
363
363
364 :param paths: full path to repository
364 :param paths: full path to repository
365 """
365 """
366 sa = meta.Session()
366 sa = meta.Session()
367 groups = path.split(Repository.url_sep())
367 groups = path.split(Repository.url_sep())
368 parent = None
368 parent = None
369 group = None
369 group = None
370
370
371 # last element is repo in nested groups structure
371 # last element is repo in nested groups structure
372 groups = groups[:-1]
372 groups = groups[:-1]
373 rgm = ReposGroupModel(sa)
373 rgm = ReposGroupModel(sa)
374 for lvl, group_name in enumerate(groups):
374 for lvl, group_name in enumerate(groups):
375 group_name = '/'.join(groups[:lvl] + [group_name])
375 group_name = '/'.join(groups[:lvl] + [group_name])
376 group = RepoGroup.get_by_group_name(group_name)
376 group = RepoGroup.get_by_group_name(group_name)
377 desc = '%s group' % group_name
377 desc = '%s group' % group_name
378
378
379 # skip folders that are now removed repos
379 # skip folders that are now removed repos
380 if REMOVED_REPO_PAT.match(group_name):
380 if REMOVED_REPO_PAT.match(group_name):
381 break
381 break
382
382
383 if group is None:
383 if group is None:
384 log.debug('creating group level: %s group_name: %s' % (lvl,
384 log.debug('creating group level: %s group_name: %s' % (lvl,
385 group_name))
385 group_name))
386 group = RepoGroup(group_name, parent)
386 group = RepoGroup(group_name, parent)
387 group.group_description = desc
387 group.group_description = desc
388 sa.add(group)
388 sa.add(group)
389 rgm._create_default_perms(group)
389 rgm._create_default_perms(group)
390 sa.flush()
390 sa.flush()
391 parent = group
391 parent = group
392 return group
392 return group
393
393
394
394
395 def repo2db_mapper(initial_repo_list, remove_obsolete=False,
395 def repo2db_mapper(initial_repo_list, remove_obsolete=False,
396 install_git_hook=False):
396 install_git_hook=False):
397 """
397 """
398 maps all repos given in initial_repo_list, non existing repositories
398 maps all repos given in initial_repo_list, non existing repositories
399 are created, if remove_obsolete is True it also check for db entries
399 are created, if remove_obsolete is True it also check for db entries
400 that are not in initial_repo_list and removes them.
400 that are not in initial_repo_list and removes them.
401
401
402 :param initial_repo_list: list of repositories found by scanning methods
402 :param initial_repo_list: list of repositories found by scanning methods
403 :param remove_obsolete: check for obsolete entries in database
403 :param remove_obsolete: check for obsolete entries in database
404 :param install_git_hook: if this is True, also check and install githook
404 :param install_git_hook: if this is True, also check and install githook
405 for a repo if missing
405 for a repo if missing
406 """
406 """
407 from rhodecode.model.repo import RepoModel
407 from rhodecode.model.repo import RepoModel
408 from rhodecode.model.scm import ScmModel
408 from rhodecode.model.scm import ScmModel
409 sa = meta.Session()
409 sa = meta.Session()
410 rm = RepoModel()
410 rm = RepoModel()
411 user = sa.query(User).filter(User.admin == True).first()
411 user = sa.query(User).filter(User.admin == True).first()
412 if user is None:
412 if user is None:
413 raise Exception('Missing administrative account !')
413 raise Exception('Missing administrative account !')
414 added = []
414 added = []
415
415
416 # # clear cache keys
416 # # clear cache keys
417 # log.debug("Clearing cache keys now...")
417 # log.debug("Clearing cache keys now...")
418 # CacheInvalidation.clear_cache()
418 # CacheInvalidation.clear_cache()
419 # sa.commit()
419 # sa.commit()
420
420
421 for name, repo in initial_repo_list.items():
421 for name, repo in initial_repo_list.items():
422 group = map_groups(name)
422 group = map_groups(name)
423 db_repo = rm.get_by_repo_name(name)
423 db_repo = rm.get_by_repo_name(name)
424 # found repo that is on filesystem not in RhodeCode database
424 # found repo that is on filesystem not in RhodeCode database
425 if not db_repo:
425 if not db_repo:
426 log.info('repository %s not found creating now' % name)
426 log.info('repository %s not found creating now' % name)
427 added.append(name)
427 added.append(name)
428 desc = (repo.description
428 desc = (repo.description
429 if repo.description != 'unknown'
429 if repo.description != 'unknown'
430 else '%s repository' % name)
430 else '%s repository' % name)
431 new_repo = rm.create_repo(
431 new_repo = rm.create_repo(
432 repo_name=name,
432 repo_name=name,
433 repo_type=repo.alias,
433 repo_type=repo.alias,
434 description=desc,
434 description=desc,
435 repos_group=getattr(group, 'group_id', None),
435 repos_group=getattr(group, 'group_id', None),
436 owner=user,
436 owner=user,
437 just_db=True
437 just_db=True
438 )
438 )
439 # we added that repo just now, and make sure it has githook
439 # we added that repo just now, and make sure it has githook
440 # installed
440 # installed
441 if new_repo.repo_type == 'git':
441 if new_repo.repo_type == 'git':
442 ScmModel().install_git_hook(new_repo.scm_instance)
442 ScmModel().install_git_hook(new_repo.scm_instance)
443 elif install_git_hook:
443 elif install_git_hook:
444 if db_repo.repo_type == 'git':
444 if db_repo.repo_type == 'git':
445 ScmModel().install_git_hook(db_repo.scm_instance)
445 ScmModel().install_git_hook(db_repo.scm_instance)
446 # during starting install all cache keys for all repositories in the
446 # during starting install all cache keys for all repositories in the
447 # system, this will register all repos and multiple instances
447 # system, this will register all repos and multiple instances
448 key, _prefix, _org_key = CacheInvalidation._get_key(name)
448 key, _prefix, _org_key = CacheInvalidation._get_key(name)
449 log.debug("Creating cache key for %s instance_id:`%s`" % (name, _prefix))
449 log.debug("Creating cache key for %s instance_id:`%s`" % (name, _prefix))
450 CacheInvalidation._get_or_create_key(key, _prefix, _org_key, commit=False)
450 CacheInvalidation._get_or_create_key(key, _prefix, _org_key, commit=False)
451 sa.commit()
451 sa.commit()
452 removed = []
452 removed = []
453 if remove_obsolete:
453 if remove_obsolete:
454 # remove from database those repositories that are not in the filesystem
454 # remove from database those repositories that are not in the filesystem
455 for repo in sa.query(Repository).all():
455 for repo in sa.query(Repository).all():
456 if repo.repo_name not in initial_repo_list.keys():
456 if repo.repo_name not in initial_repo_list.keys():
457 log.debug("Removing non existing repository found in db `%s`" %
457 log.debug("Removing non existing repository found in db `%s`" %
458 repo.repo_name)
458 repo.repo_name)
459 try:
459 try:
460 sa.delete(repo)
460 sa.delete(repo)
461 sa.commit()
461 sa.commit()
462 removed.append(repo.repo_name)
462 removed.append(repo.repo_name)
463 except:
463 except:
464 #don't hold further removals on error
464 #don't hold further removals on error
465 log.error(traceback.format_exc())
465 log.error(traceback.format_exc())
466 sa.rollback()
466 sa.rollback()
467
467
468 return added, removed
468 return added, removed
469
469
470
470
471 # set cache regions for beaker so celery can utilise it
471 # set cache regions for beaker so celery can utilise it
472 def add_cache(settings):
472 def add_cache(settings):
473 cache_settings = {'regions': None}
473 cache_settings = {'regions': None}
474 for key in settings.keys():
474 for key in settings.keys():
475 for prefix in ['beaker.cache.', 'cache.']:
475 for prefix in ['beaker.cache.', 'cache.']:
476 if key.startswith(prefix):
476 if key.startswith(prefix):
477 name = key.split(prefix)[1].strip()
477 name = key.split(prefix)[1].strip()
478 cache_settings[name] = settings[key].strip()
478 cache_settings[name] = settings[key].strip()
479 if cache_settings['regions']:
479 if cache_settings['regions']:
480 for region in cache_settings['regions'].split(','):
480 for region in cache_settings['regions'].split(','):
481 region = region.strip()
481 region = region.strip()
482 region_settings = {}
482 region_settings = {}
483 for key, value in cache_settings.items():
483 for key, value in cache_settings.items():
484 if key.startswith(region):
484 if key.startswith(region):
485 region_settings[key.split('.')[1]] = value
485 region_settings[key.split('.')[1]] = value
486 region_settings['expire'] = int(region_settings.get('expire',
486 region_settings['expire'] = int(region_settings.get('expire',
487 60))
487 60))
488 region_settings.setdefault('lock_dir',
488 region_settings.setdefault('lock_dir',
489 cache_settings.get('lock_dir'))
489 cache_settings.get('lock_dir'))
490 region_settings.setdefault('data_dir',
490 region_settings.setdefault('data_dir',
491 cache_settings.get('data_dir'))
491 cache_settings.get('data_dir'))
492
492
493 if 'type' not in region_settings:
493 if 'type' not in region_settings:
494 region_settings['type'] = cache_settings.get('type',
494 region_settings['type'] = cache_settings.get('type',
495 'memory')
495 'memory')
496 beaker.cache.cache_regions[region] = region_settings
496 beaker.cache.cache_regions[region] = region_settings
497
497
498
498
499 def load_rcextensions(root_path):
499 def load_rcextensions(root_path):
500 import rhodecode
500 import rhodecode
501 from rhodecode.config import conf
501 from rhodecode.config import conf
502
502
503 path = os.path.join(root_path, 'rcextensions', '__init__.py')
503 path = os.path.join(root_path, 'rcextensions', '__init__.py')
504 if os.path.isfile(path):
504 if os.path.isfile(path):
505 rcext = create_module('rc', path)
505 rcext = create_module('rc', path)
506 EXT = rhodecode.EXTENSIONS = rcext
506 EXT = rhodecode.EXTENSIONS = rcext
507 log.debug('Found rcextensions now loading %s...' % rcext)
507 log.debug('Found rcextensions now loading %s...' % rcext)
508
508
509 # Additional mappings that are not present in the pygments lexers
509 # Additional mappings that are not present in the pygments lexers
510 conf.LANGUAGES_EXTENSIONS_MAP.update(getattr(EXT, 'EXTRA_MAPPINGS', {}))
510 conf.LANGUAGES_EXTENSIONS_MAP.update(getattr(EXT, 'EXTRA_MAPPINGS', {}))
511
511
512 #OVERRIDE OUR EXTENSIONS FROM RC-EXTENSIONS (if present)
512 #OVERRIDE OUR EXTENSIONS FROM RC-EXTENSIONS (if present)
513
513
514 if getattr(EXT, 'INDEX_EXTENSIONS', []) != []:
514 if getattr(EXT, 'INDEX_EXTENSIONS', []) != []:
515 log.debug('settings custom INDEX_EXTENSIONS')
515 log.debug('settings custom INDEX_EXTENSIONS')
516 conf.INDEX_EXTENSIONS = getattr(EXT, 'INDEX_EXTENSIONS', [])
516 conf.INDEX_EXTENSIONS = getattr(EXT, 'INDEX_EXTENSIONS', [])
517
517
518 #ADDITIONAL MAPPINGS
518 #ADDITIONAL MAPPINGS
519 log.debug('adding extra into INDEX_EXTENSIONS')
519 log.debug('adding extra into INDEX_EXTENSIONS')
520 conf.INDEX_EXTENSIONS.extend(getattr(EXT, 'EXTRA_INDEX_EXTENSIONS', []))
520 conf.INDEX_EXTENSIONS.extend(getattr(EXT, 'EXTRA_INDEX_EXTENSIONS', []))
521
521
522
522
523 #==============================================================================
523 #==============================================================================
524 # TEST FUNCTIONS AND CREATORS
524 # TEST FUNCTIONS AND CREATORS
525 #==============================================================================
525 #==============================================================================
526 def create_test_index(repo_location, config, full_index):
526 def create_test_index(repo_location, config, full_index):
527 """
527 """
528 Makes default test index
528 Makes default test index
529
529
530 :param config: test config
530 :param config: test config
531 :param full_index:
531 :param full_index:
532 """
532 """
533
533
534 from rhodecode.lib.indexers.daemon import WhooshIndexingDaemon
534 from rhodecode.lib.indexers.daemon import WhooshIndexingDaemon
535 from rhodecode.lib.pidlock import DaemonLock, LockHeld
535 from rhodecode.lib.pidlock import DaemonLock, LockHeld
536
536
537 repo_location = repo_location
537 repo_location = repo_location
538
538
539 index_location = os.path.join(config['app_conf']['index_dir'])
539 index_location = os.path.join(config['app_conf']['index_dir'])
540 if not os.path.exists(index_location):
540 if not os.path.exists(index_location):
541 os.makedirs(index_location)
541 os.makedirs(index_location)
542
542
543 try:
543 try:
544 l = DaemonLock(file_=jn(dn(index_location), 'make_index.lock'))
544 l = DaemonLock(file_=jn(dn(index_location), 'make_index.lock'))
545 WhooshIndexingDaemon(index_location=index_location,
545 WhooshIndexingDaemon(index_location=index_location,
546 repo_location=repo_location)\
546 repo_location=repo_location)\
547 .run(full_index=full_index)
547 .run(full_index=full_index)
548 l.release()
548 l.release()
549 except LockHeld:
549 except LockHeld:
550 pass
550 pass
551
551
552
552
553 def create_test_env(repos_test_path, config):
553 def create_test_env(repos_test_path, config):
554 """
554 """
555 Makes a fresh database and
555 Makes a fresh database and
556 install test repository into tmp dir
556 install test repository into tmp dir
557 """
557 """
558 from rhodecode.lib.db_manage import DbManage
558 from rhodecode.lib.db_manage import DbManage
559 from rhodecode.tests import HG_REPO, GIT_REPO, TESTS_TMP_PATH
559 from rhodecode.tests import HG_REPO, GIT_REPO, TESTS_TMP_PATH
560
560
561 # PART ONE create db
561 # PART ONE create db
562 dbconf = config['sqlalchemy.db1.url']
562 dbconf = config['sqlalchemy.db1.url']
563 log.debug('making test db %s' % dbconf)
563 log.debug('making test db %s' % dbconf)
564
564
565 # create test dir if it doesn't exist
565 # create test dir if it doesn't exist
566 if not os.path.isdir(repos_test_path):
566 if not os.path.isdir(repos_test_path):
567 log.debug('Creating testdir %s' % repos_test_path)
567 log.debug('Creating testdir %s' % repos_test_path)
568 os.makedirs(repos_test_path)
568 os.makedirs(repos_test_path)
569
569
570 dbmanage = DbManage(log_sql=True, dbconf=dbconf, root=config['here'],
570 dbmanage = DbManage(log_sql=True, dbconf=dbconf, root=config['here'],
571 tests=True)
571 tests=True)
572 dbmanage.create_tables(override=True)
572 dbmanage.create_tables(override=True)
573 dbmanage.create_settings(dbmanage.config_prompt(repos_test_path))
573 dbmanage.create_settings(dbmanage.config_prompt(repos_test_path))
574 dbmanage.create_default_user()
574 dbmanage.create_default_user()
575 dbmanage.admin_prompt()
575 dbmanage.admin_prompt()
576 dbmanage.create_permissions()
576 dbmanage.create_permissions()
577 dbmanage.populate_default_permissions()
577 dbmanage.populate_default_permissions()
578 Session().commit()
578 Session().commit()
579 # PART TWO make test repo
579 # PART TWO make test repo
580 log.debug('making test vcs repositories')
580 log.debug('making test vcs repositories')
581
581
582 idx_path = config['app_conf']['index_dir']
582 idx_path = config['app_conf']['index_dir']
583 data_path = config['app_conf']['cache_dir']
583 data_path = config['app_conf']['cache_dir']
584
584
585 #clean index and data
585 #clean index and data
586 if idx_path and os.path.exists(idx_path):
586 if idx_path and os.path.exists(idx_path):
587 log.debug('remove %s' % idx_path)
587 log.debug('remove %s' % idx_path)
588 shutil.rmtree(idx_path)
588 shutil.rmtree(idx_path)
589
589
590 if data_path and os.path.exists(data_path):
590 if data_path and os.path.exists(data_path):
591 log.debug('remove %s' % data_path)
591 log.debug('remove %s' % data_path)
592 shutil.rmtree(data_path)
592 shutil.rmtree(data_path)
593
593
594 #CREATE DEFAULT TEST REPOS
594 #CREATE DEFAULT TEST REPOS
595 cur_dir = dn(dn(abspath(__file__)))
595 cur_dir = dn(dn(abspath(__file__)))
596 tar = tarfile.open(jn(cur_dir, 'tests', "vcs_test_hg.tar.gz"))
596 tar = tarfile.open(jn(cur_dir, 'tests', "vcs_test_hg.tar.gz"))
597 tar.extractall(jn(TESTS_TMP_PATH, HG_REPO))
597 tar.extractall(jn(TESTS_TMP_PATH, HG_REPO))
598 tar.close()
598 tar.close()
599
599
600 cur_dir = dn(dn(abspath(__file__)))
600 cur_dir = dn(dn(abspath(__file__)))
601 tar = tarfile.open(jn(cur_dir, 'tests', "vcs_test_git.tar.gz"))
601 tar = tarfile.open(jn(cur_dir, 'tests', "vcs_test_git.tar.gz"))
602 tar.extractall(jn(TESTS_TMP_PATH, GIT_REPO))
602 tar.extractall(jn(TESTS_TMP_PATH, GIT_REPO))
603 tar.close()
603 tar.close()
604
604
605 #LOAD VCS test stuff
605 #LOAD VCS test stuff
606 from rhodecode.tests.vcs import setup_package
606 from rhodecode.tests.vcs import setup_package
607 setup_package()
607 setup_package()
608
608
609
609
610 #==============================================================================
610 #==============================================================================
611 # PASTER COMMANDS
611 # PASTER COMMANDS
612 #==============================================================================
612 #==============================================================================
613 class BasePasterCommand(Command):
613 class BasePasterCommand(Command):
614 """
614 """
615 Abstract Base Class for paster commands.
615 Abstract Base Class for paster commands.
616
616
617 The celery commands are somewhat aggressive about loading
617 The celery commands are somewhat aggressive about loading
618 celery.conf, and since our module sets the `CELERY_LOADER`
618 celery.conf, and since our module sets the `CELERY_LOADER`
619 environment variable to our loader, we have to bootstrap a bit and
619 environment variable to our loader, we have to bootstrap a bit and
620 make sure we've had a chance to load the pylons config off of the
620 make sure we've had a chance to load the pylons config off of the
621 command line, otherwise everything fails.
621 command line, otherwise everything fails.
622 """
622 """
623 min_args = 1
623 min_args = 1
624 min_args_error = "Please provide a paster config file as an argument."
624 min_args_error = "Please provide a paster config file as an argument."
625 takes_config_file = 1
625 takes_config_file = 1
626 requires_config_file = True
626 requires_config_file = True
627
627
628 def notify_msg(self, msg, log=False):
628 def notify_msg(self, msg, log=False):
629 """Make a notification to user, additionally if logger is passed
629 """Make a notification to user, additionally if logger is passed
630 it logs this action using given logger
630 it logs this action using given logger
631
631
632 :param msg: message that will be printed to user
632 :param msg: message that will be printed to user
633 :param log: logging instance, to use to additionally log this message
633 :param log: logging instance, to use to additionally log this message
634
634
635 """
635 """
636 if log and isinstance(log, logging):
636 if log and isinstance(log, logging):
637 log(msg)
637 log(msg)
638
638
639 def run(self, args):
639 def run(self, args):
640 """
640 """
641 Overrides Command.run
641 Overrides Command.run
642
642
643 Checks for a config file argument and loads it.
643 Checks for a config file argument and loads it.
644 """
644 """
645 if len(args) < self.min_args:
645 if len(args) < self.min_args:
646 raise BadCommand(
646 raise BadCommand(
647 self.min_args_error % {'min_args': self.min_args,
647 self.min_args_error % {'min_args': self.min_args,
648 'actual_args': len(args)})
648 'actual_args': len(args)})
649
649
650 # Decrement because we're going to lob off the first argument.
650 # Decrement because we're going to lob off the first argument.
651 # @@ This is hacky
651 # @@ This is hacky
652 self.min_args -= 1
652 self.min_args -= 1
653 self.bootstrap_config(args[0])
653 self.bootstrap_config(args[0])
654 self.update_parser()
654 self.update_parser()
655 return super(BasePasterCommand, self).run(args[1:])
655 return super(BasePasterCommand, self).run(args[1:])
656
656
657 def update_parser(self):
657 def update_parser(self):
658 """
658 """
659 Abstract method. Allows for the class's parser to be updated
659 Abstract method. Allows for the class's parser to be updated
660 before the superclass's `run` method is called. Necessary to
660 before the superclass's `run` method is called. Necessary to
661 allow options/arguments to be passed through to the underlying
661 allow options/arguments to be passed through to the underlying
662 celery command.
662 celery command.
663 """
663 """
664 raise NotImplementedError("Abstract Method.")
664 raise NotImplementedError("Abstract Method.")
665
665
666 def bootstrap_config(self, conf):
666 def bootstrap_config(self, conf):
667 """
667 """
668 Loads the pylons configuration.
668 Loads the pylons configuration.
669 """
669 """
670 from pylons import config as pylonsconfig
670 from pylons import config as pylonsconfig
671
671
672 self.path_to_ini_file = os.path.realpath(conf)
672 self.path_to_ini_file = os.path.realpath(conf)
673 conf = paste.deploy.appconfig('config:' + self.path_to_ini_file)
673 conf = paste.deploy.appconfig('config:' + self.path_to_ini_file)
674 pylonsconfig.init_app(conf.global_conf, conf.local_conf)
674 pylonsconfig.init_app(conf.global_conf, conf.local_conf)
@@ -1,18 +1,18 b''
1 """
1 """
2 Mercurial libs compatibility
2 Mercurial libs compatibility
3 """
3 """
4
4
5 from mercurial import archival, merge as hg_merge, patch, ui
5 from mercurial import archival, merge as hg_merge, patch, ui
6 from mercurial.commands import clone, nullid, pull
6 from mercurial.commands import clone, nullid, pull
7 from mercurial.context import memctx, memfilectx
7 from mercurial.context import memctx, memfilectx
8 from mercurial.error import RepoError, RepoLookupError, Abort
8 from mercurial.error import RepoError, RepoLookupError, Abort
9 from mercurial.hgweb.common import get_contact
9 from mercurial.hgweb.common import get_contact
10 from mercurial.localrepo import localrepository
10 from mercurial.localrepo import localrepository
11 from mercurial.match import match
11 from mercurial.match import match
12 from mercurial.mdiff import diffopts
12 from mercurial.mdiff import diffopts
13 from mercurial.node import hex
13 from mercurial.node import hex
14 from mercurial.encoding import tolocal
14 from mercurial.encoding import tolocal
15 from mercurial import discovery
15 from mercurial import discovery
16 from mercurial import localrepo
16 from mercurial import localrepo
17 from mercurial import scmutil
17 from mercurial import scmutil
18 from mercurial.discovery import findcommonoutgoing No newline at end of file
18 from mercurial.discovery import findcommonoutgoing
@@ -1,343 +1,343 b''
1 """ this is forms validation classes
1 """ this is forms validation classes
2 http://formencode.org/module-formencode.validators.html
2 http://formencode.org/module-formencode.validators.html
3 for list off all availible validators
3 for list off all availible validators
4
4
5 we can create our own validators
5 we can create our own validators
6
6
7 The table below outlines the options which can be used in a schema in addition to the validators themselves
7 The table below outlines the options which can be used in a schema in addition to the validators themselves
8 pre_validators [] These validators will be applied before the schema
8 pre_validators [] These validators will be applied before the schema
9 chained_validators [] These validators will be applied after the schema
9 chained_validators [] These validators will be applied after the schema
10 allow_extra_fields False If True, then it is not an error when keys that aren't associated with a validator are present
10 allow_extra_fields False If True, then it is not an error when keys that aren't associated with a validator are present
11 filter_extra_fields False If True, then keys that aren't associated with a validator are removed
11 filter_extra_fields False If True, then keys that aren't associated with a validator are removed
12 if_key_missing NoDefault If this is given, then any keys that aren't available but are expected will be replaced with this value (and then validated). This does not override a present .if_missing attribute on validators. NoDefault is a special FormEncode class to mean that no default values has been specified and therefore missing keys shouldn't take a default value.
12 if_key_missing NoDefault If this is given, then any keys that aren't available but are expected will be replaced with this value (and then validated). This does not override a present .if_missing attribute on validators. NoDefault is a special FormEncode class to mean that no default values has been specified and therefore missing keys shouldn't take a default value.
13 ignore_key_missing False If True, then missing keys will be missing in the result, if the validator doesn't have .if_missing on it already
13 ignore_key_missing False If True, then missing keys will be missing in the result, if the validator doesn't have .if_missing on it already
14
14
15
15
16 <name> = formencode.validators.<name of validator>
16 <name> = formencode.validators.<name of validator>
17 <name> must equal form name
17 <name> must equal form name
18 list=[1,2,3,4,5]
18 list=[1,2,3,4,5]
19 for SELECT use formencode.All(OneOf(list), Int())
19 for SELECT use formencode.All(OneOf(list), Int())
20
20
21 """
21 """
22 import logging
22 import logging
23
23
24 import formencode
24 import formencode
25 from formencode import All
25 from formencode import All
26
26
27 from pylons.i18n.translation import _
27 from pylons.i18n.translation import _
28
28
29 from rhodecode.model import validators as v
29 from rhodecode.model import validators as v
30 from rhodecode import BACKENDS
30 from rhodecode import BACKENDS
31
31
32 log = logging.getLogger(__name__)
32 log = logging.getLogger(__name__)
33
33
34
34
35 class LoginForm(formencode.Schema):
35 class LoginForm(formencode.Schema):
36 allow_extra_fields = True
36 allow_extra_fields = True
37 filter_extra_fields = True
37 filter_extra_fields = True
38 username = v.UnicodeString(
38 username = v.UnicodeString(
39 strip=True,
39 strip=True,
40 min=1,
40 min=1,
41 not_empty=True,
41 not_empty=True,
42 messages={
42 messages={
43 'empty': _(u'Please enter a login'),
43 'empty': _(u'Please enter a login'),
44 'tooShort': _(u'Enter a value %(min)i characters long or more')}
44 'tooShort': _(u'Enter a value %(min)i characters long or more')}
45 )
45 )
46
46
47 password = v.UnicodeString(
47 password = v.UnicodeString(
48 strip=False,
48 strip=False,
49 min=3,
49 min=3,
50 not_empty=True,
50 not_empty=True,
51 messages={
51 messages={
52 'empty': _(u'Please enter a password'),
52 'empty': _(u'Please enter a password'),
53 'tooShort': _(u'Enter %(min)i characters or more')}
53 'tooShort': _(u'Enter %(min)i characters or more')}
54 )
54 )
55
55
56 remember = v.StringBoolean(if_missing=False)
56 remember = v.StringBoolean(if_missing=False)
57
57
58 chained_validators = [v.ValidAuth()]
58 chained_validators = [v.ValidAuth()]
59
59
60
60
61 def UserForm(edit=False, old_data={}):
61 def UserForm(edit=False, old_data={}):
62 class _UserForm(formencode.Schema):
62 class _UserForm(formencode.Schema):
63 allow_extra_fields = True
63 allow_extra_fields = True
64 filter_extra_fields = True
64 filter_extra_fields = True
65 username = All(v.UnicodeString(strip=True, min=1, not_empty=True),
65 username = All(v.UnicodeString(strip=True, min=1, not_empty=True),
66 v.ValidUsername(edit, old_data))
66 v.ValidUsername(edit, old_data))
67 if edit:
67 if edit:
68 new_password = All(
68 new_password = All(
69 v.ValidPassword(),
69 v.ValidPassword(),
70 v.UnicodeString(strip=False, min=6, not_empty=False)
70 v.UnicodeString(strip=False, min=6, not_empty=False)
71 )
71 )
72 password_confirmation = All(
72 password_confirmation = All(
73 v.ValidPassword(),
73 v.ValidPassword(),
74 v.UnicodeString(strip=False, min=6, not_empty=False),
74 v.UnicodeString(strip=False, min=6, not_empty=False),
75 )
75 )
76 admin = v.StringBoolean(if_missing=False)
76 admin = v.StringBoolean(if_missing=False)
77 else:
77 else:
78 password = All(
78 password = All(
79 v.ValidPassword(),
79 v.ValidPassword(),
80 v.UnicodeString(strip=False, min=6, not_empty=True)
80 v.UnicodeString(strip=False, min=6, not_empty=True)
81 )
81 )
82 password_confirmation = All(
82 password_confirmation = All(
83 v.ValidPassword(),
83 v.ValidPassword(),
84 v.UnicodeString(strip=False, min=6, not_empty=False)
84 v.UnicodeString(strip=False, min=6, not_empty=False)
85 )
85 )
86
86
87 active = v.StringBoolean(if_missing=False)
87 active = v.StringBoolean(if_missing=False)
88 firstname = v.UnicodeString(strip=True, min=1, not_empty=False)
88 firstname = v.UnicodeString(strip=True, min=1, not_empty=False)
89 lastname = v.UnicodeString(strip=True, min=1, not_empty=False)
89 lastname = v.UnicodeString(strip=True, min=1, not_empty=False)
90 email = All(v.Email(not_empty=True), v.UniqSystemEmail(old_data))
90 email = All(v.Email(not_empty=True), v.UniqSystemEmail(old_data))
91
91
92 chained_validators = [v.ValidPasswordsMatch()]
92 chained_validators = [v.ValidPasswordsMatch()]
93
93
94 return _UserForm
94 return _UserForm
95
95
96
96
97 def UsersGroupForm(edit=False, old_data={}, available_members=[]):
97 def UsersGroupForm(edit=False, old_data={}, available_members=[]):
98 class _UsersGroupForm(formencode.Schema):
98 class _UsersGroupForm(formencode.Schema):
99 allow_extra_fields = True
99 allow_extra_fields = True
100 filter_extra_fields = True
100 filter_extra_fields = True
101
101
102 users_group_name = All(
102 users_group_name = All(
103 v.UnicodeString(strip=True, min=1, not_empty=True),
103 v.UnicodeString(strip=True, min=1, not_empty=True),
104 v.ValidUsersGroup(edit, old_data)
104 v.ValidUsersGroup(edit, old_data)
105 )
105 )
106
106
107 users_group_active = v.StringBoolean(if_missing=False)
107 users_group_active = v.StringBoolean(if_missing=False)
108
108
109 if edit:
109 if edit:
110 users_group_members = v.OneOf(
110 users_group_members = v.OneOf(
111 available_members, hideList=False, testValueList=True,
111 available_members, hideList=False, testValueList=True,
112 if_missing=None, not_empty=False
112 if_missing=None, not_empty=False
113 )
113 )
114
114
115 return _UsersGroupForm
115 return _UsersGroupForm
116
116
117
117
118 def ReposGroupForm(edit=False, old_data={}, available_groups=[]):
118 def ReposGroupForm(edit=False, old_data={}, available_groups=[]):
119 class _ReposGroupForm(formencode.Schema):
119 class _ReposGroupForm(formencode.Schema):
120 allow_extra_fields = True
120 allow_extra_fields = True
121 filter_extra_fields = False
121 filter_extra_fields = False
122
122
123 group_name = All(v.UnicodeString(strip=True, min=1, not_empty=True),
123 group_name = All(v.UnicodeString(strip=True, min=1, not_empty=True),
124 v.SlugifyName())
124 v.SlugifyName())
125 group_description = v.UnicodeString(strip=True, min=1,
125 group_description = v.UnicodeString(strip=True, min=1,
126 not_empty=True)
126 not_empty=True)
127 group_parent_id = v.OneOf(available_groups, hideList=False,
127 group_parent_id = v.OneOf(available_groups, hideList=False,
128 testValueList=True,
128 testValueList=True,
129 if_missing=None, not_empty=False)
129 if_missing=None, not_empty=False)
130 enable_locking = v.StringBoolean(if_missing=False)
130 enable_locking = v.StringBoolean(if_missing=False)
131 chained_validators = [v.ValidReposGroup(edit, old_data),
131 chained_validators = [v.ValidReposGroup(edit, old_data),
132 v.ValidPerms('group')]
132 v.ValidPerms('group')]
133
133
134 return _ReposGroupForm
134 return _ReposGroupForm
135
135
136
136
137 def RegisterForm(edit=False, old_data={}):
137 def RegisterForm(edit=False, old_data={}):
138 class _RegisterForm(formencode.Schema):
138 class _RegisterForm(formencode.Schema):
139 allow_extra_fields = True
139 allow_extra_fields = True
140 filter_extra_fields = True
140 filter_extra_fields = True
141 username = All(
141 username = All(
142 v.ValidUsername(edit, old_data),
142 v.ValidUsername(edit, old_data),
143 v.UnicodeString(strip=True, min=1, not_empty=True)
143 v.UnicodeString(strip=True, min=1, not_empty=True)
144 )
144 )
145 password = All(
145 password = All(
146 v.ValidPassword(),
146 v.ValidPassword(),
147 v.UnicodeString(strip=False, min=6, not_empty=True)
147 v.UnicodeString(strip=False, min=6, not_empty=True)
148 )
148 )
149 password_confirmation = All(
149 password_confirmation = All(
150 v.ValidPassword(),
150 v.ValidPassword(),
151 v.UnicodeString(strip=False, min=6, not_empty=True)
151 v.UnicodeString(strip=False, min=6, not_empty=True)
152 )
152 )
153 active = v.StringBoolean(if_missing=False)
153 active = v.StringBoolean(if_missing=False)
154 firstname = v.UnicodeString(strip=True, min=1, not_empty=False)
154 firstname = v.UnicodeString(strip=True, min=1, not_empty=False)
155 lastname = v.UnicodeString(strip=True, min=1, not_empty=False)
155 lastname = v.UnicodeString(strip=True, min=1, not_empty=False)
156 email = All(v.Email(not_empty=True), v.UniqSystemEmail(old_data))
156 email = All(v.Email(not_empty=True), v.UniqSystemEmail(old_data))
157
157
158 chained_validators = [v.ValidPasswordsMatch()]
158 chained_validators = [v.ValidPasswordsMatch()]
159
159
160 return _RegisterForm
160 return _RegisterForm
161
161
162
162
163 def PasswordResetForm():
163 def PasswordResetForm():
164 class _PasswordResetForm(formencode.Schema):
164 class _PasswordResetForm(formencode.Schema):
165 allow_extra_fields = True
165 allow_extra_fields = True
166 filter_extra_fields = True
166 filter_extra_fields = True
167 email = All(v.ValidSystemEmail(), v.Email(not_empty=True))
167 email = All(v.ValidSystemEmail(), v.Email(not_empty=True))
168 return _PasswordResetForm
168 return _PasswordResetForm
169
169
170
170
171 def RepoForm(edit=False, old_data={}, supported_backends=BACKENDS.keys(),
171 def RepoForm(edit=False, old_data={}, supported_backends=BACKENDS.keys(),
172 repo_groups=[], landing_revs=[]):
172 repo_groups=[], landing_revs=[]):
173 class _RepoForm(formencode.Schema):
173 class _RepoForm(formencode.Schema):
174 allow_extra_fields = True
174 allow_extra_fields = True
175 filter_extra_fields = False
175 filter_extra_fields = False
176 repo_name = All(v.UnicodeString(strip=True, min=1, not_empty=True),
176 repo_name = All(v.UnicodeString(strip=True, min=1, not_empty=True),
177 v.SlugifyName())
177 v.SlugifyName())
178 clone_uri = All(v.UnicodeString(strip=True, min=1, not_empty=False))
178 clone_uri = All(v.UnicodeString(strip=True, min=1, not_empty=False))
179 repo_group = v.OneOf(repo_groups, hideList=True)
179 repo_group = v.OneOf(repo_groups, hideList=True)
180 repo_type = v.OneOf(supported_backends)
180 repo_type = v.OneOf(supported_backends)
181 description = v.UnicodeString(strip=True, min=1, not_empty=False)
181 description = v.UnicodeString(strip=True, min=1, not_empty=False)
182 private = v.StringBoolean(if_missing=False)
182 private = v.StringBoolean(if_missing=False)
183 enable_statistics = v.StringBoolean(if_missing=False)
183 enable_statistics = v.StringBoolean(if_missing=False)
184 enable_downloads = v.StringBoolean(if_missing=False)
184 enable_downloads = v.StringBoolean(if_missing=False)
185 enable_locking = v.StringBoolean(if_missing=False)
185 enable_locking = v.StringBoolean(if_missing=False)
186 landing_rev = v.OneOf(landing_revs, hideList=True)
186 landing_rev = v.OneOf(landing_revs, hideList=True)
187
187
188 if edit:
188 if edit:
189 #this is repo owner
189 #this is repo owner
190 user = All(v.UnicodeString(not_empty=True), v.ValidRepoUser())
190 user = All(v.UnicodeString(not_empty=True), v.ValidRepoUser())
191
191
192 chained_validators = [v.ValidCloneUri(),
192 chained_validators = [v.ValidCloneUri(),
193 v.ValidRepoName(edit, old_data),
193 v.ValidRepoName(edit, old_data),
194 v.ValidPerms()]
194 v.ValidPerms()]
195 return _RepoForm
195 return _RepoForm
196
196
197
197
198 def RepoForkForm(edit=False, old_data={}, supported_backends=BACKENDS.keys(),
198 def RepoForkForm(edit=False, old_data={}, supported_backends=BACKENDS.keys(),
199 repo_groups=[], landing_revs=[]):
199 repo_groups=[], landing_revs=[]):
200 class _RepoForkForm(formencode.Schema):
200 class _RepoForkForm(formencode.Schema):
201 allow_extra_fields = True
201 allow_extra_fields = True
202 filter_extra_fields = False
202 filter_extra_fields = False
203 repo_name = All(v.UnicodeString(strip=True, min=1, not_empty=True),
203 repo_name = All(v.UnicodeString(strip=True, min=1, not_empty=True),
204 v.SlugifyName())
204 v.SlugifyName())
205 repo_group = v.OneOf(repo_groups, hideList=True)
205 repo_group = v.OneOf(repo_groups, hideList=True)
206 repo_type = All(v.ValidForkType(old_data), v.OneOf(supported_backends))
206 repo_type = All(v.ValidForkType(old_data), v.OneOf(supported_backends))
207 description = v.UnicodeString(strip=True, min=1, not_empty=True)
207 description = v.UnicodeString(strip=True, min=1, not_empty=True)
208 private = v.StringBoolean(if_missing=False)
208 private = v.StringBoolean(if_missing=False)
209 copy_permissions = v.StringBoolean(if_missing=False)
209 copy_permissions = v.StringBoolean(if_missing=False)
210 update_after_clone = v.StringBoolean(if_missing=False)
210 update_after_clone = v.StringBoolean(if_missing=False)
211 fork_parent_id = v.UnicodeString()
211 fork_parent_id = v.UnicodeString()
212 chained_validators = [v.ValidForkName(edit, old_data)]
212 chained_validators = [v.ValidForkName(edit, old_data)]
213 landing_rev = v.OneOf(landing_revs, hideList=True)
213 landing_rev = v.OneOf(landing_revs, hideList=True)
214
214
215 return _RepoForkForm
215 return _RepoForkForm
216
216
217
217
218 def RepoSettingsForm(edit=False, old_data={},
218 def RepoSettingsForm(edit=False, old_data={},
219 supported_backends=BACKENDS.keys(), repo_groups=[],
219 supported_backends=BACKENDS.keys(), repo_groups=[],
220 landing_revs=[]):
220 landing_revs=[]):
221 class _RepoForm(formencode.Schema):
221 class _RepoForm(formencode.Schema):
222 allow_extra_fields = True
222 allow_extra_fields = True
223 filter_extra_fields = False
223 filter_extra_fields = False
224 repo_name = All(v.UnicodeString(strip=True, min=1, not_empty=True),
224 repo_name = All(v.UnicodeString(strip=True, min=1, not_empty=True),
225 v.SlugifyName())
225 v.SlugifyName())
226 description = v.UnicodeString(strip=True, min=1, not_empty=True)
226 description = v.UnicodeString(strip=True, min=1, not_empty=True)
227 repo_group = v.OneOf(repo_groups, hideList=True)
227 repo_group = v.OneOf(repo_groups, hideList=True)
228 private = v.StringBoolean(if_missing=False)
228 private = v.StringBoolean(if_missing=False)
229 landing_rev = v.OneOf(landing_revs, hideList=True)
229 landing_rev = v.OneOf(landing_revs, hideList=True)
230 chained_validators = [v.ValidRepoName(edit, old_data), v.ValidPerms(),
230 chained_validators = [v.ValidRepoName(edit, old_data), v.ValidPerms(),
231 v.ValidSettings()]
231 v.ValidSettings()]
232 return _RepoForm
232 return _RepoForm
233
233
234
234
235 def ApplicationSettingsForm():
235 def ApplicationSettingsForm():
236 class _ApplicationSettingsForm(formencode.Schema):
236 class _ApplicationSettingsForm(formencode.Schema):
237 allow_extra_fields = True
237 allow_extra_fields = True
238 filter_extra_fields = False
238 filter_extra_fields = False
239 rhodecode_title = v.UnicodeString(strip=True, min=1, not_empty=True)
239 rhodecode_title = v.UnicodeString(strip=True, min=1, not_empty=True)
240 rhodecode_realm = v.UnicodeString(strip=True, min=1, not_empty=True)
240 rhodecode_realm = v.UnicodeString(strip=True, min=1, not_empty=True)
241 rhodecode_ga_code = v.UnicodeString(strip=True, min=1, not_empty=False)
241 rhodecode_ga_code = v.UnicodeString(strip=True, min=1, not_empty=False)
242
242
243 return _ApplicationSettingsForm
243 return _ApplicationSettingsForm
244
244
245
245
246 def ApplicationVisualisationForm():
246 def ApplicationVisualisationForm():
247 class _ApplicationVisualisationForm(formencode.Schema):
247 class _ApplicationVisualisationForm(formencode.Schema):
248 allow_extra_fields = True
248 allow_extra_fields = True
249 filter_extra_fields = False
249 filter_extra_fields = False
250 rhodecode_show_public_icon = v.StringBoolean(if_missing=False)
250 rhodecode_show_public_icon = v.StringBoolean(if_missing=False)
251 rhodecode_show_private_icon = v.StringBoolean(if_missing=False)
251 rhodecode_show_private_icon = v.StringBoolean(if_missing=False)
252 rhodecode_stylify_metatags = v.StringBoolean(if_missing=False)
252 rhodecode_stylify_metatags = v.StringBoolean(if_missing=False)
253
253
254 return _ApplicationVisualisationForm
254 return _ApplicationVisualisationForm
255
255
256
256
257 def ApplicationUiSettingsForm():
257 def ApplicationUiSettingsForm():
258 class _ApplicationUiSettingsForm(formencode.Schema):
258 class _ApplicationUiSettingsForm(formencode.Schema):
259 allow_extra_fields = True
259 allow_extra_fields = True
260 filter_extra_fields = False
260 filter_extra_fields = False
261 web_push_ssl = v.StringBoolean(if_missing=False)
261 web_push_ssl = v.StringBoolean(if_missing=False)
262 paths_root_path = All(
262 paths_root_path = All(
263 v.ValidPath(),
263 v.ValidPath(),
264 v.UnicodeString(strip=True, min=1, not_empty=True)
264 v.UnicodeString(strip=True, min=1, not_empty=True)
265 )
265 )
266 hooks_changegroup_update = v.StringBoolean(if_missing=False)
266 hooks_changegroup_update = v.StringBoolean(if_missing=False)
267 hooks_changegroup_repo_size = v.StringBoolean(if_missing=False)
267 hooks_changegroup_repo_size = v.StringBoolean(if_missing=False)
268 hooks_changegroup_push_logger = v.StringBoolean(if_missing=False)
268 hooks_changegroup_push_logger = v.StringBoolean(if_missing=False)
269 hooks_outgoing_pull_logger = v.StringBoolean(if_missing=False)
269 hooks_outgoing_pull_logger = v.StringBoolean(if_missing=False)
270
270
271 extensions_largefiles = v.StringBoolean(if_missing=False)
271 extensions_largefiles = v.StringBoolean(if_missing=False)
272 extensions_hgsubversion = v.StringBoolean(if_missing=False)
272 extensions_hgsubversion = v.StringBoolean(if_missing=False)
273 extensions_hggit = v.StringBoolean(if_missing=False)
273 extensions_hggit = v.StringBoolean(if_missing=False)
274
274
275 return _ApplicationUiSettingsForm
275 return _ApplicationUiSettingsForm
276
276
277
277
278 def DefaultPermissionsForm(perms_choices, register_choices, create_choices,
278 def DefaultPermissionsForm(perms_choices, register_choices, create_choices,
279 fork_choices):
279 fork_choices):
280 class _DefaultPermissionsForm(formencode.Schema):
280 class _DefaultPermissionsForm(formencode.Schema):
281 allow_extra_fields = True
281 allow_extra_fields = True
282 filter_extra_fields = True
282 filter_extra_fields = True
283 overwrite_default = v.StringBoolean(if_missing=False)
283 overwrite_default = v.StringBoolean(if_missing=False)
284 anonymous = v.StringBoolean(if_missing=False)
284 anonymous = v.StringBoolean(if_missing=False)
285 default_perm = v.OneOf(perms_choices)
285 default_perm = v.OneOf(perms_choices)
286 default_register = v.OneOf(register_choices)
286 default_register = v.OneOf(register_choices)
287 default_create = v.OneOf(create_choices)
287 default_create = v.OneOf(create_choices)
288 default_fork = v.OneOf(fork_choices)
288 default_fork = v.OneOf(fork_choices)
289
289
290 return _DefaultPermissionsForm
290 return _DefaultPermissionsForm
291
291
292
292
293 def LdapSettingsForm(tls_reqcert_choices, search_scope_choices,
293 def LdapSettingsForm(tls_reqcert_choices, search_scope_choices,
294 tls_kind_choices):
294 tls_kind_choices):
295 class _LdapSettingsForm(formencode.Schema):
295 class _LdapSettingsForm(formencode.Schema):
296 allow_extra_fields = True
296 allow_extra_fields = True
297 filter_extra_fields = True
297 filter_extra_fields = True
298 #pre_validators = [LdapLibValidator]
298 #pre_validators = [LdapLibValidator]
299 ldap_active = v.StringBoolean(if_missing=False)
299 ldap_active = v.StringBoolean(if_missing=False)
300 ldap_host = v.UnicodeString(strip=True,)
300 ldap_host = v.UnicodeString(strip=True,)
301 ldap_port = v.Number(strip=True,)
301 ldap_port = v.Number(strip=True,)
302 ldap_tls_kind = v.OneOf(tls_kind_choices)
302 ldap_tls_kind = v.OneOf(tls_kind_choices)
303 ldap_tls_reqcert = v.OneOf(tls_reqcert_choices)
303 ldap_tls_reqcert = v.OneOf(tls_reqcert_choices)
304 ldap_dn_user = v.UnicodeString(strip=True,)
304 ldap_dn_user = v.UnicodeString(strip=True,)
305 ldap_dn_pass = v.UnicodeString(strip=True,)
305 ldap_dn_pass = v.UnicodeString(strip=True,)
306 ldap_base_dn = v.UnicodeString(strip=True,)
306 ldap_base_dn = v.UnicodeString(strip=True,)
307 ldap_filter = v.UnicodeString(strip=True,)
307 ldap_filter = v.UnicodeString(strip=True,)
308 ldap_search_scope = v.OneOf(search_scope_choices)
308 ldap_search_scope = v.OneOf(search_scope_choices)
309 ldap_attr_login = All(
309 ldap_attr_login = All(
310 v.AttrLoginValidator(),
310 v.AttrLoginValidator(),
311 v.UnicodeString(strip=True,)
311 v.UnicodeString(strip=True,)
312 )
312 )
313 ldap_attr_firstname = v.UnicodeString(strip=True,)
313 ldap_attr_firstname = v.UnicodeString(strip=True,)
314 ldap_attr_lastname = v.UnicodeString(strip=True,)
314 ldap_attr_lastname = v.UnicodeString(strip=True,)
315 ldap_attr_email = v.UnicodeString(strip=True,)
315 ldap_attr_email = v.UnicodeString(strip=True,)
316
316
317 return _LdapSettingsForm
317 return _LdapSettingsForm
318
318
319
319
320 def UserExtraEmailForm():
320 def UserExtraEmailForm():
321 class _UserExtraEmailForm(formencode.Schema):
321 class _UserExtraEmailForm(formencode.Schema):
322 email = All(v.UniqSystemEmail(), v.Email)
322 email = All(v.UniqSystemEmail(), v.Email)
323
323
324 return _UserExtraEmailForm
324 return _UserExtraEmailForm
325
325
326
326
327 def PullRequestForm():
327 def PullRequestForm():
328 class _PullRequestForm(formencode.Schema):
328 class _PullRequestForm(formencode.Schema):
329 allow_extra_fields = True
329 allow_extra_fields = True
330 filter_extra_fields = True
330 filter_extra_fields = True
331
331
332 user = v.UnicodeString(strip=True, required=True)
332 user = v.UnicodeString(strip=True, required=True)
333 org_repo = v.UnicodeString(strip=True, required=True)
333 org_repo = v.UnicodeString(strip=True, required=True)
334 org_ref = v.UnicodeString(strip=True, required=True)
334 org_ref = v.UnicodeString(strip=True, required=True)
335 other_repo = v.UnicodeString(strip=True, required=True)
335 other_repo = v.UnicodeString(strip=True, required=True)
336 other_ref = v.UnicodeString(strip=True, required=True)
336 other_ref = v.UnicodeString(strip=True, required=True)
337 revisions = All(v.NotReviewedRevisions()(), v.UniqueList(not_empty=True))
337 revisions = All(v.NotReviewedRevisions()(), v.UniqueList(not_empty=True))
338 review_members = v.UniqueList(not_empty=True)
338 review_members = v.UniqueList(not_empty=True)
339
339
340 pullrequest_title = v.UnicodeString(strip=True, required=True, min=3)
340 pullrequest_title = v.UnicodeString(strip=True, required=True, min=3)
341 pullrequest_desc = v.UnicodeString(strip=True, required=False)
341 pullrequest_desc = v.UnicodeString(strip=True, required=False)
342
342
343 return _PullRequestForm No newline at end of file
343 return _PullRequestForm
@@ -1,282 +1,282 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2 <%inherit file="/base/base.html"/>
2 <%inherit file="/base/base.html"/>
3
3
4 <%def name="title()">
4 <%def name="title()">
5 ${_('Edit repository')} ${c.repo_info.repo_name} - ${c.rhodecode_name}
5 ${_('Edit repository')} ${c.repo_info.repo_name} - ${c.rhodecode_name}
6 </%def>
6 </%def>
7
7
8 <%def name="breadcrumbs_links()">
8 <%def name="breadcrumbs_links()">
9 ${h.link_to(_('Admin'),h.url('admin_home'))}
9 ${h.link_to(_('Admin'),h.url('admin_home'))}
10 &raquo;
10 &raquo;
11 ${h.link_to(_('Repositories'),h.url('repos'))}
11 ${h.link_to(_('Repositories'),h.url('repos'))}
12 &raquo;
12 &raquo;
13 ${_('edit')} &raquo; ${h.link_to(c.repo_info.just_name,h.url('summary_home',repo_name=c.repo_name))}
13 ${_('edit')} &raquo; ${h.link_to(c.repo_info.just_name,h.url('summary_home',repo_name=c.repo_name))}
14 </%def>
14 </%def>
15
15
16 <%def name="page_nav()">
16 <%def name="page_nav()">
17 ${self.menu('admin')}
17 ${self.menu('admin')}
18 </%def>
18 </%def>
19
19
20 <%def name="main()">
20 <%def name="main()">
21 <div class="box box-left">
21 <div class="box box-left">
22 <!-- box / title -->
22 <!-- box / title -->
23 <div class="title">
23 <div class="title">
24 ${self.breadcrumbs()}
24 ${self.breadcrumbs()}
25 </div>
25 </div>
26 ${h.form(url('repo', repo_name=c.repo_info.repo_name),method='put')}
26 ${h.form(url('repo', repo_name=c.repo_info.repo_name),method='put')}
27 <div class="form">
27 <div class="form">
28 <!-- fields -->
28 <!-- fields -->
29 <div class="fields">
29 <div class="fields">
30 <div class="field">
30 <div class="field">
31 <div class="label">
31 <div class="label">
32 <label for="repo_name">${_('Name')}:</label>
32 <label for="repo_name">${_('Name')}:</label>
33 </div>
33 </div>
34 <div class="input">
34 <div class="input">
35 ${h.text('repo_name',class_="medium")}
35 ${h.text('repo_name',class_="medium")}
36 </div>
36 </div>
37 </div>
37 </div>
38 <div class="field">
38 <div class="field">
39 <div class="label">
39 <div class="label">
40 <label for="clone_uri">${_('Clone uri')}:</label>
40 <label for="clone_uri">${_('Clone uri')}:</label>
41 </div>
41 </div>
42 <div class="input">
42 <div class="input">
43 ${h.text('clone_uri',class_="medium")}
43 ${h.text('clone_uri',class_="medium")}
44 <span class="help-block">${_('Optional http[s] url from which repository should be cloned.')}</span>
44 <span class="help-block">${_('Optional http[s] url from which repository should be cloned.')}</span>
45 </div>
45 </div>
46 </div>
46 </div>
47 <div class="field">
47 <div class="field">
48 <div class="label">
48 <div class="label">
49 <label for="repo_group">${_('Repository group')}:</label>
49 <label for="repo_group">${_('Repository group')}:</label>
50 </div>
50 </div>
51 <div class="input">
51 <div class="input">
52 ${h.select('repo_group','',c.repo_groups,class_="medium")}
52 ${h.select('repo_group','',c.repo_groups,class_="medium")}
53 <span class="help-block">${_('Optional select a group to put this repository into.')}</span>
53 <span class="help-block">${_('Optional select a group to put this repository into.')}</span>
54 </div>
54 </div>
55 </div>
55 </div>
56 <div class="field">
56 <div class="field">
57 <div class="label">
57 <div class="label">
58 <label for="repo_type">${_('Type')}:</label>
58 <label for="repo_type">${_('Type')}:</label>
59 </div>
59 </div>
60 <div class="input">
60 <div class="input">
61 ${h.select('repo_type','hg',c.backends,class_="medium")}
61 ${h.select('repo_type','hg',c.backends,class_="medium")}
62 </div>
62 </div>
63 </div>
63 </div>
64 <div class="field">
64 <div class="field">
65 <div class="label">
65 <div class="label">
66 <label for="landing_rev">${_('Landing revision')}:</label>
66 <label for="landing_rev">${_('Landing revision')}:</label>
67 </div>
67 </div>
68 <div class="input">
68 <div class="input">
69 ${h.select('landing_rev','',c.landing_revs,class_="medium")}
69 ${h.select('landing_rev','',c.landing_revs,class_="medium")}
70 <span class="help-block">${_('Default revision for files page, downloads, whoosh and readme')}</span>
70 <span class="help-block">${_('Default revision for files page, downloads, whoosh and readme')}</span>
71 </div>
71 </div>
72 </div>
72 </div>
73 <div class="field">
73 <div class="field">
74 <div class="label label-textarea">
74 <div class="label label-textarea">
75 <label for="description">${_('Description')}:</label>
75 <label for="description">${_('Description')}:</label>
76 </div>
76 </div>
77 <div class="textarea text-area editor">
77 <div class="textarea text-area editor">
78 ${h.textarea('description')}
78 ${h.textarea('description')}
79 <span class="help-block">${_('Keep it short and to the point. Use a README file for longer descriptions.')}</span>
79 <span class="help-block">${_('Keep it short and to the point. Use a README file for longer descriptions.')}</span>
80 </div>
80 </div>
81 </div>
81 </div>
82
82
83 <div class="field">
83 <div class="field">
84 <div class="label label-checkbox">
84 <div class="label label-checkbox">
85 <label for="private">${_('Private repository')}:</label>
85 <label for="private">${_('Private repository')}:</label>
86 </div>
86 </div>
87 <div class="checkboxes">
87 <div class="checkboxes">
88 ${h.checkbox('private',value="True")}
88 ${h.checkbox('private',value="True")}
89 <span class="help-block">${_('Private repositories are only visible to people explicitly added as collaborators.')}</span>
89 <span class="help-block">${_('Private repositories are only visible to people explicitly added as collaborators.')}</span>
90 </div>
90 </div>
91 </div>
91 </div>
92 <div class="field">
92 <div class="field">
93 <div class="label label-checkbox">
93 <div class="label label-checkbox">
94 <label for="enable_statistics">${_('Enable statistics')}:</label>
94 <label for="enable_statistics">${_('Enable statistics')}:</label>
95 </div>
95 </div>
96 <div class="checkboxes">
96 <div class="checkboxes">
97 ${h.checkbox('enable_statistics',value="True")}
97 ${h.checkbox('enable_statistics',value="True")}
98 <span class="help-block">${_('Enable statistics window on summary page.')}</span>
98 <span class="help-block">${_('Enable statistics window on summary page.')}</span>
99 </div>
99 </div>
100 </div>
100 </div>
101 <div class="field">
101 <div class="field">
102 <div class="label label-checkbox">
102 <div class="label label-checkbox">
103 <label for="enable_downloads">${_('Enable downloads')}:</label>
103 <label for="enable_downloads">${_('Enable downloads')}:</label>
104 </div>
104 </div>
105 <div class="checkboxes">
105 <div class="checkboxes">
106 ${h.checkbox('enable_downloads',value="True")}
106 ${h.checkbox('enable_downloads',value="True")}
107 <span class="help-block">${_('Enable download menu on summary page.')}</span>
107 <span class="help-block">${_('Enable download menu on summary page.')}</span>
108 </div>
108 </div>
109 </div>
109 </div>
110 <div class="field">
110 <div class="field">
111 <div class="label label-checkbox">
111 <div class="label label-checkbox">
112 <label for="enable_locking">${_('Enable locking')}:</label>
112 <label for="enable_locking">${_('Enable locking')}:</label>
113 </div>
113 </div>
114 <div class="checkboxes">
114 <div class="checkboxes">
115 ${h.checkbox('enable_locking',value="True")}
115 ${h.checkbox('enable_locking',value="True")}
116 <span class="help-block">${_('Enable lock-by-pulling on repository.')}</span>
116 <span class="help-block">${_('Enable lock-by-pulling on repository.')}</span>
117 </div>
117 </div>
118 </div>
118 </div>
119 <div class="field">
119 <div class="field">
120 <div class="label">
120 <div class="label">
121 <label for="user">${_('Owner')}:</label>
121 <label for="user">${_('Owner')}:</label>
122 </div>
122 </div>
123 <div class="input input-medium ac">
123 <div class="input input-medium ac">
124 <div class="perm_ac">
124 <div class="perm_ac">
125 ${h.text('user',class_='yui-ac-input')}
125 ${h.text('user',class_='yui-ac-input')}
126 <span class="help-block">${_('Change owner of this repository.')}</span>
126 <span class="help-block">${_('Change owner of this repository.')}</span>
127 <div id="owner_container"></div>
127 <div id="owner_container"></div>
128 </div>
128 </div>
129 </div>
129 </div>
130 </div>
130 </div>
131
131
132 <div class="field">
132 <div class="field">
133 <div class="label">
133 <div class="label">
134 <label for="input">${_('Permissions')}:</label>
134 <label for="input">${_('Permissions')}:</label>
135 </div>
135 </div>
136 <div class="input">
136 <div class="input">
137 <%include file="repo_edit_perms.html"/>
137 <%include file="repo_edit_perms.html"/>
138 </div>
138 </div>
139
139
140 <div class="buttons">
140 <div class="buttons">
141 ${h.submit('save',_('Save'),class_="ui-btn large")}
141 ${h.submit('save',_('Save'),class_="ui-btn large")}
142 ${h.reset('reset',_('Reset'),class_="ui-btn large")}
142 ${h.reset('reset',_('Reset'),class_="ui-btn large")}
143 </div>
143 </div>
144 </div>
144 </div>
145 </div>
145 </div>
146 </div>
146 </div>
147 ${h.end_form()}
147 ${h.end_form()}
148 </div>
148 </div>
149
149
150 <div class="box box-right">
150 <div class="box box-right">
151 <div class="title">
151 <div class="title">
152 <h5>${_('Administration')}</h5>
152 <h5>${_('Administration')}</h5>
153 </div>
153 </div>
154
154
155 <h3>${_('Statistics')}</h3>
155 <h3>${_('Statistics')}</h3>
156 ${h.form(url('repo_stats', repo_name=c.repo_info.repo_name),method='delete')}
156 ${h.form(url('repo_stats', repo_name=c.repo_info.repo_name),method='delete')}
157 <div class="form">
157 <div class="form">
158 <div class="fields">
158 <div class="fields">
159 ${h.submit('reset_stats_%s' % c.repo_info.repo_name,_('Reset current statistics'),class_="ui-btn",onclick="return confirm('"+_('Confirm to remove current statistics')+"');")}
159 ${h.submit('reset_stats_%s' % c.repo_info.repo_name,_('Reset current statistics'),class_="ui-btn",onclick="return confirm('"+_('Confirm to remove current statistics')+"');")}
160 <div class="field" style="border:none;color:#888">
160 <div class="field" style="border:none;color:#888">
161 <ul>
161 <ul>
162 <li>${_('Fetched to rev')}: ${c.stats_revision}/${c.repo_last_rev}</li>
162 <li>${_('Fetched to rev')}: ${c.stats_revision}/${c.repo_last_rev}</li>
163 <li>${_('Stats gathered')}: ${c.stats_percentage}%</li>
163 <li>${_('Stats gathered')}: ${c.stats_percentage}%</li>
164 </ul>
164 </ul>
165 </div>
165 </div>
166 </div>
166 </div>
167 </div>
167 </div>
168 ${h.end_form()}
168 ${h.end_form()}
169
169
170 %if c.repo_info.clone_uri:
170 %if c.repo_info.clone_uri:
171 <h3>${_('Remote')}</h3>
171 <h3>${_('Remote')}</h3>
172 ${h.form(url('repo_pull', repo_name=c.repo_info.repo_name),method='put')}
172 ${h.form(url('repo_pull', repo_name=c.repo_info.repo_name),method='put')}
173 <div class="form">
173 <div class="form">
174 <div class="fields">
174 <div class="fields">
175 ${h.submit('remote_pull_%s' % c.repo_info.repo_name,_('Pull changes from remote location'),class_="ui-btn",onclick="return confirm('"+_('Confirm to pull changes from remote side')+"');")}
175 ${h.submit('remote_pull_%s' % c.repo_info.repo_name,_('Pull changes from remote location'),class_="ui-btn",onclick="return confirm('"+_('Confirm to pull changes from remote side')+"');")}
176 <div class="field" style="border:none">
176 <div class="field" style="border:none">
177 <ul>
177 <ul>
178 <li><a href="${c.repo_info.clone_uri}">${c.repo_info.clone_uri}</a></li>
178 <li><a href="${c.repo_info.clone_uri}">${c.repo_info.clone_uri}</a></li>
179 </ul>
179 </ul>
180 </div>
180 </div>
181 </div>
181 </div>
182 </div>
182 </div>
183 ${h.end_form()}
183 ${h.end_form()}
184 %endif
184 %endif
185
185
186 <h3>${_('Cache')}</h3>
186 <h3>${_('Cache')}</h3>
187 ${h.form(url('repo_cache', repo_name=c.repo_info.repo_name),method='delete')}
187 ${h.form(url('repo_cache', repo_name=c.repo_info.repo_name),method='delete')}
188 <div class="form">
188 <div class="form">
189 <div class="fields">
189 <div class="fields">
190 ${h.submit('reset_cache_%s' % c.repo_info.repo_name,_('Invalidate repository cache'),class_="ui-btn",onclick="return confirm('"+_('Confirm to invalidate repository cache')+"');")}
190 ${h.submit('reset_cache_%s' % c.repo_info.repo_name,_('Invalidate repository cache'),class_="ui-btn",onclick="return confirm('"+_('Confirm to invalidate repository cache')+"');")}
191 <div class="field" style="border:none;color:#888">
191 <div class="field" style="border:none;color:#888">
192 <ul>
192 <ul>
193 <li>${_('Manually invalidate cache for this repository. On first access repository will be cached again')}
193 <li>${_('Manually invalidate cache for this repository. On first access repository will be cached again')}
194 </li>
194 </li>
195 </ul>
195 </ul>
196 </div>
196 </div>
197 <div class="field" style="border:none;">
197 <div class="field" style="border:none;">
198 ${_('List of cached values')}
198 ${_('List of cached values')}
199 <ul>
199 <ul>
200 %for cache in c.repo_info.cache_keys:
200 %for cache in c.repo_info.cache_keys:
201 <li>INSTANCE ID:${cache.prefix or '-'} ${cache.cache_args} CACHED: ${h.bool2icon(cache.cache_active)}</li>
201 <li>INSTANCE ID:${cache.prefix or '-'} ${cache.cache_args} CACHED: ${h.bool2icon(cache.cache_active)}</li>
202 %endfor
202 %endfor
203 </ul>
203 </ul>
204 </div>
204 </div>
205 </div>
205 </div>
206 </div>
206 </div>
207 ${h.end_form()}
207 ${h.end_form()}
208
208
209 <h3>${_('Public journal')}</h3>
209 <h3>${_('Public journal')}</h3>
210 ${h.form(url('repo_public_journal', repo_name=c.repo_info.repo_name),method='put')}
210 ${h.form(url('repo_public_journal', repo_name=c.repo_info.repo_name),method='put')}
211 <div class="form">
211 <div class="form">
212 ${h.hidden('auth_token',str(h.get_token()))}
212 ${h.hidden('auth_token',str(h.get_token()))}
213 <div class="field">
213 <div class="field">
214 %if c.in_public_journal:
214 %if c.in_public_journal:
215 ${h.submit('set_public_%s' % c.repo_info.repo_name,_('Remove from public journal'),class_="ui-btn")}
215 ${h.submit('set_public_%s' % c.repo_info.repo_name,_('Remove from public journal'),class_="ui-btn")}
216 %else:
216 %else:
217 ${h.submit('set_public_%s' % c.repo_info.repo_name,_('Add to public journal'),class_="ui-btn")}
217 ${h.submit('set_public_%s' % c.repo_info.repo_name,_('Add to public journal'),class_="ui-btn")}
218 %endif
218 %endif
219 </div>
219 </div>
220 <div class="field" style="border:none;color:#888">
220 <div class="field" style="border:none;color:#888">
221 <ul>
221 <ul>
222 <li>${_('All actions made on this repository will be accessible to everyone in public journal')}
222 <li>${_('All actions made on this repository will be accessible to everyone in public journal')}
223 </li>
223 </li>
224 </ul>
224 </ul>
225 </div>
225 </div>
226 </div>
226 </div>
227 ${h.end_form()}
227 ${h.end_form()}
228
228
229 <h3>${_('Locking')}</h3>
229 <h3>${_('Locking')}</h3>
230 ${h.form(url('repo_locking', repo_name=c.repo_info.repo_name),method='put')}
230 ${h.form(url('repo_locking', repo_name=c.repo_info.repo_name),method='put')}
231 <div class="form">
231 <div class="form">
232 <div class="fields">
232 <div class="fields">
233 %if c.repo_info.locked[0]:
233 %if c.repo_info.locked[0]:
234 ${h.submit('set_unlock' ,_('Unlock locked repo'),class_="ui-btn",onclick="return confirm('"+_('Confirm to unlock repository')+"');")}
234 ${h.submit('set_unlock' ,_('Unlock locked repo'),class_="ui-btn",onclick="return confirm('"+_('Confirm to unlock repository')+"');")}
235 ${'Locked by %s on %s' % (h.person_by_id(c.repo_info.locked[0]),h.fmt_date(h.time_to_datetime(c.repo_info.locked[1])))}
235 ${'Locked by %s on %s' % (h.person_by_id(c.repo_info.locked[0]),h.fmt_date(h.time_to_datetime(c.repo_info.locked[1])))}
236 %else:
236 %else:
237 ${h.submit('set_lock',_('lock repo'),class_="ui-btn",onclick="return confirm('"+_('Confirm to lock repository')+"');")}
237 ${h.submit('set_lock',_('lock repo'),class_="ui-btn",onclick="return confirm('"+_('Confirm to lock repository')+"');")}
238 ${_('Repository is not locked')}
238 ${_('Repository is not locked')}
239 %endif
239 %endif
240 </div>
240 </div>
241 <div class="field" style="border:none;color:#888">
241 <div class="field" style="border:none;color:#888">
242 <ul>
242 <ul>
243 <li>${_('Force locking on repository. Works only when anonymous access is disabled')}
243 <li>${_('Force locking on repository. Works only when anonymous access is disabled')}
244 </li>
244 </li>
245 </ul>
245 </ul>
246 </div>
246 </div>
247 </div>
247 </div>
248 ${h.end_form()}
248 ${h.end_form()}
249
249
250 <h3>${_('Set as fork of')}</h3>
250 <h3>${_('Set as fork of')}</h3>
251 ${h.form(url('repo_as_fork', repo_name=c.repo_info.repo_name),method='put')}
251 ${h.form(url('repo_as_fork', repo_name=c.repo_info.repo_name),method='put')}
252 <div class="form">
252 <div class="form">
253 <div class="fields">
253 <div class="fields">
254 ${h.select('id_fork_of','',c.repos_list,class_="medium")}
254 ${h.select('id_fork_of','',c.repos_list,class_="medium")}
255 ${h.submit('set_as_fork_%s' % c.repo_info.repo_name,_('set'),class_="ui-btn",)}
255 ${h.submit('set_as_fork_%s' % c.repo_info.repo_name,_('set'),class_="ui-btn",)}
256 </div>
256 </div>
257 <div class="field" style="border:none;color:#888">
257 <div class="field" style="border:none;color:#888">
258 <ul>
258 <ul>
259 <li>${_('''Manually set this repository as a fork of another from the list''')}</li>
259 <li>${_('''Manually set this repository as a fork of another from the list''')}</li>
260 </ul>
260 </ul>
261 </div>
261 </div>
262 </div>
262 </div>
263 ${h.end_form()}
263 ${h.end_form()}
264
264
265 <h3>${_('Delete')}</h3>
265 <h3>${_('Delete')}</h3>
266 ${h.form(url('repo', repo_name=c.repo_info.repo_name),method='delete')}
266 ${h.form(url('repo', repo_name=c.repo_info.repo_name),method='delete')}
267 <div class="form">
267 <div class="form">
268 <div class="fields">
268 <div class="fields">
269 ${h.submit('remove_%s' % c.repo_info.repo_name,_('Remove this repository'),class_="ui-btn red",onclick="return confirm('"+_('Confirm to delete this repository')+"');")}
269 ${h.submit('remove_%s' % c.repo_info.repo_name,_('Remove this repository'),class_="ui-btn red",onclick="return confirm('"+_('Confirm to delete this repository')+"');")}
270 </div>
270 </div>
271 <div class="field" style="border:none;color:#888">
271 <div class="field" style="border:none;color:#888">
272 <ul>
272 <ul>
273 <li>${_('''This repository will be renamed in a special way in order to be unaccesible for RhodeCode and VCS systems.
273 <li>${_('''This repository will be renamed in a special way in order to be unaccesible for RhodeCode and VCS systems.
274 If you need fully delete it from filesystem please do it manually''')}
274 If you need fully delete it from filesystem please do it manually''')}
275 </li>
275 </li>
276 </ul>
276 </ul>
277 </div>
277 </div>
278 </div>
278 </div>
279 ${h.end_form()}
279 ${h.end_form()}
280 </div>
280 </div>
281
281
282 </%def>
282 </%def>
@@ -1,128 +1,128 b''
1 <table id="permissions_manage" class="noborder">
1 <table id="permissions_manage" class="noborder">
2 <tr>
2 <tr>
3 <td>${_('none')}</td>
3 <td>${_('none')}</td>
4 <td>${_('read')}</td>
4 <td>${_('read')}</td>
5 <td>${_('write')}</td>
5 <td>${_('write')}</td>
6 <td>${_('admin')}</td>
6 <td>${_('admin')}</td>
7 <td>${_('member')}</td>
7 <td>${_('member')}</td>
8 <td></td>
8 <td></td>
9 </tr>
9 </tr>
10 ## USERS
10 ## USERS
11 %for r2p in c.repo_info.repo_to_perm:
11 %for r2p in c.repo_info.repo_to_perm:
12 %if r2p.user.username =='default' and c.repo_info.private:
12 %if r2p.user.username =='default' and c.repo_info.private:
13 <tr>
13 <tr>
14 <td colspan="4">
14 <td colspan="4">
15 <span class="private_repo_msg">
15 <span class="private_repo_msg">
16 ${_('private repository')}
16 ${_('private repository')}
17 </span>
17 </span>
18 </td>
18 </td>
19 <td class="private_repo_msg"><img style="vertical-align:bottom" src="${h.url('/images/icons/user.png')}"/>${_('default')}</td>
19 <td class="private_repo_msg"><img style="vertical-align:bottom" src="${h.url('/images/icons/user.png')}"/>${_('default')}</td>
20 </tr>
20 </tr>
21 %else:
21 %else:
22 <tr id="id${id(r2p.user.username)}">
22 <tr id="id${id(r2p.user.username)}">
23 <td>${h.radio('u_perm_%s' % r2p.user.username,'repository.none')}</td>
23 <td>${h.radio('u_perm_%s' % r2p.user.username,'repository.none')}</td>
24 <td>${h.radio('u_perm_%s' % r2p.user.username,'repository.read')}</td>
24 <td>${h.radio('u_perm_%s' % r2p.user.username,'repository.read')}</td>
25 <td>${h.radio('u_perm_%s' % r2p.user.username,'repository.write')}</td>
25 <td>${h.radio('u_perm_%s' % r2p.user.username,'repository.write')}</td>
26 <td>${h.radio('u_perm_%s' % r2p.user.username,'repository.admin')}</td>
26 <td>${h.radio('u_perm_%s' % r2p.user.username,'repository.admin')}</td>
27 <td style="white-space: nowrap;">
27 <td style="white-space: nowrap;">
28 <img class="perm-gravatar" src="${h.gravatar_url(r2p.user.email,14)}"/>${r2p.user.username if r2p.user.username != 'default' else _('default')}
28 <img class="perm-gravatar" src="${h.gravatar_url(r2p.user.email,14)}"/>${r2p.user.username if r2p.user.username != 'default' else _('default')}
29 </td>
29 </td>
30 <td>
30 <td>
31 %if r2p.user.username !='default':
31 %if r2p.user.username !='default':
32 <span class="delete_icon action_button" onclick="ajaxActionUser(${r2p.user.user_id},'${'id%s'%id(r2p.user.username)}')">
32 <span class="delete_icon action_button" onclick="ajaxActionUser(${r2p.user.user_id},'${'id%s'%id(r2p.user.username)}')">
33 ${_('revoke')}
33 ${_('revoke')}
34 </span>
34 </span>
35 %endif
35 %endif
36 </td>
36 </td>
37 </tr>
37 </tr>
38 %endif
38 %endif
39 %endfor
39 %endfor
40
40
41 ## USERS GROUPS
41 ## USERS GROUPS
42 %for g2p in c.repo_info.users_group_to_perm:
42 %for g2p in c.repo_info.users_group_to_perm:
43 <tr id="id${id(g2p.users_group.users_group_name)}">
43 <tr id="id${id(g2p.users_group.users_group_name)}">
44 <td>${h.radio('g_perm_%s' % g2p.users_group.users_group_name,'repository.none')}</td>
44 <td>${h.radio('g_perm_%s' % g2p.users_group.users_group_name,'repository.none')}</td>
45 <td>${h.radio('g_perm_%s' % g2p.users_group.users_group_name,'repository.read')}</td>
45 <td>${h.radio('g_perm_%s' % g2p.users_group.users_group_name,'repository.read')}</td>
46 <td>${h.radio('g_perm_%s' % g2p.users_group.users_group_name,'repository.write')}</td>
46 <td>${h.radio('g_perm_%s' % g2p.users_group.users_group_name,'repository.write')}</td>
47 <td>${h.radio('g_perm_%s' % g2p.users_group.users_group_name,'repository.admin')}</td>
47 <td>${h.radio('g_perm_%s' % g2p.users_group.users_group_name,'repository.admin')}</td>
48 <td style="white-space: nowrap;">
48 <td style="white-space: nowrap;">
49 <img class="perm-gravatar" src="${h.url('/images/icons/group.png')}"/>
49 <img class="perm-gravatar" src="${h.url('/images/icons/group.png')}"/>
50 %if h.HasPermissionAny('hg.admin')():
50 %if h.HasPermissionAny('hg.admin')():
51 <a href="${h.url('edit_users_group',id=g2p.users_group.users_group_id)}">${g2p.users_group.users_group_name}</a>
51 <a href="${h.url('edit_users_group',id=g2p.users_group.users_group_id)}">${g2p.users_group.users_group_name}</a>
52 %else:
52 %else:
53 ${g2p.users_group.users_group_name}
53 ${g2p.users_group.users_group_name}
54 %endif
54 %endif
55 </td>
55 </td>
56 <td>
56 <td>
57 <span class="delete_icon action_button" onclick="ajaxActionUsersGroup(${g2p.users_group.users_group_id},'${'id%s'%id(g2p.users_group.users_group_name)}')">
57 <span class="delete_icon action_button" onclick="ajaxActionUsersGroup(${g2p.users_group.users_group_id},'${'id%s'%id(g2p.users_group.users_group_name)}')">
58 ${_('revoke')}
58 ${_('revoke')}
59 </span>
59 </span>
60 </td>
60 </td>
61 </tr>
61 </tr>
62 %endfor
62 %endfor
63 <%
63 <%
64 _tmpl = h.literal("""' \
64 _tmpl = h.literal("""' \
65 <td><input type="radio" value="repository.none" name="perm_new_member_{0}" id="perm_new_member_{0}"></td> \
65 <td><input type="radio" value="repository.none" name="perm_new_member_{0}" id="perm_new_member_{0}"></td> \
66 <td><input type="radio" value="repository.read" name="perm_new_member_{0}" id="perm_new_member_{0}"></td> \
66 <td><input type="radio" value="repository.read" name="perm_new_member_{0}" id="perm_new_member_{0}"></td> \
67 <td><input type="radio" value="repository.write" name="perm_new_member_{0}" id="perm_new_member_{0}"></td> \
67 <td><input type="radio" value="repository.write" name="perm_new_member_{0}" id="perm_new_member_{0}"></td> \
68 <td><input type="radio" value="repository.admin" name="perm_new_member_{0}" id="perm_new_member_{0}"></td> \
68 <td><input type="radio" value="repository.admin" name="perm_new_member_{0}" id="perm_new_member_{0}"></td> \
69 <td class="ac"> \
69 <td class="ac"> \
70 <div class="perm_ac" id="perm_ac_{0}"> \
70 <div class="perm_ac" id="perm_ac_{0}"> \
71 <input class="yui-ac-input" id="perm_new_member_name_{0}" name="perm_new_member_name_{0}" value="" type="text"> \
71 <input class="yui-ac-input" id="perm_new_member_name_{0}" name="perm_new_member_name_{0}" value="" type="text"> \
72 <input id="perm_new_member_type_{0}" name="perm_new_member_type_{0}" value="" type="hidden"> \
72 <input id="perm_new_member_type_{0}" name="perm_new_member_type_{0}" value="" type="hidden"> \
73 <div id="perm_container_{0}"></div> \
73 <div id="perm_container_{0}"></div> \
74 </div> \
74 </div> \
75 </td> \
75 </td> \
76 <td></td>'""")
76 <td></td>'""")
77 %>
77 %>
78 ## ADD HERE DYNAMICALLY NEW INPUTS FROM THE '_tmpl'
78 ## ADD HERE DYNAMICALLY NEW INPUTS FROM THE '_tmpl'
79 <tr class="new_members last_new_member" id="add_perm_input"></tr>
79 <tr class="new_members last_new_member" id="add_perm_input"></tr>
80 <tr>
80 <tr>
81 <td colspan="6">
81 <td colspan="6">
82 <span id="add_perm" class="add_icon" style="cursor: pointer;">
82 <span id="add_perm" class="add_icon" style="cursor: pointer;">
83 ${_('Add another member')}
83 ${_('Add another member')}
84 </span>
84 </span>
85 </td>
85 </td>
86 </tr>
86 </tr>
87 </table>
87 </table>
88 <script type="text/javascript">
88 <script type="text/javascript">
89 function ajaxActionUser(user_id, field_id) {
89 function ajaxActionUser(user_id, field_id) {
90 var sUrl = "${h.url('delete_repo_user',repo_name=c.repo_name)}";
90 var sUrl = "${h.url('delete_repo_user',repo_name=c.repo_name)}";
91 var callback = {
91 var callback = {
92 success: function (o) {
92 success: function (o) {
93 var tr = YUD.get(String(field_id));
93 var tr = YUD.get(String(field_id));
94 tr.parentNode.removeChild(tr);
94 tr.parentNode.removeChild(tr);
95 },
95 },
96 failure: function (o) {
96 failure: function (o) {
97 alert("${_('Failed to remove user')}");
97 alert("${_('Failed to remove user')}");
98 },
98 },
99 };
99 };
100 var postData = '_method=delete&user_id=' + user_id;
100 var postData = '_method=delete&user_id=' + user_id;
101 var request = YAHOO.util.Connect.asyncRequest('POST', sUrl, callback, postData);
101 var request = YAHOO.util.Connect.asyncRequest('POST', sUrl, callback, postData);
102 };
102 };
103
103
104 function ajaxActionUsersGroup(users_group_id,field_id){
104 function ajaxActionUsersGroup(users_group_id,field_id){
105 var sUrl = "${h.url('delete_repo_users_group',repo_name=c.repo_name)}";
105 var sUrl = "${h.url('delete_repo_users_group',repo_name=c.repo_name)}";
106 var callback = {
106 var callback = {
107 success:function(o){
107 success:function(o){
108 var tr = YUD.get(String(field_id));
108 var tr = YUD.get(String(field_id));
109 tr.parentNode.removeChild(tr);
109 tr.parentNode.removeChild(tr);
110 },
110 },
111 failure:function(o){
111 failure:function(o){
112 alert("${_('Failed to remove users group')}");
112 alert("${_('Failed to remove users group')}");
113 },
113 },
114 };
114 };
115 var postData = '_method=delete&users_group_id='+users_group_id;
115 var postData = '_method=delete&users_group_id='+users_group_id;
116 var request = YAHOO.util.Connect.asyncRequest('POST', sUrl, callback, postData);
116 var request = YAHOO.util.Connect.asyncRequest('POST', sUrl, callback, postData);
117 };
117 };
118
118
119 YUE.onDOMReady(function () {
119 YUE.onDOMReady(function () {
120 if (!YUD.hasClass('perm_new_member_name', 'error')) {
120 if (!YUD.hasClass('perm_new_member_name', 'error')) {
121 YUD.setStyle('add_perm_input', 'display', 'none');
121 YUD.setStyle('add_perm_input', 'display', 'none');
122 }
122 }
123 YAHOO.util.Event.addListener('add_perm', 'click', function () {
123 YAHOO.util.Event.addListener('add_perm', 'click', function () {
124 addPermAction(${_tmpl}, ${c.users_array|n}, ${c.users_groups_array|n});
124 addPermAction(${_tmpl}, ${c.users_array|n}, ${c.users_groups_array|n});
125 });
125 });
126 });
126 });
127
127
128 </script>
128 </script>
@@ -1,112 +1,112 b''
1 <table id="permissions_manage" class="noborder">
1 <table id="permissions_manage" class="noborder">
2 <tr>
2 <tr>
3 <td>${_('none')}</td>
3 <td>${_('none')}</td>
4 <td>${_('read')}</td>
4 <td>${_('read')}</td>
5 <td>${_('write')}</td>
5 <td>${_('write')}</td>
6 <td>${_('admin')}</td>
6 <td>${_('admin')}</td>
7 <td>${_('member')}</td>
7 <td>${_('member')}</td>
8 <td></td>
8 <td></td>
9 </tr>
9 </tr>
10 ## USERS
10 ## USERS
11 %for r2p in c.repos_group.repo_group_to_perm:
11 %for r2p in c.repos_group.repo_group_to_perm:
12 <tr id="id${id(r2p.user.username)}">
12 <tr id="id${id(r2p.user.username)}">
13 <td>${h.radio('u_perm_%s' % r2p.user.username,'group.none')}</td>
13 <td>${h.radio('u_perm_%s' % r2p.user.username,'group.none')}</td>
14 <td>${h.radio('u_perm_%s' % r2p.user.username,'group.read')}</td>
14 <td>${h.radio('u_perm_%s' % r2p.user.username,'group.read')}</td>
15 <td>${h.radio('u_perm_%s' % r2p.user.username,'group.write')}</td>
15 <td>${h.radio('u_perm_%s' % r2p.user.username,'group.write')}</td>
16 <td>${h.radio('u_perm_%s' % r2p.user.username,'group.admin')}</td>
16 <td>${h.radio('u_perm_%s' % r2p.user.username,'group.admin')}</td>
17 <td style="white-space: nowrap;">
17 <td style="white-space: nowrap;">
18 <img class="perm-gravatar" src="${h.gravatar_url(r2p.user.email,14)}"/>${r2p.user.username if r2p.user.username != 'default' else _('default')}
18 <img class="perm-gravatar" src="${h.gravatar_url(r2p.user.email,14)}"/>${r2p.user.username if r2p.user.username != 'default' else _('default')}
19 </td>
19 </td>
20 <td>
20 <td>
21 %if r2p.user.username !='default':
21 %if r2p.user.username !='default':
22 <span class="delete_icon action_button" onclick="ajaxActionUser(${r2p.user.user_id},'${'id%s'%id(r2p.user.username)}')">
22 <span class="delete_icon action_button" onclick="ajaxActionUser(${r2p.user.user_id},'${'id%s'%id(r2p.user.username)}')">
23 ${_('revoke')}
23 ${_('revoke')}
24 </span>
24 </span>
25 %endif
25 %endif
26 </td>
26 </td>
27 </tr>
27 </tr>
28 %endfor
28 %endfor
29
29
30 ## USERS GROUPS
30 ## USERS GROUPS
31 %for g2p in c.repos_group.users_group_to_perm:
31 %for g2p in c.repos_group.users_group_to_perm:
32 <tr id="id${id(g2p.users_group.users_group_name)}">
32 <tr id="id${id(g2p.users_group.users_group_name)}">
33 <td>${h.radio('g_perm_%s' % g2p.users_group.users_group_name,'group.none')}</td>
33 <td>${h.radio('g_perm_%s' % g2p.users_group.users_group_name,'group.none')}</td>
34 <td>${h.radio('g_perm_%s' % g2p.users_group.users_group_name,'group.read')}</td>
34 <td>${h.radio('g_perm_%s' % g2p.users_group.users_group_name,'group.read')}</td>
35 <td>${h.radio('g_perm_%s' % g2p.users_group.users_group_name,'group.write')}</td>
35 <td>${h.radio('g_perm_%s' % g2p.users_group.users_group_name,'group.write')}</td>
36 <td>${h.radio('g_perm_%s' % g2p.users_group.users_group_name,'group.admin')}</td>
36 <td>${h.radio('g_perm_%s' % g2p.users_group.users_group_name,'group.admin')}</td>
37 <td style="white-space: nowrap;">
37 <td style="white-space: nowrap;">
38 <img class="perm-gravatar" src="${h.url('/images/icons/group.png')}"/>${g2p.users_group.users_group_name}
38 <img class="perm-gravatar" src="${h.url('/images/icons/group.png')}"/>${g2p.users_group.users_group_name}
39 </td>
39 </td>
40 <td>
40 <td>
41 <span class="delete_icon action_button" onclick="ajaxActionUsersGroup(${g2p.users_group.users_group_id},'${'id%s'%id(g2p.users_group.users_group_name)}')">
41 <span class="delete_icon action_button" onclick="ajaxActionUsersGroup(${g2p.users_group.users_group_id},'${'id%s'%id(g2p.users_group.users_group_name)}')">
42 ${_('revoke')}
42 ${_('revoke')}
43 </span>
43 </span>
44 </td>
44 </td>
45 </tr>
45 </tr>
46 %endfor
46 %endfor
47 <%
47 <%
48 _tmpl = h.literal("""' \
48 _tmpl = h.literal("""' \
49 <td><input type="radio" value="group.none" name="perm_new_member_{0}" id="perm_new_member_{0}"></td> \
49 <td><input type="radio" value="group.none" name="perm_new_member_{0}" id="perm_new_member_{0}"></td> \
50 <td><input type="radio" value="group.read" name="perm_new_member_{0}" id="perm_new_member_{0}"></td> \
50 <td><input type="radio" value="group.read" name="perm_new_member_{0}" id="perm_new_member_{0}"></td> \
51 <td><input type="radio" value="group.write" name="perm_new_member_{0}" id="perm_new_member_{0}"></td> \
51 <td><input type="radio" value="group.write" name="perm_new_member_{0}" id="perm_new_member_{0}"></td> \
52 <td><input type="radio" value="group.admin" name="perm_new_member_{0}" id="perm_new_member_{0}"></td> \
52 <td><input type="radio" value="group.admin" name="perm_new_member_{0}" id="perm_new_member_{0}"></td> \
53 <td class="ac"> \
53 <td class="ac"> \
54 <div class="perm_ac" id="perm_ac_{0}"> \
54 <div class="perm_ac" id="perm_ac_{0}"> \
55 <input class="yui-ac-input" id="perm_new_member_name_{0}" name="perm_new_member_name_{0}" value="" type="text"> \
55 <input class="yui-ac-input" id="perm_new_member_name_{0}" name="perm_new_member_name_{0}" value="" type="text"> \
56 <input id="perm_new_member_type_{0}" name="perm_new_member_type_{0}" value="" type="hidden"> \
56 <input id="perm_new_member_type_{0}" name="perm_new_member_type_{0}" value="" type="hidden"> \
57 <div id="perm_container_{0}"></div> \
57 <div id="perm_container_{0}"></div> \
58 </div> \
58 </div> \
59 </td> \
59 </td> \
60 <td></td>'""")
60 <td></td>'""")
61 %>
61 %>
62 ## ADD HERE DYNAMICALLY NEW INPUTS FROM THE '_tmpl'
62 ## ADD HERE DYNAMICALLY NEW INPUTS FROM THE '_tmpl'
63 <tr class="new_members last_new_member" id="add_perm_input"></tr>
63 <tr class="new_members last_new_member" id="add_perm_input"></tr>
64 <tr>
64 <tr>
65 <td colspan="6">
65 <td colspan="6">
66 <span id="add_perm" class="add_icon" style="cursor: pointer;">
66 <span id="add_perm" class="add_icon" style="cursor: pointer;">
67 ${_('Add another member')}
67 ${_('Add another member')}
68 </span>
68 </span>
69 </td>
69 </td>
70 </tr>
70 </tr>
71 </table>
71 </table>
72 <script type="text/javascript">
72 <script type="text/javascript">
73 function ajaxActionUser(user_id, field_id) {
73 function ajaxActionUser(user_id, field_id) {
74 var sUrl = "${h.url('delete_repos_group_user_perm',group_name=c.repos_group.group_name)}";
74 var sUrl = "${h.url('delete_repos_group_user_perm',group_name=c.repos_group.group_name)}";
75 var callback = {
75 var callback = {
76 success: function (o) {
76 success: function (o) {
77 var tr = YUD.get(String(field_id));
77 var tr = YUD.get(String(field_id));
78 tr.parentNode.removeChild(tr);
78 tr.parentNode.removeChild(tr);
79 },
79 },
80 failure: function (o) {
80 failure: function (o) {
81 alert("${_('Failed to remove user')}");
81 alert("${_('Failed to remove user')}");
82 },
82 },
83 };
83 };
84 var postData = '_method=delete&user_id=' + user_id;
84 var postData = '_method=delete&user_id=' + user_id;
85 var request = YAHOO.util.Connect.asyncRequest('POST', sUrl, callback, postData);
85 var request = YAHOO.util.Connect.asyncRequest('POST', sUrl, callback, postData);
86 };
86 };
87
87
88 function ajaxActionUsersGroup(users_group_id,field_id){
88 function ajaxActionUsersGroup(users_group_id,field_id){
89 var sUrl = "${h.url('delete_repos_group_users_group_perm',group_name=c.repos_group.group_name)}";
89 var sUrl = "${h.url('delete_repos_group_users_group_perm',group_name=c.repos_group.group_name)}";
90 var callback = {
90 var callback = {
91 success:function(o){
91 success:function(o){
92 var tr = YUD.get(String(field_id));
92 var tr = YUD.get(String(field_id));
93 tr.parentNode.removeChild(tr);
93 tr.parentNode.removeChild(tr);
94 },
94 },
95 failure:function(o){
95 failure:function(o){
96 alert("${_('Failed to remove users group')}");
96 alert("${_('Failed to remove users group')}");
97 },
97 },
98 };
98 };
99 var postData = '_method=delete&users_group_id='+users_group_id;
99 var postData = '_method=delete&users_group_id='+users_group_id;
100 var request = YAHOO.util.Connect.asyncRequest('POST', sUrl, callback, postData);
100 var request = YAHOO.util.Connect.asyncRequest('POST', sUrl, callback, postData);
101 };
101 };
102
102
103 YUE.onDOMReady(function () {
103 YUE.onDOMReady(function () {
104 if (!YUD.hasClass('perm_new_member_name', 'error')) {
104 if (!YUD.hasClass('perm_new_member_name', 'error')) {
105 YUD.setStyle('add_perm_input', 'display', 'none');
105 YUD.setStyle('add_perm_input', 'display', 'none');
106 }
106 }
107 YAHOO.util.Event.addListener('add_perm', 'click', function () {
107 YAHOO.util.Event.addListener('add_perm', 'click', function () {
108 addPermAction(${_tmpl}, ${c.users_array|n}, ${c.users_groups_array|n});
108 addPermAction(${_tmpl}, ${c.users_array|n}, ${c.users_groups_array|n});
109 });
109 });
110 });
110 });
111
111
112 </script>
112 </script>
@@ -1,22 +1,22 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2 <%inherit file="/base/base.html"/>
2 <%inherit file="/base/base.html"/>
3 <%def name="title()">
3 <%def name="title()">
4 ${_('Repository group')} - ${c.rhodecode_name}
4 ${_('Repository group')} - ${c.rhodecode_name}
5 </%def>
5 </%def>
6
6
7 <%def name="breadcrumbs()">
7 <%def name="breadcrumbs()">
8 <span class="groups_breadcrumbs">
8 <span class="groups_breadcrumbs">
9 ${h.link_to(_(u'Home'),h.url('/'))}
9 ${h.link_to(_(u'Home'),h.url('/'))}
10 %if c.group.parent_group:
10 %if c.group.parent_group:
11 &raquo; ${h.link_to(c.group.parent_group.name,h.url('repos_group_home',group_name=c.group.parent_group.group_name))}
11 &raquo; ${h.link_to(c.group.parent_group.name,h.url('repos_group_home',group_name=c.group.parent_group.group_name))}
12 %endif
12 %endif
13 &raquo; "${c.group.name}" ${_('with')}
13 &raquo; "${c.group.name}" ${_('with')}
14 </span>
14 </span>
15 </%def>
15 </%def>
16
16
17 <%def name="page_nav()">
17 <%def name="page_nav()">
18 ${self.menu('admin')}
18 ${self.menu('admin')}
19 </%def>
19 </%def>
20 <%def name="main()">
20 <%def name="main()">
21 <%include file="/index_base.html" args="parent=self,short_repo_names=True"/>
21 <%include file="/index_base.html" args="parent=self,short_repo_names=True"/>
22 </%def>
22 </%def>
@@ -1,81 +1,81 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2 <%inherit file="/base/base.html"/>
2 <%inherit file="/base/base.html"/>
3
3
4 <%def name="title()">
4 <%def name="title()">
5 ${_('Edit repos group')} ${c.repos_group.name} - ${c.rhodecode_name}
5 ${_('Edit repos group')} ${c.repos_group.name} - ${c.rhodecode_name}
6 </%def>
6 </%def>
7 <%def name="breadcrumbs_links()">
7 <%def name="breadcrumbs_links()">
8 ${h.link_to(_('Admin'),h.url('admin_home'))}
8 ${h.link_to(_('Admin'),h.url('admin_home'))}
9 &raquo;
9 &raquo;
10 ${h.link_to(_('Repos groups'),h.url('repos_groups'))}
10 ${h.link_to(_('Repos groups'),h.url('repos_groups'))}
11 &raquo;
11 &raquo;
12 ${_('edit repos group')} "${c.repos_group.name}"
12 ${_('edit repos group')} "${c.repos_group.name}"
13 </%def>
13 </%def>
14
14
15 <%def name="page_nav()">
15 <%def name="page_nav()">
16 ${self.menu('admin')}
16 ${self.menu('admin')}
17 </%def>
17 </%def>
18
18
19 <%def name="main()">
19 <%def name="main()">
20 <div class="box">
20 <div class="box">
21 <!-- box / title -->
21 <!-- box / title -->
22 <div class="title">
22 <div class="title">
23 ${self.breadcrumbs()}
23 ${self.breadcrumbs()}
24 </div>
24 </div>
25 <!-- end box / title -->
25 <!-- end box / title -->
26 ${h.form(url('repos_group',id=c.repos_group.group_id),method='put')}
26 ${h.form(url('repos_group',id=c.repos_group.group_id),method='put')}
27 <div class="form">
27 <div class="form">
28 <!-- fields -->
28 <!-- fields -->
29 <div class="fields">
29 <div class="fields">
30 <div class="field">
30 <div class="field">
31 <div class="label">
31 <div class="label">
32 <label for="group_name">${_('Group name')}:</label>
32 <label for="group_name">${_('Group name')}:</label>
33 </div>
33 </div>
34 <div class="input">
34 <div class="input">
35 ${h.text('group_name',class_='medium')}
35 ${h.text('group_name',class_='medium')}
36 </div>
36 </div>
37 </div>
37 </div>
38
38
39 <div class="field">
39 <div class="field">
40 <div class="label label-textarea">
40 <div class="label label-textarea">
41 <label for="group_description">${_('Description')}:</label>
41 <label for="group_description">${_('Description')}:</label>
42 </div>
42 </div>
43 <div class="textarea text-area editor">
43 <div class="textarea text-area editor">
44 ${h.textarea('group_description',cols=23,rows=5,class_="medium")}
44 ${h.textarea('group_description',cols=23,rows=5,class_="medium")}
45 </div>
45 </div>
46 </div>
46 </div>
47
47
48 <div class="field">
48 <div class="field">
49 <div class="label">
49 <div class="label">
50 <label for="group_parent_id">${_('Group parent')}:</label>
50 <label for="group_parent_id">${_('Group parent')}:</label>
51 </div>
51 </div>
52 <div class="input">
52 <div class="input">
53 ${h.select('group_parent_id','',c.repo_groups,class_="medium")}
53 ${h.select('group_parent_id','',c.repo_groups,class_="medium")}
54 </div>
54 </div>
55 </div>
55 </div>
56 <div class="field">
56 <div class="field">
57 <div class="label">
57 <div class="label">
58 <label for="input">${_('Permissions')}:</label>
58 <label for="input">${_('Permissions')}:</label>
59 </div>
59 </div>
60 <div class="input">
60 <div class="input">
61 <%include file="repos_group_edit_perms.html"/>
61 <%include file="repos_group_edit_perms.html"/>
62 </div>
62 </div>
63 </div>
63 </div>
64 <div class="field">
64 <div class="field">
65 <div class="label label-checkbox">
65 <div class="label label-checkbox">
66 <label for="enable_locking">${_('Enable locking')}:</label>
66 <label for="enable_locking">${_('Enable locking')}:</label>
67 </div>
67 </div>
68 <div class="checkboxes">
68 <div class="checkboxes">
69 ${h.checkbox('enable_locking',value="True")}
69 ${h.checkbox('enable_locking',value="True")}
70 <span class="help-block">${_('Enable lock-by-pulling on group. This option will be applied to all other groups and repositories inside')}</span>
70 <span class="help-block">${_('Enable lock-by-pulling on group. This option will be applied to all other groups and repositories inside')}</span>
71 </div>
71 </div>
72 </div>
72 </div>
73 <div class="buttons">
73 <div class="buttons">
74 ${h.submit('save',_('Save'),class_="ui-btn large")}
74 ${h.submit('save',_('Save'),class_="ui-btn large")}
75 ${h.reset('reset',_('Reset'),class_="ui-btn large")}
75 ${h.reset('reset',_('Reset'),class_="ui-btn large")}
76 </div>
76 </div>
77 </div>
77 </div>
78 </div>
78 </div>
79 ${h.end_form()}
79 ${h.end_form()}
80 </div>
80 </div>
81 </%def>
81 </%def>
@@ -1,327 +1,327 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2 <%inherit file="/base/base.html"/>
2 <%inherit file="/base/base.html"/>
3
3
4 <%def name="title()">
4 <%def name="title()">
5 ${_('Settings administration')} - ${c.rhodecode_name}
5 ${_('Settings administration')} - ${c.rhodecode_name}
6 </%def>
6 </%def>
7
7
8 <%def name="breadcrumbs_links()">
8 <%def name="breadcrumbs_links()">
9 ${h.link_to(_('Admin'),h.url('admin_home'))} &raquo; ${_('Settings')}
9 ${h.link_to(_('Admin'),h.url('admin_home'))} &raquo; ${_('Settings')}
10 </%def>
10 </%def>
11
11
12 <%def name="page_nav()">
12 <%def name="page_nav()">
13 ${self.menu('admin')}
13 ${self.menu('admin')}
14 </%def>
14 </%def>
15
15
16 <%def name="main()">
16 <%def name="main()">
17 <div class="box">
17 <div class="box">
18 <!-- box / title -->
18 <!-- box / title -->
19 <div class="title">
19 <div class="title">
20 ${self.breadcrumbs()}
20 ${self.breadcrumbs()}
21 </div>
21 </div>
22 <!-- end box / title -->
22 <!-- end box / title -->
23
23
24 <h3>${_('Remap and rescan repositories')}</h3>
24 <h3>${_('Remap and rescan repositories')}</h3>
25 ${h.form(url('admin_setting', setting_id='mapping'),method='put')}
25 ${h.form(url('admin_setting', setting_id='mapping'),method='put')}
26 <div class="form">
26 <div class="form">
27 <!-- fields -->
27 <!-- fields -->
28
28
29 <div class="fields">
29 <div class="fields">
30 <div class="field">
30 <div class="field">
31 <div class="label label-checkbox">
31 <div class="label label-checkbox">
32 <label for="destroy">${_('rescan option')}:</label>
32 <label for="destroy">${_('rescan option')}:</label>
33 </div>
33 </div>
34 <div class="checkboxes">
34 <div class="checkboxes">
35 <div class="checkbox">
35 <div class="checkbox">
36 ${h.checkbox('destroy',True)}
36 ${h.checkbox('destroy',True)}
37 <label for="destroy">
37 <label for="destroy">
38 <span class="tooltip" title="${h.tooltip(_('In case a repository was deleted from filesystem and there are leftovers in the database check this option to scan obsolete data in database and remove it.'))}">
38 <span class="tooltip" title="${h.tooltip(_('In case a repository was deleted from filesystem and there are leftovers in the database check this option to scan obsolete data in database and remove it.'))}">
39 ${_('destroy old data')}</span> </label>
39 ${_('destroy old data')}</span> </label>
40 </div>
40 </div>
41 <span class="help-block">${_('Rescan repositories location for new repositories. Also deletes obsolete if `destroy` flag is checked ')}</span>
41 <span class="help-block">${_('Rescan repositories location for new repositories. Also deletes obsolete if `destroy` flag is checked ')}</span>
42 </div>
42 </div>
43 </div>
43 </div>
44
44
45 <div class="buttons">
45 <div class="buttons">
46 ${h.submit('rescan',_('Rescan repositories'),class_="ui-btn large")}
46 ${h.submit('rescan',_('Rescan repositories'),class_="ui-btn large")}
47 </div>
47 </div>
48 </div>
48 </div>
49 </div>
49 </div>
50 ${h.end_form()}
50 ${h.end_form()}
51
51
52 <h3>${_('Whoosh indexing')}</h3>
52 <h3>${_('Whoosh indexing')}</h3>
53 ${h.form(url('admin_setting', setting_id='whoosh'),method='put')}
53 ${h.form(url('admin_setting', setting_id='whoosh'),method='put')}
54 <div class="form">
54 <div class="form">
55 <!-- fields -->
55 <!-- fields -->
56
56
57 <div class="fields">
57 <div class="fields">
58 <div class="field">
58 <div class="field">
59 <div class="label label-checkbox">
59 <div class="label label-checkbox">
60 <label>${_('index build option')}:</label>
60 <label>${_('index build option')}:</label>
61 </div>
61 </div>
62 <div class="checkboxes">
62 <div class="checkboxes">
63 <div class="checkbox">
63 <div class="checkbox">
64 ${h.checkbox('full_index',True)}
64 ${h.checkbox('full_index',True)}
65 <label for="full_index">${_('build from scratch')}</label>
65 <label for="full_index">${_('build from scratch')}</label>
66 </div>
66 </div>
67 </div>
67 </div>
68 </div>
68 </div>
69
69
70 <div class="buttons">
70 <div class="buttons">
71 ${h.submit('reindex',_('Reindex'),class_="ui-btn large")}
71 ${h.submit('reindex',_('Reindex'),class_="ui-btn large")}
72 </div>
72 </div>
73 </div>
73 </div>
74 </div>
74 </div>
75 ${h.end_form()}
75 ${h.end_form()}
76
76
77 <h3>${_('Global application settings')}</h3>
77 <h3>${_('Global application settings')}</h3>
78 ${h.form(url('admin_setting', setting_id='global'),method='put')}
78 ${h.form(url('admin_setting', setting_id='global'),method='put')}
79 <div class="form">
79 <div class="form">
80 <!-- fields -->
80 <!-- fields -->
81
81
82 <div class="fields">
82 <div class="fields">
83
83
84 <div class="field">
84 <div class="field">
85 <div class="label">
85 <div class="label">
86 <label for="rhodecode_title">${_('Application name')}:</label>
86 <label for="rhodecode_title">${_('Application name')}:</label>
87 </div>
87 </div>
88 <div class="input">
88 <div class="input">
89 ${h.text('rhodecode_title',size=30)}
89 ${h.text('rhodecode_title',size=30)}
90 </div>
90 </div>
91 </div>
91 </div>
92
92
93 <div class="field">
93 <div class="field">
94 <div class="label">
94 <div class="label">
95 <label for="rhodecode_realm">${_('Realm text')}:</label>
95 <label for="rhodecode_realm">${_('Realm text')}:</label>
96 </div>
96 </div>
97 <div class="input">
97 <div class="input">
98 ${h.text('rhodecode_realm',size=30)}
98 ${h.text('rhodecode_realm',size=30)}
99 </div>
99 </div>
100 </div>
100 </div>
101
101
102 <div class="field">
102 <div class="field">
103 <div class="label">
103 <div class="label">
104 <label for="rhodecode_ga_code">${_('GA code')}:</label>
104 <label for="rhodecode_ga_code">${_('GA code')}:</label>
105 </div>
105 </div>
106 <div class="input">
106 <div class="input">
107 ${h.text('rhodecode_ga_code',size=30)}
107 ${h.text('rhodecode_ga_code',size=30)}
108 </div>
108 </div>
109 </div>
109 </div>
110
110
111 <div class="buttons">
111 <div class="buttons">
112 ${h.submit('save',_('Save settings'),class_="ui-btn large")}
112 ${h.submit('save',_('Save settings'),class_="ui-btn large")}
113 ${h.reset('reset',_('Reset'),class_="ui-btn large")}
113 ${h.reset('reset',_('Reset'),class_="ui-btn large")}
114 </div>
114 </div>
115 </div>
115 </div>
116 </div>
116 </div>
117 ${h.end_form()}
117 ${h.end_form()}
118
118
119 <h3>${_('Visualisation settings')}</h3>
119 <h3>${_('Visualisation settings')}</h3>
120 ${h.form(url('admin_setting', setting_id='visual'),method='put')}
120 ${h.form(url('admin_setting', setting_id='visual'),method='put')}
121 <div class="form">
121 <div class="form">
122 <!-- fields -->
122 <!-- fields -->
123
123
124 <div class="fields">
124 <div class="fields">
125
125
126 <div class="field">
126 <div class="field">
127 <div class="label label-checkbox">
127 <div class="label label-checkbox">
128 <label>${_('Icons')}:</label>
128 <label>${_('Icons')}:</label>
129 </div>
129 </div>
130 <div class="checkboxes">
130 <div class="checkboxes">
131 <div class="checkbox">
131 <div class="checkbox">
132 ${h.checkbox('rhodecode_show_public_icon','True')}
132 ${h.checkbox('rhodecode_show_public_icon','True')}
133 <label for="rhodecode_show_public_icon">${_('Show public repo icon on repositories')}</label>
133 <label for="rhodecode_show_public_icon">${_('Show public repo icon on repositories')}</label>
134 </div>
134 </div>
135 <div class="checkbox">
135 <div class="checkbox">
136 ${h.checkbox('rhodecode_show_private_icon','True')}
136 ${h.checkbox('rhodecode_show_private_icon','True')}
137 <label for="rhodecode_show_private_icon">${_('Show private repo icon on repositories')}</label>
137 <label for="rhodecode_show_private_icon">${_('Show private repo icon on repositories')}</label>
138 </div>
138 </div>
139 </div>
139 </div>
140 </div>
140 </div>
141
141
142 <div class="field">
142 <div class="field">
143 <div class="label label-checkbox">
143 <div class="label label-checkbox">
144 <label>${_('Meta-Tagging')}:</label>
144 <label>${_('Meta-Tagging')}:</label>
145 </div>
145 </div>
146 <div class="checkboxes">
146 <div class="checkboxes">
147 <div class="checkbox">
147 <div class="checkbox">
148 ${h.checkbox('rhodecode_stylify_metatags','True')}
148 ${h.checkbox('rhodecode_stylify_metatags','True')}
149 <label for="rhodecode_stylify_metatags">${_('Stylify recognised metatags:')}</label>
149 <label for="rhodecode_stylify_metatags">${_('Stylify recognised metatags:')}</label>
150 </div>
150 </div>
151 <div style="padding-left: 20px;">
151 <div style="padding-left: 20px;">
152 <ul> <!-- Fix style here -->
152 <ul> <!-- Fix style here -->
153 <li>[featured] <span class="metatag" tag="featured">featured</span></li>
153 <li>[featured] <span class="metatag" tag="featured">featured</span></li>
154 <li>[stale] <span class="metatag" tag="stale">stale</span></li>
154 <li>[stale] <span class="metatag" tag="stale">stale</span></li>
155 <li>[dead] <span class="metatag" tag="dead">dead</span></li>
155 <li>[dead] <span class="metatag" tag="dead">dead</span></li>
156 <li>[lang =&gt; lang] <span class="metatag" tag="lang" >lang</span></li>
156 <li>[lang =&gt; lang] <span class="metatag" tag="lang" >lang</span></li>
157 <li>[license =&gt; License] <span class="metatag" tag="license"><a href="http://www.opensource.org/licenses/License" >License</a></span></li>
157 <li>[license =&gt; License] <span class="metatag" tag="license"><a href="http://www.opensource.org/licenses/License" >License</a></span></li>
158 <li>[requires =&gt; Repo] <span class="metatag" tag="requires" >requires =&gt; <a href="#" >Repo</a></span></li>
158 <li>[requires =&gt; Repo] <span class="metatag" tag="requires" >requires =&gt; <a href="#" >Repo</a></span></li>
159 <li>[recommends =&gt; Repo] <span class="metatag" tag="recommends" >recommends =&gt; <a href="#" >Repo</a></span></li>
159 <li>[recommends =&gt; Repo] <span class="metatag" tag="recommends" >recommends =&gt; <a href="#" >Repo</a></span></li>
160 <li>[see =&gt; URI] <span class="metatag" tag="see">see =&gt; <a href="#">URI</a> </span></li>
160 <li>[see =&gt; URI] <span class="metatag" tag="see">see =&gt; <a href="#">URI</a> </span></li>
161 </ul>
161 </ul>
162 </div>
162 </div>
163 </div>
163 </div>
164 </div>
164 </div>
165
165
166 <div class="buttons">
166 <div class="buttons">
167 ${h.submit('save',_('Save settings'),class_="ui-btn large")}
167 ${h.submit('save',_('Save settings'),class_="ui-btn large")}
168 ${h.reset('reset',_('Reset'),class_="ui-btn large")}
168 ${h.reset('reset',_('Reset'),class_="ui-btn large")}
169 </div>
169 </div>
170
170
171 </div>
171 </div>
172 </div>
172 </div>
173 ${h.end_form()}
173 ${h.end_form()}
174
174
175
175
176 <h3>${_('VCS settings')}</h3>
176 <h3>${_('VCS settings')}</h3>
177 ${h.form(url('admin_setting', setting_id='vcs'),method='put')}
177 ${h.form(url('admin_setting', setting_id='vcs'),method='put')}
178 <div class="form">
178 <div class="form">
179 <!-- fields -->
179 <!-- fields -->
180
180
181 <div class="fields">
181 <div class="fields">
182
182
183 <div class="field">
183 <div class="field">
184 <div class="label label-checkbox">
184 <div class="label label-checkbox">
185 <label>${_('Web')}:</label>
185 <label>${_('Web')}:</label>
186 </div>
186 </div>
187 <div class="checkboxes">
187 <div class="checkboxes">
188 <div class="checkbox">
188 <div class="checkbox">
189 ${h.checkbox('web_push_ssl','true')}
189 ${h.checkbox('web_push_ssl','true')}
190 <label for="web_push_ssl">${_('require ssl for vcs operations')}</label>
190 <label for="web_push_ssl">${_('require ssl for vcs operations')}</label>
191 </div>
191 </div>
192 <span class="help-block">${_('RhodeCode will require SSL for pushing or pulling. If SSL is missing it will return HTTP Error 406: Not Acceptable')}</span>
192 <span class="help-block">${_('RhodeCode will require SSL for pushing or pulling. If SSL is missing it will return HTTP Error 406: Not Acceptable')}</span>
193 </div>
193 </div>
194 </div>
194 </div>
195
195
196 <div class="field">
196 <div class="field">
197 <div class="label label-checkbox">
197 <div class="label label-checkbox">
198 <label>${_('Hooks')}:</label>
198 <label>${_('Hooks')}:</label>
199 </div>
199 </div>
200 <div class="checkboxes">
200 <div class="checkboxes">
201 <div class="checkbox">
201 <div class="checkbox">
202 ${h.checkbox('hooks_changegroup_update','True')}
202 ${h.checkbox('hooks_changegroup_update','True')}
203 <label for="hooks_changegroup_update">${_('Update repository after push (hg update)')}</label>
203 <label for="hooks_changegroup_update">${_('Update repository after push (hg update)')}</label>
204 </div>
204 </div>
205 <div class="checkbox">
205 <div class="checkbox">
206 ${h.checkbox('hooks_changegroup_repo_size','True')}
206 ${h.checkbox('hooks_changegroup_repo_size','True')}
207 <label for="hooks_changegroup_repo_size">${_('Show repository size after push')}</label>
207 <label for="hooks_changegroup_repo_size">${_('Show repository size after push')}</label>
208 </div>
208 </div>
209 <div class="checkbox">
209 <div class="checkbox">
210 ${h.checkbox('hooks_changegroup_push_logger','True')}
210 ${h.checkbox('hooks_changegroup_push_logger','True')}
211 <label for="hooks_changegroup_push_logger">${_('Log user push commands')}</label>
211 <label for="hooks_changegroup_push_logger">${_('Log user push commands')}</label>
212 </div>
212 </div>
213 <div class="checkbox">
213 <div class="checkbox">
214 ${h.checkbox('hooks_outgoing_pull_logger','True')}
214 ${h.checkbox('hooks_outgoing_pull_logger','True')}
215 <label for="hooks_outgoing_pull_logger">${_('Log user pull commands')}</label>
215 <label for="hooks_outgoing_pull_logger">${_('Log user pull commands')}</label>
216 </div>
216 </div>
217 </div>
217 </div>
218 <div class="input" style="margin-top:10px">
218 <div class="input" style="margin-top:10px">
219 ${h.link_to(_('advanced setup'),url('admin_edit_setting',setting_id='hooks'),class_="ui-btn")}
219 ${h.link_to(_('advanced setup'),url('admin_edit_setting',setting_id='hooks'),class_="ui-btn")}
220 </div>
220 </div>
221 </div>
221 </div>
222 <div class="field">
222 <div class="field">
223 <div class="label label-checkbox">
223 <div class="label label-checkbox">
224 <label>${_('Mercurial Extensions')}:</label>
224 <label>${_('Mercurial Extensions')}:</label>
225 </div>
225 </div>
226 <div class="checkboxes">
226 <div class="checkboxes">
227 <div class="checkbox">
227 <div class="checkbox">
228 ${h.checkbox('extensions_largefiles','True')}
228 ${h.checkbox('extensions_largefiles','True')}
229 <label for="extensions_hgsubversion">${_('largefiles extensions')}</label>
229 <label for="extensions_hgsubversion">${_('largefiles extensions')}</label>
230 </div>
230 </div>
231 <div class="checkbox">
231 <div class="checkbox">
232 ${h.checkbox('extensions_hgsubversion','True')}
232 ${h.checkbox('extensions_hgsubversion','True')}
233 <label for="extensions_hgsubversion">${_('hgsubversion extensions')}</label>
233 <label for="extensions_hgsubversion">${_('hgsubversion extensions')}</label>
234 </div>
234 </div>
235 <span class="help-block">${_('Requires hgsubversion library installed. Allows clonning from svn remote locations')}</span>
235 <span class="help-block">${_('Requires hgsubversion library installed. Allows clonning from svn remote locations')}</span>
236 ##<div class="checkbox">
236 ##<div class="checkbox">
237 ## ${h.checkbox('extensions_hggit','True')}
237 ## ${h.checkbox('extensions_hggit','True')}
238 ## <label for="extensions_hggit">${_('hg-git extensions')}</label>
238 ## <label for="extensions_hggit">${_('hg-git extensions')}</label>
239 ##</div>
239 ##</div>
240 ##<span class="help-block">${_('Requires hg-git library installed. Allows clonning from git remote locations')}</span>
240 ##<span class="help-block">${_('Requires hg-git library installed. Allows clonning from git remote locations')}</span>
241 </div>
241 </div>
242 </div>
242 </div>
243 <div class="field">
243 <div class="field">
244 <div class="label">
244 <div class="label">
245 <label for="paths_root_path">${_('Repositories location')}:</label>
245 <label for="paths_root_path">${_('Repositories location')}:</label>
246 </div>
246 </div>
247 <div class="input">
247 <div class="input">
248 ${h.text('paths_root_path',size=30,readonly="readonly")}
248 ${h.text('paths_root_path',size=30,readonly="readonly")}
249 <span id="path_unlock" class="tooltip"
249 <span id="path_unlock" class="tooltip"
250 title="${h.tooltip(_('This a crucial application setting. If you are really sure you need to change this, you must restart application in order to make this setting take effect. Click this label to unlock.'))}">
250 title="${h.tooltip(_('This a crucial application setting. If you are really sure you need to change this, you must restart application in order to make this setting take effect. Click this label to unlock.'))}">
251 ${_('unlock')}</span>
251 ${_('unlock')}</span>
252 <span class="help-block">${_('Location where repositories are stored. After changing this value a restart, and rescan is required')}</span>
252 <span class="help-block">${_('Location where repositories are stored. After changing this value a restart, and rescan is required')}</span>
253 </div>
253 </div>
254 </div>
254 </div>
255
255
256 <div class="buttons">
256 <div class="buttons">
257 ${h.submit('save',_('Save settings'),class_="ui-btn large")}
257 ${h.submit('save',_('Save settings'),class_="ui-btn large")}
258 ${h.reset('reset',_('Reset'),class_="ui-btn large")}
258 ${h.reset('reset',_('Reset'),class_="ui-btn large")}
259 </div>
259 </div>
260 </div>
260 </div>
261 </div>
261 </div>
262 ${h.end_form()}
262 ${h.end_form()}
263
263
264 <script type="text/javascript">
264 <script type="text/javascript">
265 YAHOO.util.Event.onDOMReady(function(){
265 YAHOO.util.Event.onDOMReady(function(){
266 YAHOO.util.Event.addListener('path_unlock','click',function(){
266 YAHOO.util.Event.addListener('path_unlock','click',function(){
267 YAHOO.util.Dom.get('paths_root_path').removeAttribute('readonly');
267 YAHOO.util.Dom.get('paths_root_path').removeAttribute('readonly');
268 });
268 });
269 });
269 });
270 </script>
270 </script>
271
271
272 <h3>${_('Test Email')}</h3>
272 <h3>${_('Test Email')}</h3>
273 ${h.form(url('admin_setting', setting_id='email'),method='put')}
273 ${h.form(url('admin_setting', setting_id='email'),method='put')}
274 <div class="form">
274 <div class="form">
275 <!-- fields -->
275 <!-- fields -->
276
276
277 <div class="fields">
277 <div class="fields">
278 <div class="field">
278 <div class="field">
279 <div class="label">
279 <div class="label">
280 <label for="test_email">${_('Email to')}:</label>
280 <label for="test_email">${_('Email to')}:</label>
281 </div>
281 </div>
282 <div class="input">
282 <div class="input">
283 ${h.text('test_email',size=30)}
283 ${h.text('test_email',size=30)}
284 </div>
284 </div>
285 </div>
285 </div>
286
286
287 <div class="buttons">
287 <div class="buttons">
288 ${h.submit('send',_('Send'),class_="ui-btn large")}
288 ${h.submit('send',_('Send'),class_="ui-btn large")}
289 </div>
289 </div>
290 </div>
290 </div>
291 </div>
291 </div>
292 ${h.end_form()}
292 ${h.end_form()}
293
293
294 <h3>${_('System Info and Packages')}</h3>
294 <h3>${_('System Info and Packages')}</h3>
295 <div class="form">
295 <div class="form">
296 <div>
296 <div>
297 <h5 id="expand_modules" style="cursor: pointer">&darr; ${_('show')} &darr;</h5>
297 <h5 id="expand_modules" style="cursor: pointer">&darr; ${_('show')} &darr;</h5>
298 </div>
298 </div>
299 <div id="expand_modules_table" style="display:none">
299 <div id="expand_modules_table" style="display:none">
300 <h5>Python - ${c.py_version}</h5>
300 <h5>Python - ${c.py_version}</h5>
301 <h5>System - ${c.platform}</h5>
301 <h5>System - ${c.platform}</h5>
302
302
303 <table class="table" style="margin:0px 0px 0px 20px">
303 <table class="table" style="margin:0px 0px 0px 20px">
304 <colgroup>
304 <colgroup>
305 <col style="width:220px">
305 <col style="width:220px">
306 </colgroup>
306 </colgroup>
307 <tbody>
307 <tbody>
308 %for key, value in c.modules:
308 %for key, value in c.modules:
309 <tr>
309 <tr>
310 <th style="text-align: right;padding-right:5px;">${key}</th>
310 <th style="text-align: right;padding-right:5px;">${key}</th>
311 <td>${value}</td>
311 <td>${value}</td>
312 </tr>
312 </tr>
313 %endfor
313 %endfor
314 </tbody>
314 </tbody>
315 </table>
315 </table>
316 </div>
316 </div>
317 </div>
317 </div>
318
318
319 <script type="text/javascript">
319 <script type="text/javascript">
320 YUE.on('expand_modules','click',function(e){
320 YUE.on('expand_modules','click',function(e){
321 YUD.setStyle('expand_modules_table','display','');
321 YUD.setStyle('expand_modules_table','display','');
322 YUD.setStyle('expand_modules','display','none');
322 YUD.setStyle('expand_modules','display','none');
323 })
323 })
324 </script>
324 </script>
325
325
326 </div>
326 </div>
327 </%def>
327 </%def>
@@ -1,284 +1,284 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2 <%inherit file="/base/base.html"/>
2 <%inherit file="/base/base.html"/>
3
3
4 <%def name="title()">
4 <%def name="title()">
5 ${_('Edit user')} ${c.user.username} - ${c.rhodecode_name}
5 ${_('Edit user')} ${c.user.username} - ${c.rhodecode_name}
6 </%def>
6 </%def>
7
7
8 <%def name="breadcrumbs_links()">
8 <%def name="breadcrumbs_links()">
9 ${h.link_to(_('Admin'),h.url('admin_home'))}
9 ${h.link_to(_('Admin'),h.url('admin_home'))}
10 &raquo;
10 &raquo;
11 ${h.link_to(_('Users'),h.url('users'))}
11 ${h.link_to(_('Users'),h.url('users'))}
12 &raquo;
12 &raquo;
13 ${_('edit')} "${c.user.username}"
13 ${_('edit')} "${c.user.username}"
14 </%def>
14 </%def>
15
15
16 <%def name="page_nav()">
16 <%def name="page_nav()">
17 ${self.menu('admin')}
17 ${self.menu('admin')}
18 </%def>
18 </%def>
19
19
20 <%def name="main()">
20 <%def name="main()">
21 <div class="box box-left">
21 <div class="box box-left">
22 <!-- box / title -->
22 <!-- box / title -->
23 <div class="title">
23 <div class="title">
24 ${self.breadcrumbs()}
24 ${self.breadcrumbs()}
25 </div>
25 </div>
26 <!-- end box / title -->
26 <!-- end box / title -->
27 ${h.form(url('update_user', id=c.user.user_id),method='put')}
27 ${h.form(url('update_user', id=c.user.user_id),method='put')}
28 <div class="form">
28 <div class="form">
29 <div class="field">
29 <div class="field">
30 <div class="gravatar_box">
30 <div class="gravatar_box">
31 <div class="gravatar"><img alt="gravatar" src="${h.gravatar_url(c.user.email)}"/></div>
31 <div class="gravatar"><img alt="gravatar" src="${h.gravatar_url(c.user.email)}"/></div>
32 <p>
32 <p>
33 %if c.use_gravatar:
33 %if c.use_gravatar:
34 <strong>${_('Change your avatar at')} <a href="http://gravatar.com">gravatar.com</a></strong>
34 <strong>${_('Change your avatar at')} <a href="http://gravatar.com">gravatar.com</a></strong>
35 <br/>${_('Using')} ${c.user.email}
35 <br/>${_('Using')} ${c.user.email}
36 %else:
36 %else:
37 <br/>${c.user.email}
37 <br/>${c.user.email}
38 %endif
38 %endif
39 </div>
39 </div>
40 </div>
40 </div>
41 <div class="field">
41 <div class="field">
42 <div class="label">
42 <div class="label">
43 <label>${_('API key')}</label> ${c.user.api_key}
43 <label>${_('API key')}</label> ${c.user.api_key}
44 </div>
44 </div>
45 </div>
45 </div>
46
46
47 <div class="fields">
47 <div class="fields">
48 <div class="field">
48 <div class="field">
49 <div class="label">
49 <div class="label">
50 <label for="username">${_('Username')}:</label>
50 <label for="username">${_('Username')}:</label>
51 </div>
51 </div>
52 <div class="input">
52 <div class="input">
53 ${h.text('username',class_='medium')}
53 ${h.text('username',class_='medium')}
54 </div>
54 </div>
55 </div>
55 </div>
56
56
57 <div class="field">
57 <div class="field">
58 <div class="label">
58 <div class="label">
59 <label for="ldap_dn">${_('LDAP DN')}:</label>
59 <label for="ldap_dn">${_('LDAP DN')}:</label>
60 </div>
60 </div>
61 <div class="input">
61 <div class="input">
62 ${h.text('ldap_dn',class_='medium disabled',readonly="readonly")}
62 ${h.text('ldap_dn',class_='medium disabled',readonly="readonly")}
63 </div>
63 </div>
64 </div>
64 </div>
65
65
66 <div class="field">
66 <div class="field">
67 <div class="label">
67 <div class="label">
68 <label for="new_password">${_('New password')}:</label>
68 <label for="new_password">${_('New password')}:</label>
69 </div>
69 </div>
70 <div class="input">
70 <div class="input">
71 ${h.password('new_password',class_='medium',autocomplete="off")}
71 ${h.password('new_password',class_='medium',autocomplete="off")}
72 </div>
72 </div>
73 </div>
73 </div>
74
74
75 <div class="field">
75 <div class="field">
76 <div class="label">
76 <div class="label">
77 <label for="password_confirmation">${_('New password confirmation')}:</label>
77 <label for="password_confirmation">${_('New password confirmation')}:</label>
78 </div>
78 </div>
79 <div class="input">
79 <div class="input">
80 ${h.password('password_confirmation',class_="medium",autocomplete="off")}
80 ${h.password('password_confirmation',class_="medium",autocomplete="off")}
81 </div>
81 </div>
82 </div>
82 </div>
83
83
84 <div class="field">
84 <div class="field">
85 <div class="label">
85 <div class="label">
86 <label for="firstname">${_('First Name')}:</label>
86 <label for="firstname">${_('First Name')}:</label>
87 </div>
87 </div>
88 <div class="input">
88 <div class="input">
89 ${h.text('firstname',class_='medium')}
89 ${h.text('firstname',class_='medium')}
90 </div>
90 </div>
91 </div>
91 </div>
92
92
93 <div class="field">
93 <div class="field">
94 <div class="label">
94 <div class="label">
95 <label for="lastname">${_('Last Name')}:</label>
95 <label for="lastname">${_('Last Name')}:</label>
96 </div>
96 </div>
97 <div class="input">
97 <div class="input">
98 ${h.text('lastname',class_='medium')}
98 ${h.text('lastname',class_='medium')}
99 </div>
99 </div>
100 </div>
100 </div>
101
101
102 <div class="field">
102 <div class="field">
103 <div class="label">
103 <div class="label">
104 <label for="email">${_('Email')}:</label>
104 <label for="email">${_('Email')}:</label>
105 </div>
105 </div>
106 <div class="input">
106 <div class="input">
107 ${h.text('email',class_='medium')}
107 ${h.text('email',class_='medium')}
108 </div>
108 </div>
109 </div>
109 </div>
110
110
111 <div class="field">
111 <div class="field">
112 <div class="label label-checkbox">
112 <div class="label label-checkbox">
113 <label for="active">${_('Active')}:</label>
113 <label for="active">${_('Active')}:</label>
114 </div>
114 </div>
115 <div class="checkboxes">
115 <div class="checkboxes">
116 ${h.checkbox('active',value=True)}
116 ${h.checkbox('active',value=True)}
117 </div>
117 </div>
118 </div>
118 </div>
119
119
120 <div class="field">
120 <div class="field">
121 <div class="label label-checkbox">
121 <div class="label label-checkbox">
122 <label for="admin">${_('Admin')}:</label>
122 <label for="admin">${_('Admin')}:</label>
123 </div>
123 </div>
124 <div class="checkboxes">
124 <div class="checkboxes">
125 ${h.checkbox('admin',value=True)}
125 ${h.checkbox('admin',value=True)}
126 </div>
126 </div>
127 </div>
127 </div>
128 <div class="buttons">
128 <div class="buttons">
129 ${h.submit('save',_('Save'),class_="ui-btn large")}
129 ${h.submit('save',_('Save'),class_="ui-btn large")}
130 ${h.reset('reset',_('Reset'),class_="ui-btn large")}
130 ${h.reset('reset',_('Reset'),class_="ui-btn large")}
131 </div>
131 </div>
132 </div>
132 </div>
133 </div>
133 </div>
134 ${h.end_form()}
134 ${h.end_form()}
135 </div>
135 </div>
136 <div style="min-height:780px" class="box box-right">
136 <div style="min-height:780px" class="box box-right">
137 <!-- box / title -->
137 <!-- box / title -->
138 <div class="title">
138 <div class="title">
139 <h5>${_('Permissions')}</h5>
139 <h5>${_('Permissions')}</h5>
140 </div>
140 </div>
141 ${h.form(url('user_perm', id=c.user.user_id),method='put')}
141 ${h.form(url('user_perm', id=c.user.user_id),method='put')}
142 <div class="form">
142 <div class="form">
143 <!-- fields -->
143 <!-- fields -->
144 <div class="fields">
144 <div class="fields">
145 <div class="field">
145 <div class="field">
146 <div class="label label-checkbox">
146 <div class="label label-checkbox">
147 <label for="inherit_permissions">${_('Inherit default permissions')}:</label>
147 <label for="inherit_permissions">${_('Inherit default permissions')}:</label>
148 </div>
148 </div>
149 <div class="checkboxes">
149 <div class="checkboxes">
150 ${h.checkbox('inherit_default_permissions',value=True)}
150 ${h.checkbox('inherit_default_permissions',value=True)}
151 </div>
151 </div>
152 <span class="help-block">${h.literal(_('Select to inherit permissions from %s settings. '
152 <span class="help-block">${h.literal(_('Select to inherit permissions from %s settings. '
153 'With this selected below options does not have any action') % h.link_to('default', url('edit_permission', id='default')))}</span>
153 'With this selected below options does not have any action') % h.link_to('default', url('edit_permission', id='default')))}</span>
154 </div>
154 </div>
155 <div id="inherit_overlay" style="${'opacity:0.3' if c.user.inherit_default_permissions else ''}" >
155 <div id="inherit_overlay" style="${'opacity:0.3' if c.user.inherit_default_permissions else ''}" >
156 <div class="field">
156 <div class="field">
157 <div class="label label-checkbox">
157 <div class="label label-checkbox">
158 <label for="create_repo_perm">${_('Create repositories')}:</label>
158 <label for="create_repo_perm">${_('Create repositories')}:</label>
159 </div>
159 </div>
160 <div class="checkboxes">
160 <div class="checkboxes">
161 ${h.checkbox('create_repo_perm',value=True)}
161 ${h.checkbox('create_repo_perm',value=True)}
162 </div>
162 </div>
163 </div>
163 </div>
164 <div class="field">
164 <div class="field">
165 <div class="label label-checkbox">
165 <div class="label label-checkbox">
166 <label for="fork_repo_perm">${_('Fork repositories')}:</label>
166 <label for="fork_repo_perm">${_('Fork repositories')}:</label>
167 </div>
167 </div>
168 <div class="checkboxes">
168 <div class="checkboxes">
169 ${h.checkbox('fork_repo_perm',value=True)}
169 ${h.checkbox('fork_repo_perm',value=True)}
170 </div>
170 </div>
171 </div>
171 </div>
172 </div>
172 </div>
173 <div class="buttons">
173 <div class="buttons">
174 ${h.submit('save',_('Save'),class_="ui-btn large")}
174 ${h.submit('save',_('Save'),class_="ui-btn large")}
175 ${h.reset('reset',_('Reset'),class_="ui-btn large")}
175 ${h.reset('reset',_('Reset'),class_="ui-btn large")}
176 </div>
176 </div>
177 </div>
177 </div>
178 </div>
178 </div>
179 ${h.end_form()}
179 ${h.end_form()}
180
180
181 ## permissions overview
181 ## permissions overview
182 <div id="perms" class="table">
182 <div id="perms" class="table">
183 %for section in sorted(c.perm_user.permissions.keys()):
183 %for section in sorted(c.perm_user.permissions.keys()):
184 <div class="perms_section_head">${section.replace("_"," ").capitalize()}</div>
184 <div class="perms_section_head">${section.replace("_"," ").capitalize()}</div>
185 %if not c.perm_user.permissions[section]:
185 %if not c.perm_user.permissions[section]:
186 <span class="empty_data">${_('Nothing here yet')}</span>
186 <span class="empty_data">${_('Nothing here yet')}</span>
187 %else:
187 %else:
188 <div id='tbl_list_wrap_${section}' class="yui-skin-sam">
188 <div id='tbl_list_wrap_${section}' class="yui-skin-sam">
189 <table id="tbl_list_${section}">
189 <table id="tbl_list_${section}">
190 <thead>
190 <thead>
191 <tr>
191 <tr>
192 <th class="left">${_('Name')}</th>
192 <th class="left">${_('Name')}</th>
193 <th class="left">${_('Permission')}</th>
193 <th class="left">${_('Permission')}</th>
194 <th class="left">${_('Edit Permission')}</th>
194 <th class="left">${_('Edit Permission')}</th>
195 </thead>
195 </thead>
196 <tbody>
196 <tbody>
197 %for k in c.perm_user.permissions[section]:
197 %for k in c.perm_user.permissions[section]:
198 <%
198 <%
199 if section != 'global':
199 if section != 'global':
200 section_perm = c.perm_user.permissions[section].get(k)
200 section_perm = c.perm_user.permissions[section].get(k)
201 _perm = section_perm.split('.')[-1]
201 _perm = section_perm.split('.')[-1]
202 else:
202 else:
203 _perm = section_perm = None
203 _perm = section_perm = None
204 %>
204 %>
205 <tr>
205 <tr>
206 <td>
206 <td>
207 %if section == 'repositories':
207 %if section == 'repositories':
208 <a href="${h.url('summary_home',repo_name=k)}">${k}</a>
208 <a href="${h.url('summary_home',repo_name=k)}">${k}</a>
209 %elif section == 'repositories_groups':
209 %elif section == 'repositories_groups':
210 <a href="${h.url('repos_group_home',group_name=k)}">${k}</a>
210 <a href="${h.url('repos_group_home',group_name=k)}">${k}</a>
211 %else:
211 %else:
212 ${h.get_permission_name(k)}
212 ${h.get_permission_name(k)}
213 %endif
213 %endif
214 </td>
214 </td>
215 <td>
215 <td>
216 %if section == 'global':
216 %if section == 'global':
217 ${h.bool2icon(k.split('.')[-1] != 'none')}
217 ${h.bool2icon(k.split('.')[-1] != 'none')}
218 %else:
218 %else:
219 <span class="perm_tag ${_perm}">${section_perm}</span>
219 <span class="perm_tag ${_perm}">${section_perm}</span>
220 %endif
220 %endif
221 </td>
221 </td>
222 <td>
222 <td>
223 %if section == 'repositories':
223 %if section == 'repositories':
224 <a href="${h.url('edit_repo',repo_name=k,anchor='permissions_manage')}">${_('edit')}</a>
224 <a href="${h.url('edit_repo',repo_name=k,anchor='permissions_manage')}">${_('edit')}</a>
225 %elif section == 'repositories_groups':
225 %elif section == 'repositories_groups':
226 <a href="${h.url('edit_repos_group',id=k,anchor='permissions_manage')}">${_('edit')}</a>
226 <a href="${h.url('edit_repos_group',id=k,anchor='permissions_manage')}">${_('edit')}</a>
227 %else:
227 %else:
228 --
228 --
229 %endif
229 %endif
230 </td>
230 </td>
231 </tr>
231 </tr>
232 %endfor
232 %endfor
233 </tbody>
233 </tbody>
234 </table>
234 </table>
235 </div>
235 </div>
236 %endif
236 %endif
237 %endfor
237 %endfor
238 </div>
238 </div>
239 </div>
239 </div>
240 <div class="box box-left">
240 <div class="box box-left">
241 <!-- box / title -->
241 <!-- box / title -->
242 <div class="title">
242 <div class="title">
243 <h5>${_('Email addresses')}</h5>
243 <h5>${_('Email addresses')}</h5>
244 </div>
244 </div>
245
245
246 <div class="emails_wrap">
246 <div class="emails_wrap">
247 <table class="noborder">
247 <table class="noborder">
248 %for em in c.user_email_map:
248 %for em in c.user_email_map:
249 <tr>
249 <tr>
250 <td><div class="gravatar"><img alt="gravatar" src="${h.gravatar_url(em.user.email,16)}"/> </div></td>
250 <td><div class="gravatar"><img alt="gravatar" src="${h.gravatar_url(em.user.email,16)}"/> </div></td>
251 <td><div class="email">${em.email}</div></td>
251 <td><div class="email">${em.email}</div></td>
252 <td>
252 <td>
253 ${h.form(url('user_emails_delete', id=c.user.user_id),method='delete')}
253 ${h.form(url('user_emails_delete', id=c.user.user_id),method='delete')}
254 ${h.hidden('del_email',em.email_id)}
254 ${h.hidden('del_email',em.email_id)}
255 ${h.submit('remove_',_('delete'),id="remove_email_%s" % em.email_id,
255 ${h.submit('remove_',_('delete'),id="remove_email_%s" % em.email_id,
256 class_="delete_icon action_button", onclick="return confirm('"+_('Confirm to delete this email: %s') % em.email+"');")}
256 class_="delete_icon action_button", onclick="return confirm('"+_('Confirm to delete this email: %s') % em.email+"');")}
257 ${h.end_form()}
257 ${h.end_form()}
258 </td>
258 </td>
259 </tr>
259 </tr>
260 %endfor
260 %endfor
261 </table>
261 </table>
262 </div>
262 </div>
263
263
264 ${h.form(url('user_emails', id=c.user.user_id),method='put')}
264 ${h.form(url('user_emails', id=c.user.user_id),method='put')}
265 <div class="form">
265 <div class="form">
266 <!-- fields -->
266 <!-- fields -->
267 <div class="fields">
267 <div class="fields">
268 <div class="field">
268 <div class="field">
269 <div class="label">
269 <div class="label">
270 <label for="email">${_('New email address')}:</label>
270 <label for="email">${_('New email address')}:</label>
271 </div>
271 </div>
272 <div class="input">
272 <div class="input">
273 ${h.text('new_email', class_='medium')}
273 ${h.text('new_email', class_='medium')}
274 </div>
274 </div>
275 </div>
275 </div>
276 <div class="buttons">
276 <div class="buttons">
277 ${h.submit('save',_('Add'),class_="ui-btn large")}
277 ${h.submit('save',_('Add'),class_="ui-btn large")}
278 ${h.reset('reset',_('Reset'),class_="ui-btn large")}
278 ${h.reset('reset',_('Reset'),class_="ui-btn large")}
279 </div>
279 </div>
280 </div>
280 </div>
281 </div>
281 </div>
282 ${h.end_form()}
282 ${h.end_form()}
283 </div>
283 </div>
284 </%def>
284 </%def>
@@ -1,165 +1,165 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2 <%inherit file="/base/base.html"/>
2 <%inherit file="/base/base.html"/>
3
3
4 <%def name="title()">
4 <%def name="title()">
5 ${_('Edit users group')} ${c.users_group.users_group_name} - ${c.rhodecode_name}
5 ${_('Edit users group')} ${c.users_group.users_group_name} - ${c.rhodecode_name}
6 </%def>
6 </%def>
7
7
8 <%def name="breadcrumbs_links()">
8 <%def name="breadcrumbs_links()">
9 ${h.link_to(_('Admin'),h.url('admin_home'))}
9 ${h.link_to(_('Admin'),h.url('admin_home'))}
10 &raquo;
10 &raquo;
11 ${h.link_to(_('UsersGroups'),h.url('users_groups'))}
11 ${h.link_to(_('UsersGroups'),h.url('users_groups'))}
12 &raquo;
12 &raquo;
13 ${_('edit')} "${c.users_group.users_group_name}"
13 ${_('edit')} "${c.users_group.users_group_name}"
14 </%def>
14 </%def>
15
15
16 <%def name="page_nav()">
16 <%def name="page_nav()">
17 ${self.menu('admin')}
17 ${self.menu('admin')}
18 </%def>
18 </%def>
19
19
20 <%def name="main()">
20 <%def name="main()">
21 <div class="box box-left">
21 <div class="box box-left">
22 <!-- box / title -->
22 <!-- box / title -->
23 <div class="title">
23 <div class="title">
24 ${self.breadcrumbs()}
24 ${self.breadcrumbs()}
25 </div>
25 </div>
26 <!-- end box / title -->
26 <!-- end box / title -->
27 ${h.form(url('users_group', id=c.users_group.users_group_id),method='put', id='edit_users_group')}
27 ${h.form(url('users_group', id=c.users_group.users_group_id),method='put', id='edit_users_group')}
28 <div class="form">
28 <div class="form">
29 <!-- fields -->
29 <!-- fields -->
30 <div class="fields">
30 <div class="fields">
31 <div class="field">
31 <div class="field">
32 <div class="label">
32 <div class="label">
33 <label for="users_group_name">${_('Group name')}:</label>
33 <label for="users_group_name">${_('Group name')}:</label>
34 </div>
34 </div>
35 <div class="input">
35 <div class="input">
36 ${h.text('users_group_name',class_='small')}
36 ${h.text('users_group_name',class_='small')}
37 </div>
37 </div>
38 </div>
38 </div>
39
39
40 <div class="field">
40 <div class="field">
41 <div class="label label-checkbox">
41 <div class="label label-checkbox">
42 <label for="users_group_active">${_('Active')}:</label>
42 <label for="users_group_active">${_('Active')}:</label>
43 </div>
43 </div>
44 <div class="checkboxes">
44 <div class="checkboxes">
45 ${h.checkbox('users_group_active',value=True)}
45 ${h.checkbox('users_group_active',value=True)}
46 </div>
46 </div>
47 </div>
47 </div>
48 <div class="field">
48 <div class="field">
49 <div class="label">
49 <div class="label">
50 <label for="users_group_active">${_('Members')}:</label>
50 <label for="users_group_active">${_('Members')}:</label>
51 </div>
51 </div>
52 <div class="select">
52 <div class="select">
53 <table>
53 <table>
54 <tr>
54 <tr>
55 <td>
55 <td>
56 <div>
56 <div>
57 <div style="float:left">
57 <div style="float:left">
58 <div class="text" style="padding: 0px 0px 6px;">${_('Choosen group members')}</div>
58 <div class="text" style="padding: 0px 0px 6px;">${_('Choosen group members')}</div>
59 ${h.select('users_group_members',[x[0] for x in c.group_members],c.group_members,multiple=True,size=8,style="min-width:210px")}
59 ${h.select('users_group_members',[x[0] for x in c.group_members],c.group_members,multiple=True,size=8,style="min-width:210px")}
60 <div id="remove_all_elements" style="cursor:pointer;text-align:center">
60 <div id="remove_all_elements" style="cursor:pointer;text-align:center">
61 ${_('Remove all elements')}
61 ${_('Remove all elements')}
62 <img alt="remove" style="vertical-align:text-bottom" src="${h.url('/images/icons/arrow_right.png')}"/>
62 <img alt="remove" style="vertical-align:text-bottom" src="${h.url('/images/icons/arrow_right.png')}"/>
63 </div>
63 </div>
64 </div>
64 </div>
65 <div style="float:left;width:20px;padding-top:50px">
65 <div style="float:left;width:20px;padding-top:50px">
66 <img alt="add" id="add_element"
66 <img alt="add" id="add_element"
67 style="padding:2px;cursor:pointer"
67 style="padding:2px;cursor:pointer"
68 src="${h.url('/images/icons/arrow_left.png')}"/>
68 src="${h.url('/images/icons/arrow_left.png')}"/>
69 <br />
69 <br />
70 <img alt="remove" id="remove_element"
70 <img alt="remove" id="remove_element"
71 style="padding:2px;cursor:pointer"
71 style="padding:2px;cursor:pointer"
72 src="${h.url('/images/icons/arrow_right.png')}"/>
72 src="${h.url('/images/icons/arrow_right.png')}"/>
73 </div>
73 </div>
74 <div style="float:left">
74 <div style="float:left">
75 <div class="text" style="padding: 0px 0px 6px;">${_('Available members')}</div>
75 <div class="text" style="padding: 0px 0px 6px;">${_('Available members')}</div>
76 ${h.select('available_members',[],c.available_members,multiple=True,size=8,style="min-width:210px")}
76 ${h.select('available_members',[],c.available_members,multiple=True,size=8,style="min-width:210px")}
77 <div id="add_all_elements" style="cursor:pointer;text-align:center">
77 <div id="add_all_elements" style="cursor:pointer;text-align:center">
78 <img alt="add" style="vertical-align:text-bottom" src="${h.url('/images/icons/arrow_left.png')}"/>
78 <img alt="add" style="vertical-align:text-bottom" src="${h.url('/images/icons/arrow_left.png')}"/>
79 ${_('Add all elements')}
79 ${_('Add all elements')}
80 </div>
80 </div>
81 </div>
81 </div>
82 </div>
82 </div>
83 </td>
83 </td>
84 </tr>
84 </tr>
85 </table>
85 </table>
86 </div>
86 </div>
87
87
88 </div>
88 </div>
89 <div class="buttons">
89 <div class="buttons">
90 ${h.submit('save',_('save'),class_="ui-btn large")}
90 ${h.submit('save',_('save'),class_="ui-btn large")}
91 </div>
91 </div>
92 </div>
92 </div>
93 </div>
93 </div>
94 ${h.end_form()}
94 ${h.end_form()}
95 </div>
95 </div>
96
96
97 <div class="box box-right">
97 <div class="box box-right">
98 <!-- box / title -->
98 <!-- box / title -->
99 <div class="title">
99 <div class="title">
100 <h5>${_('Permissions')}</h5>
100 <h5>${_('Permissions')}</h5>
101 </div>
101 </div>
102 ${h.form(url('users_group_perm', id=c.users_group.users_group_id), method='put')}
102 ${h.form(url('users_group_perm', id=c.users_group.users_group_id), method='put')}
103 <div class="form">
103 <div class="form">
104 <!-- fields -->
104 <!-- fields -->
105 <div class="fields">
105 <div class="fields">
106 <div class="field">
106 <div class="field">
107 <div class="label label-checkbox">
107 <div class="label label-checkbox">
108 <label for="inherit_permissions">${_('Inherit default permissions')}:</label>
108 <label for="inherit_permissions">${_('Inherit default permissions')}:</label>
109 </div>
109 </div>
110 <div class="checkboxes">
110 <div class="checkboxes">
111 ${h.checkbox('inherit_default_permissions',value=True)}
111 ${h.checkbox('inherit_default_permissions',value=True)}
112 </div>
112 </div>
113 <span class="help-block">${h.literal(_('Select to inherit permissions from %s settings. '
113 <span class="help-block">${h.literal(_('Select to inherit permissions from %s settings. '
114 'With this selected below options does not have any action') % h.link_to('default', url('edit_permission', id='default')))}</span>
114 'With this selected below options does not have any action') % h.link_to('default', url('edit_permission', id='default')))}</span>
115 </div>
115 </div>
116 <div id="inherit_overlay" style="${'opacity:0.3' if c.users_group.inherit_default_permissions else ''}" >
116 <div id="inherit_overlay" style="${'opacity:0.3' if c.users_group.inherit_default_permissions else ''}" >
117 <div class="field">
117 <div class="field">
118 <div class="label label-checkbox">
118 <div class="label label-checkbox">
119 <label for="create_repo_perm">${_('Create repositories')}:</label>
119 <label for="create_repo_perm">${_('Create repositories')}:</label>
120 </div>
120 </div>
121 <div class="checkboxes">
121 <div class="checkboxes">
122 ${h.checkbox('create_repo_perm',value=True)}
122 ${h.checkbox('create_repo_perm',value=True)}
123 </div>
123 </div>
124 </div>
124 </div>
125 <div class="field">
125 <div class="field">
126 <div class="label label-checkbox">
126 <div class="label label-checkbox">
127 <label for="fork_repo_perm">${_('Fork repositories')}:</label>
127 <label for="fork_repo_perm">${_('Fork repositories')}:</label>
128 </div>
128 </div>
129 <div class="checkboxes">
129 <div class="checkboxes">
130 ${h.checkbox('fork_repo_perm',value=True)}
130 ${h.checkbox('fork_repo_perm',value=True)}
131 </div>
131 </div>
132 </div>
132 </div>
133 </div>
133 </div>
134 <div class="buttons">
134 <div class="buttons">
135 ${h.submit('save',_('Save'),class_="ui-btn large")}
135 ${h.submit('save',_('Save'),class_="ui-btn large")}
136 ${h.reset('reset',_('Reset'),class_="ui-btn large")}
136 ${h.reset('reset',_('Reset'),class_="ui-btn large")}
137 </div>
137 </div>
138 </div>
138 </div>
139 </div>
139 </div>
140 ${h.end_form()}
140 ${h.end_form()}
141 </div>
141 </div>
142
142
143 <div class="box box-right">
143 <div class="box box-right">
144 <!-- box / title -->
144 <!-- box / title -->
145 <div class="title">
145 <div class="title">
146 <h5>${_('Group members')}</h5>
146 <h5>${_('Group members')}</h5>
147 </div>
147 </div>
148 <div class="group_members_wrap">
148 <div class="group_members_wrap">
149 <ul class="group_members">
149 <ul class="group_members">
150 %for user in c.group_members_obj:
150 %for user in c.group_members_obj:
151 <li>
151 <li>
152 <div class="group_member">
152 <div class="group_member">
153 <div class="gravatar"><img alt="gravatar" src="${h.gravatar_url(user.email,24)}"/> </div>
153 <div class="gravatar"><img alt="gravatar" src="${h.gravatar_url(user.email,24)}"/> </div>
154 <div>${user.username}</div>
154 <div>${user.username}</div>
155 <div>${user.full_name}</div>
155 <div>${user.full_name}</div>
156 </div>
156 </div>
157 </li>
157 </li>
158 %endfor
158 %endfor
159 </ul>
159 </ul>
160 </div>
160 </div>
161 </div>
161 </div>
162 <script type="text/javascript">
162 <script type="text/javascript">
163 MultiSelectWidget('users_group_members','available_members','edit_users_group');
163 MultiSelectWidget('users_group_members','available_members','edit_users_group');
164 </script>
164 </script>
165 </%def>
165 </%def>
@@ -1,20 +1,20 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2 <%inherit file="main.html"/>
2 <%inherit file="main.html"/>
3
3
4 User <b>${pr_user_created}</b> opened pull request for repository
4 User <b>${pr_user_created}</b> opened pull request for repository
5 ${pr_repo_url} and wants you to review changes.
5 ${pr_repo_url} and wants you to review changes.
6
6
7 <div>title: ${pr_title}</div>
7 <div>title: ${pr_title}</div>
8 <div>description:</div>
8 <div>description:</div>
9 <p>
9 <p>
10 ${body}
10 ${body}
11 </p>
11 </p>
12
12
13 <div>revisions for reviewing</div>
13 <div>revisions for reviewing</div>
14 <ul>
14 <ul>
15 %for r in pr_revisions:
15 %for r in pr_revisions:
16 <li>${r}</li>
16 <li>${r}</li>
17 %endfor
17 %endfor
18 </ul>
18 </ul>
19
19
20 View this pull request here: ${pr_url}
20 View this pull request here: ${pr_url}
@@ -1,15 +1,15 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2 <%inherit file="main.html"/>
2 <%inherit file="main.html"/>
3
3
4 User <b>${pr_comment_user}</b> commented on pull request #${pr_id} for
4 User <b>${pr_comment_user}</b> commented on pull request #${pr_id} for
5 repository ${pr_target_repo}
5 repository ${pr_target_repo}
6
6
7 <p>
7 <p>
8 ${body}
8 ${body}
9
9
10 %if status_change:
10 %if status_change:
11 <span>New status -> ${status_change}</span>
11 <span>New status -> ${status_change}</span>
12 %endif
12 %endif
13 </p>
13 </p>
14
14
15 View this comment here: ${pr_comment_url}
15 View this comment here: ${pr_comment_url}
@@ -1,142 +1,142 b''
1 <%inherit file="/base/base.html"/>
1 <%inherit file="/base/base.html"/>
2
2
3 <%def name="title()">
3 <%def name="title()">
4 ${_('%s files') % c.repo_name} - ${c.rhodecode_name}
4 ${_('%s files') % c.repo_name} - ${c.rhodecode_name}
5 </%def>
5 </%def>
6
6
7 <%def name="breadcrumbs_links()">
7 <%def name="breadcrumbs_links()">
8 ${h.link_to(_(u'Home'),h.url('/'))}
8 ${h.link_to(_(u'Home'),h.url('/'))}
9 &raquo;
9 &raquo;
10 ${h.link_to(c.repo_name,h.url('files_home',repo_name=c.repo_name))}
10 ${h.link_to(c.repo_name,h.url('files_home',repo_name=c.repo_name))}
11 &raquo;
11 &raquo;
12 ${_('files')}
12 ${_('files')}
13 %if c.file:
13 %if c.file:
14 @ r${c.changeset.revision}:${h.short_id(c.changeset.raw_id)}
14 @ r${c.changeset.revision}:${h.short_id(c.changeset.raw_id)}
15 %endif
15 %endif
16 </%def>
16 </%def>
17
17
18 <%def name="page_nav()">
18 <%def name="page_nav()">
19 ${self.menu('files')}
19 ${self.menu('files')}
20 </%def>
20 </%def>
21
21
22 <%def name="main()">
22 <%def name="main()">
23 <div class="box">
23 <div class="box">
24 <!-- box / title -->
24 <!-- box / title -->
25 <div class="title">
25 <div class="title">
26 ${self.breadcrumbs()}
26 ${self.breadcrumbs()}
27 <ul class="links">
27 <ul class="links">
28 <li>
28 <li>
29 <span style="text-transform: uppercase;"><a href="#">${_('branch')}: ${c.changeset.branch}</a></span>
29 <span style="text-transform: uppercase;"><a href="#">${_('branch')}: ${c.changeset.branch}</a></span>
30 </li>
30 </li>
31 </ul>
31 </ul>
32 </div>
32 </div>
33 <div class="table">
33 <div class="table">
34 <div id="files_data">
34 <div id="files_data">
35 <%include file='files_ypjax.html'/>
35 <%include file='files_ypjax.html'/>
36 </div>
36 </div>
37 </div>
37 </div>
38 </div>
38 </div>
39
39
40 <script type="text/javascript">
40 <script type="text/javascript">
41 var CACHE = {};
41 var CACHE = {};
42 var CACHE_EXPIRE = 60*1000; //cache for 60s
42 var CACHE_EXPIRE = 60*1000; //cache for 60s
43 //used to construct links from the search list
43 //used to construct links from the search list
44 var node_list_url = '${h.url("files_home",repo_name=c.repo_name,revision='__REV__',f_path='__FPATH__')}';
44 var node_list_url = '${h.url("files_home",repo_name=c.repo_name,revision='__REV__',f_path='__FPATH__')}';
45 //send the nodelist request to this url
45 //send the nodelist request to this url
46 var url_base = '${h.url("files_nodelist_home",repo_name=c.repo_name,revision='__REV__',f_path='__FPATH__')}';
46 var url_base = '${h.url("files_nodelist_home",repo_name=c.repo_name,revision='__REV__',f_path='__FPATH__')}';
47
47
48 var ypjax_links = function(){
48 var ypjax_links = function(){
49 YUE.on(YUQ('.ypjax-link'), 'click',function(e){
49 YUE.on(YUQ('.ypjax-link'), 'click',function(e){
50
50
51 //don't do ypjax on middle click
51 //don't do ypjax on middle click
52 if(e.which == 2 || !History.enabled){
52 if(e.which == 2 || !History.enabled){
53 return true;
53 return true;
54 }
54 }
55
55
56 var el = e.currentTarget;
56 var el = e.currentTarget;
57 var url = el.href;
57 var url = el.href;
58
58
59 var _base_url = '${h.url("files_home",repo_name=c.repo_name,revision='',f_path='')}';
59 var _base_url = '${h.url("files_home",repo_name=c.repo_name,revision='',f_path='')}';
60 _base_url = _base_url.replace('//','/')
60 _base_url = _base_url.replace('//','/')
61
61
62 //extract rev and the f_path from url.
62 //extract rev and the f_path from url.
63 parts = url.split(_base_url)
63 parts = url.split(_base_url)
64 if(parts.length != 2){
64 if(parts.length != 2){
65 return false;
65 return false;
66 }
66 }
67
67
68 var parts2 = parts[1].split('/');
68 var parts2 = parts[1].split('/');
69 var rev = parts2.shift(); // pop the first element which is the revision
69 var rev = parts2.shift(); // pop the first element which is the revision
70 var f_path = parts2.join('/');
70 var f_path = parts2.join('/');
71
71
72 var title = "${_('%s files') % c.repo_name}" + " - " + f_path;
72 var title = "${_('%s files') % c.repo_name}" + " - " + f_path;
73
73
74 var _node_list_url = node_list_url.replace('__REV__',rev);
74 var _node_list_url = node_list_url.replace('__REV__',rev);
75 var _url_base = url_base.replace('__REV__',rev).replace('__FPATH__', f_path);
75 var _url_base = url_base.replace('__REV__',rev).replace('__FPATH__', f_path);
76
76
77 // Change our States and save some data for handling events
77 // Change our States and save some data for handling events
78 var data = {url:url,title:title, url_base:_url_base,
78 var data = {url:url,title:title, url_base:_url_base,
79 node_list_url:_node_list_url};
79 node_list_url:_node_list_url};
80 History.pushState(data, title, url);
80 History.pushState(data, title, url);
81
81
82 //now we're sure that we can do ypjax things
82 //now we're sure that we can do ypjax things
83 YUE.preventDefault(e)
83 YUE.preventDefault(e)
84 return false;
84 return false;
85 });
85 });
86 }
86 }
87
87
88 var callbacks = function(State){
88 var callbacks = function(State){
89 ypjax_links();
89 ypjax_links();
90 tooltip_activate();
90 tooltip_activate();
91 fileBrowserListeners(State.url, State.data.node_list_url, State.data.url_base);
91 fileBrowserListeners(State.url, State.data.node_list_url, State.data.url_base);
92 // Inform Google Analytics of the change
92 // Inform Google Analytics of the change
93 if ( typeof window.pageTracker !== 'undefined' ) {
93 if ( typeof window.pageTracker !== 'undefined' ) {
94 window.pageTracker._trackPageview(State.url);
94 window.pageTracker._trackPageview(State.url);
95 }
95 }
96 }
96 }
97
97
98 YUE.onDOMReady(function(){
98 YUE.onDOMReady(function(){
99 ypjax_links();
99 ypjax_links();
100 var container = 'files_data';
100 var container = 'files_data';
101 //Bind to StateChange Event
101 //Bind to StateChange Event
102 History.Adapter.bind(window,'statechange',function(){
102 History.Adapter.bind(window,'statechange',function(){
103 var State = History.getState();
103 var State = History.getState();
104 cache_key = State.url;
104 cache_key = State.url;
105 //check if we have this request in cache maybe ?
105 //check if we have this request in cache maybe ?
106 var _cache_obj = CACHE[cache_key];
106 var _cache_obj = CACHE[cache_key];
107 var _cur_time = new Date().getTime();
107 var _cur_time = new Date().getTime();
108 // get from cache if it's there and not yet expired !
108 // get from cache if it's there and not yet expired !
109 if(_cache_obj !== undefined && _cache_obj[0] > _cur_time){
109 if(_cache_obj !== undefined && _cache_obj[0] > _cur_time){
110 YUD.get(container).innerHTML=_cache_obj[1];
110 YUD.get(container).innerHTML=_cache_obj[1];
111 YUD.setStyle(container,'opacity','1.0');
111 YUD.setStyle(container,'opacity','1.0');
112
112
113 //callbacks after ypjax call
113 //callbacks after ypjax call
114 callbacks(State);
114 callbacks(State);
115 }
115 }
116 else{
116 else{
117 ypjax(State.url,container,function(o){
117 ypjax(State.url,container,function(o){
118 //callbacks after ypjax call
118 //callbacks after ypjax call
119 callbacks(State);
119 callbacks(State);
120 if (o !== undefined){
120 if (o !== undefined){
121 //store our request in cache
121 //store our request in cache
122 var _expire_on = new Date().getTime()+CACHE_EXPIRE;
122 var _expire_on = new Date().getTime()+CACHE_EXPIRE;
123 CACHE[cache_key] = [_expire_on, o.responseText];
123 CACHE[cache_key] = [_expire_on, o.responseText];
124 }
124 }
125 });
125 });
126 }
126 }
127 });
127 });
128
128
129 // init the search filter
129 // init the search filter
130 var _State = {
130 var _State = {
131 url: "${h.url.current()}",
131 url: "${h.url.current()}",
132 data: {
132 data: {
133 node_list_url: node_list_url.replace('__REV__',"${c.changeset.raw_id}"),
133 node_list_url: node_list_url.replace('__REV__',"${c.changeset.raw_id}"),
134 url_base: url_base.replace('__REV__',"${c.changeset.raw_id}").replace('__FPATH__', "${h.safe_unicode(c.file.path)}")
134 url_base: url_base.replace('__REV__',"${c.changeset.raw_id}").replace('__FPATH__', "${h.safe_unicode(c.file.path)}")
135 }
135 }
136 }
136 }
137 fileBrowserListeners(_State.url, _State.data.node_list_url, _State.data.url_base);
137 fileBrowserListeners(_State.url, _State.data.node_list_url, _State.data.url_base);
138 });
138 });
139
139
140 </script>
140 </script>
141
141
142 </%def>
142 </%def>
@@ -1,18 +1,18 b''
1 %if c.file:
1 %if c.file:
2 <h3 class="files_location">
2 <h3 class="files_location">
3 ${_('Location')}: ${h.files_breadcrumbs(c.repo_name,c.changeset.raw_id,c.file.path)}
3 ${_('Location')}: ${h.files_breadcrumbs(c.repo_name,c.changeset.raw_id,c.file.path)}
4 %if c.annotate:
4 %if c.annotate:
5 - ${_('annotation')}
5 - ${_('annotation')}
6 %endif
6 %endif
7 </h3>
7 </h3>
8 %if c.file.is_dir():
8 %if c.file.is_dir():
9 <%include file='files_browser.html'/>
9 <%include file='files_browser.html'/>
10 %else:
10 %else:
11 <%include file='files_source.html'/>
11 <%include file='files_source.html'/>
12 %endif
12 %endif
13 %else:
13 %else:
14 <h2>
14 <h2>
15 <a href="#" onClick="javascript:parent.history.back();" target="main">${_('Go back')}</a>
15 <a href="#" onClick="javascript:parent.history.back();" target="main">${_('Go back')}</a>
16 ${_('No files at given path')}: "${c.f_path or "/"}"
16 ${_('No files at given path')}: "${c.f_path or "/"}"
17 </h2>
17 </h2>
18 %endif
18 %endif
@@ -1,195 +1,195 b''
1 <%inherit file="/base/base.html"/>
1 <%inherit file="/base/base.html"/>
2
2
3 <%def name="title()">
3 <%def name="title()">
4 ${c.repo_name} ${_('Pull request #%s') % c.pull_request.pull_request_id}
4 ${c.repo_name} ${_('Pull request #%s') % c.pull_request.pull_request_id}
5 </%def>
5 </%def>
6
6
7 <%def name="breadcrumbs_links()">
7 <%def name="breadcrumbs_links()">
8 ${h.link_to(_(u'Home'),h.url('/'))}
8 ${h.link_to(_(u'Home'),h.url('/'))}
9 &raquo;
9 &raquo;
10 ${h.link_to(c.repo_name,h.url('changelog_home',repo_name=c.repo_name))}
10 ${h.link_to(c.repo_name,h.url('changelog_home',repo_name=c.repo_name))}
11 &raquo;
11 &raquo;
12 ${_('Pull request #%s') % c.pull_request.pull_request_id}
12 ${_('Pull request #%s') % c.pull_request.pull_request_id}
13 </%def>
13 </%def>
14
14
15 <%def name="main()">
15 <%def name="main()">
16
16
17 <div class="box">
17 <div class="box">
18 <!-- box / title -->
18 <!-- box / title -->
19 <div class="title">
19 <div class="title">
20 ${self.breadcrumbs()}
20 ${self.breadcrumbs()}
21 </div>
21 </div>
22 %if c.pull_request.is_closed():
22 %if c.pull_request.is_closed():
23 <div style="padding:10px; font-size:22px;width:100%;text-align: center; color:#88D882">${_('Closed %s') % (h.age(c.pull_request.updated_on))} ${_('with status %s') % h.changeset_status_lbl(c.current_changeset_status)}</div>
23 <div style="padding:10px; font-size:22px;width:100%;text-align: center; color:#88D882">${_('Closed %s') % (h.age(c.pull_request.updated_on))} ${_('with status %s') % h.changeset_status_lbl(c.current_changeset_status)}</div>
24 %endif
24 %endif
25 <h3>${_('Title')}: ${c.pull_request.title}</h3>
25 <h3>${_('Title')}: ${c.pull_request.title}</h3>
26
26
27 <div class="form">
27 <div class="form">
28 <div id="summary" class="fields">
28 <div id="summary" class="fields">
29 <div class="field">
29 <div class="field">
30 <div class="label-summary">
30 <div class="label-summary">
31 <label>${_('Status')}:</label>
31 <label>${_('Status')}:</label>
32 </div>
32 </div>
33 <div class="input">
33 <div class="input">
34 <div class="changeset-status-container" style="float:none;clear:both">
34 <div class="changeset-status-container" style="float:none;clear:both">
35 %if c.current_changeset_status:
35 %if c.current_changeset_status:
36 <div title="${_('Pull request status')}" class="changeset-status-lbl">[${h.changeset_status_lbl(c.current_changeset_status)}]</div>
36 <div title="${_('Pull request status')}" class="changeset-status-lbl">[${h.changeset_status_lbl(c.current_changeset_status)}]</div>
37 <div class="changeset-status-ico" style="padding:1px 4px"><img src="${h.url('/images/icons/flag_status_%s.png' % c.current_changeset_status)}" /></div>
37 <div class="changeset-status-ico" style="padding:1px 4px"><img src="${h.url('/images/icons/flag_status_%s.png' % c.current_changeset_status)}" /></div>
38 %endif
38 %endif
39 </div>
39 </div>
40 </div>
40 </div>
41 </div>
41 </div>
42 <div class="field">
42 <div class="field">
43 <div class="label-summary">
43 <div class="label-summary">
44 <label>${_('Still not reviewed by')}:</label>
44 <label>${_('Still not reviewed by')}:</label>
45 </div>
45 </div>
46 <div class="input">
46 <div class="input">
47 <div class="tooltip" title="${h.tooltip(','.join([x.username for x in c.pull_request_pending_reviewers]))}">${ungettext('%d reviewer', '%d reviewers',len(c.pull_request_pending_reviewers)) % len(c.pull_request_pending_reviewers)}</div>
47 <div class="tooltip" title="${h.tooltip(','.join([x.username for x in c.pull_request_pending_reviewers]))}">${ungettext('%d reviewer', '%d reviewers',len(c.pull_request_pending_reviewers)) % len(c.pull_request_pending_reviewers)}</div>
48 </div>
48 </div>
49 </div>
49 </div>
50 </div>
50 </div>
51 </div>
51 </div>
52 <div style="white-space:pre-wrap;padding:3px 3px 5px 20px">${h.literal(c.pull_request.description)}</div>
52 <div style="white-space:pre-wrap;padding:3px 3px 5px 20px">${h.literal(c.pull_request.description)}</div>
53 <div style="padding:4px 4px 10px 20px">
53 <div style="padding:4px 4px 10px 20px">
54 <div>${_('Created on')}: ${h.fmt_date(c.pull_request.created_on)}</div>
54 <div>${_('Created on')}: ${h.fmt_date(c.pull_request.created_on)}</div>
55 </div>
55 </div>
56
56
57 <div style="min-height:160px">
57 <div style="min-height:160px">
58 ##DIFF
58 ##DIFF
59 <div class="table" style="float:left;clear:none">
59 <div class="table" style="float:left;clear:none">
60 <div id="body" class="diffblock">
60 <div id="body" class="diffblock">
61 <div style="white-space:pre-wrap;padding:5px">${_('Compare view')}</div>
61 <div style="white-space:pre-wrap;padding:5px">${_('Compare view')}</div>
62 </div>
62 </div>
63 <div id="changeset_compare_view_content">
63 <div id="changeset_compare_view_content">
64 ##CS
64 ##CS
65 <div style="font-size:1.1em;font-weight: bold;clear:both;padding-top:10px">${_('Incoming changesets')}</div>
65 <div style="font-size:1.1em;font-weight: bold;clear:both;padding-top:10px">${_('Incoming changesets')}</div>
66 <%include file="/compare/compare_cs.html" />
66 <%include file="/compare/compare_cs.html" />
67
67
68 ## FILES
68 ## FILES
69 <div style="font-size:1.1em;font-weight: bold;clear:both;padding-top:10px">${_('Files affected')}</div>
69 <div style="font-size:1.1em;font-weight: bold;clear:both;padding-top:10px">${_('Files affected')}</div>
70 <div class="cs_files">
70 <div class="cs_files">
71 %for fid, change, f, stat in c.files:
71 %for fid, change, f, stat in c.files:
72 <div class="cs_${change}">
72 <div class="cs_${change}">
73 <div class="node">${h.link_to(h.safe_unicode(f),h.url.current(anchor=fid))}</div>
73 <div class="node">${h.link_to(h.safe_unicode(f),h.url.current(anchor=fid))}</div>
74 <div class="changes">${h.fancy_file_stats(stat)}</div>
74 <div class="changes">${h.fancy_file_stats(stat)}</div>
75 </div>
75 </div>
76 %endfor
76 %endfor
77 </div>
77 </div>
78 </div>
78 </div>
79 </div>
79 </div>
80 ## REVIEWERS
80 ## REVIEWERS
81 <div style="float:left; border-left:1px dashed #eee">
81 <div style="float:left; border-left:1px dashed #eee">
82 <h4>${_('Pull request reviewers')}</h4>
82 <h4>${_('Pull request reviewers')}</h4>
83 <div id="reviewers" style="padding:0px 0px 0px 15px">
83 <div id="reviewers" style="padding:0px 0px 0px 15px">
84 ## members goes here !
84 ## members goes here !
85 <div class="group_members_wrap">
85 <div class="group_members_wrap">
86 <ul id="review_members" class="group_members">
86 <ul id="review_members" class="group_members">
87 %for member,status in c.pull_request_reviewers:
87 %for member,status in c.pull_request_reviewers:
88 <li id="reviewer_${member.user_id}">
88 <li id="reviewer_${member.user_id}">
89 <div class="reviewers_member">
89 <div class="reviewers_member">
90 <div style="float:left;padding:0px 3px 0px 0px" class="tooltip" title="${h.tooltip(h.changeset_status_lbl(status[0][1].status if status else 'not_reviewed'))}">
90 <div style="float:left;padding:0px 3px 0px 0px" class="tooltip" title="${h.tooltip(h.changeset_status_lbl(status[0][1].status if status else 'not_reviewed'))}">
91 <img src="${h.url(str('/images/icons/flag_status_%s.png' % (status[0][1].status if status else 'not_reviewed')))}"/>
91 <img src="${h.url(str('/images/icons/flag_status_%s.png' % (status[0][1].status if status else 'not_reviewed')))}"/>
92 </div>
92 </div>
93 <div class="gravatar"><img alt="gravatar" src="${h.gravatar_url(member.email,14)}"/> </div>
93 <div class="gravatar"><img alt="gravatar" src="${h.gravatar_url(member.email,14)}"/> </div>
94 <div style="float:left">${member.full_name} (${_('owner')})</div>
94 <div style="float:left">${member.full_name} (${_('owner')})</div>
95 <input type="hidden" value="${member.user_id}" name="review_members" />
95 <input type="hidden" value="${member.user_id}" name="review_members" />
96 %if not c.pull_request.is_closed() and (h.HasPermissionAny('hg.admin', 'repository.admin')() or c.pull_request.author.user_id == c.rhodecode_user.user_id):
96 %if not c.pull_request.is_closed() and (h.HasPermissionAny('hg.admin', 'repository.admin')() or c.pull_request.author.user_id == c.rhodecode_user.user_id):
97 <span class="delete_icon action_button" onclick="removeReviewer(${member.user_id})"></span>
97 <span class="delete_icon action_button" onclick="removeReviewer(${member.user_id})"></span>
98 %endif
98 %endif
99 </div>
99 </div>
100 </li>
100 </li>
101 %endfor
101 %endfor
102 </ul>
102 </ul>
103 </div>
103 </div>
104 %if not c.pull_request.is_closed():
104 %if not c.pull_request.is_closed():
105 <div class='ac'>
105 <div class='ac'>
106 %if h.HasPermissionAny('hg.admin', 'repository.admin')() or c.pull_request.author.user_id == c.rhodecode_user.user_id:
106 %if h.HasPermissionAny('hg.admin', 'repository.admin')() or c.pull_request.author.user_id == c.rhodecode_user.user_id:
107 <div class="reviewer_ac">
107 <div class="reviewer_ac">
108 ${h.text('user', class_='yui-ac-input')}
108 ${h.text('user', class_='yui-ac-input')}
109 <span class="help-block">${_('Add reviewer to this pull request.')}</span>
109 <span class="help-block">${_('Add reviewer to this pull request.')}</span>
110 <div id="reviewers_container"></div>
110 <div id="reviewers_container"></div>
111 </div>
111 </div>
112 <div style="padding:0px 10px">
112 <div style="padding:0px 10px">
113 <span id="update_pull_request" class="ui-btn xsmall">${_('save')}</span>
113 <span id="update_pull_request" class="ui-btn xsmall">${_('save')}</span>
114 </div>
114 </div>
115 %endif
115 %endif
116 </div>
116 </div>
117 %endif
117 %endif
118 </div>
118 </div>
119 </div>
119 </div>
120 </div>
120 </div>
121 <script>
121 <script>
122 var _USERS_AC_DATA = ${c.users_array|n};
122 var _USERS_AC_DATA = ${c.users_array|n};
123 var _GROUPS_AC_DATA = ${c.users_groups_array|n};
123 var _GROUPS_AC_DATA = ${c.users_groups_array|n};
124 AJAX_COMMENT_URL = "${url('pullrequest_comment',repo_name=c.repo_name,pull_request_id=c.pull_request.pull_request_id)}";
124 AJAX_COMMENT_URL = "${url('pullrequest_comment',repo_name=c.repo_name,pull_request_id=c.pull_request.pull_request_id)}";
125 AJAX_COMMENT_DELETE_URL = "${url('pullrequest_comment_delete',repo_name=c.repo_name,comment_id='__COMMENT_ID__')}";
125 AJAX_COMMENT_DELETE_URL = "${url('pullrequest_comment_delete',repo_name=c.repo_name,comment_id='__COMMENT_ID__')}";
126 AJAX_UPDATE_PULLREQUEST = "${url('pullrequest_update',repo_name=c.repo_name,pull_request_id=c.pull_request.pull_request_id)}"
126 AJAX_UPDATE_PULLREQUEST = "${url('pullrequest_update',repo_name=c.repo_name,pull_request_id=c.pull_request.pull_request_id)}"
127 </script>
127 </script>
128
128
129 ## diff block
129 ## diff block
130 <%namespace name="diff_block" file="/changeset/diff_block.html"/>
130 <%namespace name="diff_block" file="/changeset/diff_block.html"/>
131 %for fid, change, f, stat in c.files:
131 %for fid, change, f, stat in c.files:
132 ${diff_block.diff_block_simple([c.changes[fid]])}
132 ${diff_block.diff_block_simple([c.changes[fid]])}
133 %endfor
133 %endfor
134
134
135 ## template for inline comment form
135 ## template for inline comment form
136 <%namespace name="comment" file="/changeset/changeset_file_comment.html"/>
136 <%namespace name="comment" file="/changeset/changeset_file_comment.html"/>
137 ${comment.comment_inline_form()}
137 ${comment.comment_inline_form()}
138
138
139 ## render comments and inlines
139 ## render comments and inlines
140 ${comment.generate_comments()}
140 ${comment.generate_comments()}
141
141
142 % if not c.pull_request.is_closed():
142 % if not c.pull_request.is_closed():
143 ## main comment form and it status
143 ## main comment form and it status
144 ${comment.comments(h.url('pullrequest_comment', repo_name=c.repo_name,
144 ${comment.comments(h.url('pullrequest_comment', repo_name=c.repo_name,
145 pull_request_id=c.pull_request.pull_request_id),
145 pull_request_id=c.pull_request.pull_request_id),
146 c.current_changeset_status,
146 c.current_changeset_status,
147 close_btn=True)}
147 close_btn=True)}
148 %endif
148 %endif
149
149
150 <script type="text/javascript">
150 <script type="text/javascript">
151 YUE.onDOMReady(function(){
151 YUE.onDOMReady(function(){
152 PullRequestAutoComplete('user', 'reviewers_container', _USERS_AC_DATA, _GROUPS_AC_DATA);
152 PullRequestAutoComplete('user', 'reviewers_container', _USERS_AC_DATA, _GROUPS_AC_DATA);
153
153
154 YUE.on(YUQ('.show-inline-comments'),'change',function(e){
154 YUE.on(YUQ('.show-inline-comments'),'change',function(e){
155 var show = 'none';
155 var show = 'none';
156 var target = e.currentTarget;
156 var target = e.currentTarget;
157 if(target.checked){
157 if(target.checked){
158 var show = ''
158 var show = ''
159 }
159 }
160 var boxid = YUD.getAttribute(target,'id_for');
160 var boxid = YUD.getAttribute(target,'id_for');
161 var comments = YUQ('#{0} .inline-comments'.format(boxid));
161 var comments = YUQ('#{0} .inline-comments'.format(boxid));
162 for(c in comments){
162 for(c in comments){
163 YUD.setStyle(comments[c],'display',show);
163 YUD.setStyle(comments[c],'display',show);
164 }
164 }
165 var btns = YUQ('#{0} .inline-comments-button'.format(boxid));
165 var btns = YUQ('#{0} .inline-comments-button'.format(boxid));
166 for(c in btns){
166 for(c in btns){
167 YUD.setStyle(btns[c],'display',show);
167 YUD.setStyle(btns[c],'display',show);
168 }
168 }
169 })
169 })
170
170
171 YUE.on(YUQ('.line'),'click',function(e){
171 YUE.on(YUQ('.line'),'click',function(e){
172 var tr = e.currentTarget;
172 var tr = e.currentTarget;
173 injectInlineForm(tr);
173 injectInlineForm(tr);
174 });
174 });
175
175
176 // inject comments into they proper positions
176 // inject comments into they proper positions
177 var file_comments = YUQ('.inline-comment-placeholder');
177 var file_comments = YUQ('.inline-comment-placeholder');
178 renderInlineComments(file_comments);
178 renderInlineComments(file_comments);
179
179
180 YUE.on(YUD.get('update_pull_request'),'click',function(e){
180 YUE.on(YUD.get('update_pull_request'),'click',function(e){
181
181
182 var reviewers_ids = [];
182 var reviewers_ids = [];
183 var ids = YUQ('#review_members input');
183 var ids = YUQ('#review_members input');
184 for(var i=0; i<ids.length;i++){
184 for(var i=0; i<ids.length;i++){
185 var id = ids[i].value
185 var id = ids[i].value
186 reviewers_ids.push(id);
186 reviewers_ids.push(id);
187 }
187 }
188 updateReviewers(reviewers_ids);
188 updateReviewers(reviewers_ids);
189 })
189 })
190 })
190 })
191 </script>
191 </script>
192
192
193 </div>
193 </div>
194
194
195 </%def>
195 </%def>
@@ -1,710 +1,710 b''
1 <%inherit file="/base/base.html"/>
1 <%inherit file="/base/base.html"/>
2
2
3 <%def name="title()">
3 <%def name="title()">
4 ${_('%s Summary') % c.repo_name} - ${c.rhodecode_name}
4 ${_('%s Summary') % c.repo_name} - ${c.rhodecode_name}
5 </%def>
5 </%def>
6
6
7 <%def name="breadcrumbs_links()">
7 <%def name="breadcrumbs_links()">
8 ${h.link_to(_(u'Home'),h.url('/'))}
8 ${h.link_to(_(u'Home'),h.url('/'))}
9 &raquo;
9 &raquo;
10 ${h.repo_link(c.dbrepo.groups_and_repo)}
10 ${h.repo_link(c.dbrepo.groups_and_repo)}
11 &raquo;
11 &raquo;
12 ${_('summary')}
12 ${_('summary')}
13 </%def>
13 </%def>
14
14
15 <%def name="page_nav()">
15 <%def name="page_nav()">
16 ${self.menu('summary')}
16 ${self.menu('summary')}
17 </%def>
17 </%def>
18
18
19 <%def name="head_extra()">
19 <%def name="head_extra()">
20 <link href="${h.url('atom_feed_home',repo_name=c.dbrepo.repo_name,api_key=c.rhodecode_user.api_key)}" rel="alternate" title="${_('repo %s ATOM feed') % c.repo_name}" type="application/atom+xml" />
20 <link href="${h.url('atom_feed_home',repo_name=c.dbrepo.repo_name,api_key=c.rhodecode_user.api_key)}" rel="alternate" title="${_('repo %s ATOM feed') % c.repo_name}" type="application/atom+xml" />
21 <link href="${h.url('rss_feed_home',repo_name=c.dbrepo.repo_name,api_key=c.rhodecode_user.api_key)}" rel="alternate" title="${_('repo %s RSS feed') % c.repo_name}" type="application/rss+xml" />
21 <link href="${h.url('rss_feed_home',repo_name=c.dbrepo.repo_name,api_key=c.rhodecode_user.api_key)}" rel="alternate" title="${_('repo %s RSS feed') % c.repo_name}" type="application/rss+xml" />
22 </%def>
22 </%def>
23
23
24 <%def name="main()">
24 <%def name="main()">
25 <%
25 <%
26 summary = lambda n:{False:'summary-short'}.get(n)
26 summary = lambda n:{False:'summary-short'}.get(n)
27 %>
27 %>
28 %if c.show_stats:
28 %if c.show_stats:
29 <div class="box box-left">
29 <div class="box box-left">
30 %else:
30 %else:
31 <div class="box">
31 <div class="box">
32 %endif
32 %endif
33 <!-- box / title -->
33 <!-- box / title -->
34 <div class="title">
34 <div class="title">
35 ${self.breadcrumbs()}
35 ${self.breadcrumbs()}
36 </div>
36 </div>
37 <!-- end box / title -->
37 <!-- end box / title -->
38 <div class="form">
38 <div class="form">
39 <div id="summary" class="fields">
39 <div id="summary" class="fields">
40
40
41 <div class="field">
41 <div class="field">
42 <div class="label-summary">
42 <div class="label-summary">
43 <label>${_('Name')}:</label>
43 <label>${_('Name')}:</label>
44 </div>
44 </div>
45 <div class="input ${summary(c.show_stats)}">
45 <div class="input ${summary(c.show_stats)}">
46 <div style="float:right;padding:5px 0px 0px 5px">
46 <div style="float:right;padding:5px 0px 0px 5px">
47 %if c.rhodecode_user.username != 'default':
47 %if c.rhodecode_user.username != 'default':
48 ${h.link_to(_('RSS'),h.url('rss_feed_home',repo_name=c.dbrepo.repo_name,api_key=c.rhodecode_user.api_key),class_='rss_icon')}
48 ${h.link_to(_('RSS'),h.url('rss_feed_home',repo_name=c.dbrepo.repo_name,api_key=c.rhodecode_user.api_key),class_='rss_icon')}
49 ${h.link_to(_('ATOM'),h.url('atom_feed_home',repo_name=c.dbrepo.repo_name,api_key=c.rhodecode_user.api_key),class_='atom_icon')}
49 ${h.link_to(_('ATOM'),h.url('atom_feed_home',repo_name=c.dbrepo.repo_name,api_key=c.rhodecode_user.api_key),class_='atom_icon')}
50 %else:
50 %else:
51 ${h.link_to(_('RSS'),h.url('rss_feed_home',repo_name=c.dbrepo.repo_name),class_='rss_icon')}
51 ${h.link_to(_('RSS'),h.url('rss_feed_home',repo_name=c.dbrepo.repo_name),class_='rss_icon')}
52 ${h.link_to(_('ATOM'),h.url('atom_feed_home',repo_name=c.dbrepo.repo_name),class_='atom_icon')}
52 ${h.link_to(_('ATOM'),h.url('atom_feed_home',repo_name=c.dbrepo.repo_name),class_='atom_icon')}
53 %endif
53 %endif
54 </div>
54 </div>
55 %if c.rhodecode_user.username != 'default':
55 %if c.rhodecode_user.username != 'default':
56 %if c.following:
56 %if c.following:
57 <span id="follow_toggle" class="following" title="${_('Stop following this repository')}"
57 <span id="follow_toggle" class="following" title="${_('Stop following this repository')}"
58 onclick="javascript:toggleFollowingRepo(this,${c.dbrepo.repo_id},'${str(h.get_token())}')">
58 onclick="javascript:toggleFollowingRepo(this,${c.dbrepo.repo_id},'${str(h.get_token())}')">
59 </span>
59 </span>
60 %else:
60 %else:
61 <span id="follow_toggle" class="follow" title="${_('Start following this repository')}"
61 <span id="follow_toggle" class="follow" title="${_('Start following this repository')}"
62 onclick="javascript:toggleFollowingRepo(this,${c.dbrepo.repo_id},'${str(h.get_token())}')">
62 onclick="javascript:toggleFollowingRepo(this,${c.dbrepo.repo_id},'${str(h.get_token())}')">
63 </span>
63 </span>
64 %endif
64 %endif
65 %endif:
65 %endif:
66 ##REPO TYPE
66 ##REPO TYPE
67 %if h.is_hg(c.dbrepo):
67 %if h.is_hg(c.dbrepo):
68 <img style="margin-bottom:2px" class="icon" title="${_('Mercurial repository')}" alt="${_('Mercurial repository')}" src="${h.url('/images/icons/hgicon.png')}"/>
68 <img style="margin-bottom:2px" class="icon" title="${_('Mercurial repository')}" alt="${_('Mercurial repository')}" src="${h.url('/images/icons/hgicon.png')}"/>
69 %endif
69 %endif
70 %if h.is_git(c.dbrepo):
70 %if h.is_git(c.dbrepo):
71 <img style="margin-bottom:2px" class="icon" title="${_('Git repository')}" alt="${_('Git repository')}" src="${h.url('/images/icons/giticon.png')}"/>
71 <img style="margin-bottom:2px" class="icon" title="${_('Git repository')}" alt="${_('Git repository')}" src="${h.url('/images/icons/giticon.png')}"/>
72 %endif
72 %endif
73
73
74 ##PUBLIC/PRIVATE
74 ##PUBLIC/PRIVATE
75 %if c.dbrepo.private:
75 %if c.dbrepo.private:
76 <img style="margin-bottom:2px" class="icon" title="${_('private repository')}" alt="${_('private repository')}" src="${h.url('/images/icons/lock.png')}"/>
76 <img style="margin-bottom:2px" class="icon" title="${_('private repository')}" alt="${_('private repository')}" src="${h.url('/images/icons/lock.png')}"/>
77 %else:
77 %else:
78 <img style="margin-bottom:2px" class="icon" title="${_('public repository')}" alt="${_('public repository')}" src="${h.url('/images/icons/lock_open.png')}"/>
78 <img style="margin-bottom:2px" class="icon" title="${_('public repository')}" alt="${_('public repository')}" src="${h.url('/images/icons/lock_open.png')}"/>
79 %endif
79 %endif
80
80
81 ##REPO NAME
81 ##REPO NAME
82 <span class="repo_name" title="${_('Non changable ID %s') % c.dbrepo.repo_id}">${h.repo_link(c.dbrepo.groups_and_repo)}</span>
82 <span class="repo_name" title="${_('Non changable ID %s') % c.dbrepo.repo_id}">${h.repo_link(c.dbrepo.groups_and_repo)}</span>
83
83
84 ##FORK
84 ##FORK
85 %if c.dbrepo.fork:
85 %if c.dbrepo.fork:
86 <div style="margin-top:5px;clear:both"">
86 <div style="margin-top:5px;clear:both"">
87 <a href="${h.url('summary_home',repo_name=c.dbrepo.fork.repo_name)}"><img class="icon" alt="${_('public')}" title="${_('Fork of')} ${c.dbrepo.fork.repo_name}" src="${h.url('/images/icons/arrow_divide.png')}"/>
87 <a href="${h.url('summary_home',repo_name=c.dbrepo.fork.repo_name)}"><img class="icon" alt="${_('public')}" title="${_('Fork of')} ${c.dbrepo.fork.repo_name}" src="${h.url('/images/icons/arrow_divide.png')}"/>
88 ${_('Fork of')} ${c.dbrepo.fork.repo_name}
88 ${_('Fork of')} ${c.dbrepo.fork.repo_name}
89 </a>
89 </a>
90 </div>
90 </div>
91 %endif
91 %endif
92 ##REMOTE
92 ##REMOTE
93 %if c.dbrepo.clone_uri:
93 %if c.dbrepo.clone_uri:
94 <div style="margin-top:5px;clear:both">
94 <div style="margin-top:5px;clear:both">
95 <a href="${h.url(str(h.hide_credentials(c.dbrepo.clone_uri)))}"><img class="icon" alt="${_('remote clone')}" title="${_('Clone from')} ${h.hide_credentials(c.dbrepo.clone_uri)}" src="${h.url('/images/icons/connect.png')}"/>
95 <a href="${h.url(str(h.hide_credentials(c.dbrepo.clone_uri)))}"><img class="icon" alt="${_('remote clone')}" title="${_('Clone from')} ${h.hide_credentials(c.dbrepo.clone_uri)}" src="${h.url('/images/icons/connect.png')}"/>
96 ${_('Clone from')} ${h.hide_credentials(c.dbrepo.clone_uri)}
96 ${_('Clone from')} ${h.hide_credentials(c.dbrepo.clone_uri)}
97 </a>
97 </a>
98 </div>
98 </div>
99 %endif
99 %endif
100 </div>
100 </div>
101 </div>
101 </div>
102
102
103 <div class="field">
103 <div class="field">
104 <div class="label-summary">
104 <div class="label-summary">
105 <label>${_('Description')}:</label>
105 <label>${_('Description')}:</label>
106 </div>
106 </div>
107 %if c.visual.stylify_metatags:
107 %if c.visual.stylify_metatags:
108 <div class="input ${summary(c.show_stats)} desc">${h.urlify_text(h.desc_stylize(c.dbrepo.description))}</div>
108 <div class="input ${summary(c.show_stats)} desc">${h.urlify_text(h.desc_stylize(c.dbrepo.description))}</div>
109 %else:
109 %else:
110 <div class="input ${summary(c.show_stats)} desc">${h.urlify_text(c.dbrepo.description)}</div>
110 <div class="input ${summary(c.show_stats)} desc">${h.urlify_text(c.dbrepo.description)}</div>
111 %endif
111 %endif
112 </div>
112 </div>
113
113
114 <div class="field">
114 <div class="field">
115 <div class="label-summary">
115 <div class="label-summary">
116 <label>${_('Contact')}:</label>
116 <label>${_('Contact')}:</label>
117 </div>
117 </div>
118 <div class="input ${summary(c.show_stats)}">
118 <div class="input ${summary(c.show_stats)}">
119 <div class="gravatar">
119 <div class="gravatar">
120 <img alt="gravatar" src="${h.gravatar_url(c.dbrepo.user.email)}"/>
120 <img alt="gravatar" src="${h.gravatar_url(c.dbrepo.user.email)}"/>
121 </div>
121 </div>
122 ${_('Username')}: ${c.dbrepo.user.username}<br/>
122 ${_('Username')}: ${c.dbrepo.user.username}<br/>
123 ${_('Name')}: ${c.dbrepo.user.name} ${c.dbrepo.user.lastname}<br/>
123 ${_('Name')}: ${c.dbrepo.user.name} ${c.dbrepo.user.lastname}<br/>
124 ${_('Email')}: <a href="mailto:${c.dbrepo.user.email}">${c.dbrepo.user.email}</a>
124 ${_('Email')}: <a href="mailto:${c.dbrepo.user.email}">${c.dbrepo.user.email}</a>
125 </div>
125 </div>
126 </div>
126 </div>
127
127
128 <div class="field">
128 <div class="field">
129 <div class="label-summary">
129 <div class="label-summary">
130 <label>${_('Clone url')}:</label>
130 <label>${_('Clone url')}:</label>
131 </div>
131 </div>
132 <div class="input ${summary(c.show_stats)}">
132 <div class="input ${summary(c.show_stats)}">
133 <div style="display:none" id="clone_by_name" class="ui-btn clone">${_('Show by Name')}</div>
133 <div style="display:none" id="clone_by_name" class="ui-btn clone">${_('Show by Name')}</div>
134 <div id="clone_by_id" class="ui-btn clone">${_('Show by ID')}</div>
134 <div id="clone_by_id" class="ui-btn clone">${_('Show by ID')}</div>
135 <input style="width:80%;margin-left:105px" type="text" id="clone_url" readonly="readonly" value="${c.clone_repo_url}"/>
135 <input style="width:80%;margin-left:105px" type="text" id="clone_url" readonly="readonly" value="${c.clone_repo_url}"/>
136 <input style="display:none;width:80%;margin-left:105px" type="text" id="clone_url_id" readonly="readonly" value="${c.clone_repo_url_id}"/>
136 <input style="display:none;width:80%;margin-left:105px" type="text" id="clone_url_id" readonly="readonly" value="${c.clone_repo_url_id}"/>
137 </div>
137 </div>
138 </div>
138 </div>
139
139
140 <div class="field">
140 <div class="field">
141 <div class="label-summary">
141 <div class="label-summary">
142 <label>${_('Trending files')}:</label>
142 <label>${_('Trending files')}:</label>
143 </div>
143 </div>
144 <div class="input ${summary(c.show_stats)}">
144 <div class="input ${summary(c.show_stats)}">
145 %if c.show_stats:
145 %if c.show_stats:
146 <div id="lang_stats"></div>
146 <div id="lang_stats"></div>
147 %else:
147 %else:
148 ${_('Statistics are disabled for this repository')}
148 ${_('Statistics are disabled for this repository')}
149 %if h.HasPermissionAll('hg.admin')('enable stats on from summary'):
149 %if h.HasPermissionAll('hg.admin')('enable stats on from summary'):
150 ${h.link_to(_('enable'),h.url('edit_repo',repo_name=c.repo_name),class_="ui-btn")}
150 ${h.link_to(_('enable'),h.url('edit_repo',repo_name=c.repo_name),class_="ui-btn")}
151 %endif
151 %endif
152 %endif
152 %endif
153 </div>
153 </div>
154 </div>
154 </div>
155
155
156 <div class="field">
156 <div class="field">
157 <div class="label-summary">
157 <div class="label-summary">
158 <label>${_('Download')}:</label>
158 <label>${_('Download')}:</label>
159 </div>
159 </div>
160 <div class="input ${summary(c.show_stats)}">
160 <div class="input ${summary(c.show_stats)}">
161 %if len(c.rhodecode_repo.revisions) == 0:
161 %if len(c.rhodecode_repo.revisions) == 0:
162 ${_('There are no downloads yet')}
162 ${_('There are no downloads yet')}
163 %elif c.enable_downloads is False:
163 %elif c.enable_downloads is False:
164 ${_('Downloads are disabled for this repository')}
164 ${_('Downloads are disabled for this repository')}
165 %if h.HasPermissionAll('hg.admin')('enable downloads on from summary'):
165 %if h.HasPermissionAll('hg.admin')('enable downloads on from summary'):
166 ${h.link_to(_('enable'),h.url('edit_repo',repo_name=c.repo_name),class_="ui-btn")}
166 ${h.link_to(_('enable'),h.url('edit_repo',repo_name=c.repo_name),class_="ui-btn")}
167 %endif
167 %endif
168 %else:
168 %else:
169 ${h.select('download_options',c.rhodecode_repo.get_changeset().raw_id,c.download_options)}
169 ${h.select('download_options',c.rhodecode_repo.get_changeset().raw_id,c.download_options)}
170 <span id="${'zip_link'}">${h.link_to(_('Download as zip'), h.url('files_archive_home',repo_name=c.dbrepo.repo_name,fname='tip.zip'),class_="archive_icon ui-btn")}</span>
170 <span id="${'zip_link'}">${h.link_to(_('Download as zip'), h.url('files_archive_home',repo_name=c.dbrepo.repo_name,fname='tip.zip'),class_="archive_icon ui-btn")}</span>
171 <span style="vertical-align: bottom">
171 <span style="vertical-align: bottom">
172 <input id="archive_subrepos" type="checkbox" name="subrepos" />
172 <input id="archive_subrepos" type="checkbox" name="subrepos" />
173 <label for="archive_subrepos" class="tooltip" title="${h.tooltip(_('Check this to download archive with subrepos'))}" >${_('with subrepos')}</label>
173 <label for="archive_subrepos" class="tooltip" title="${h.tooltip(_('Check this to download archive with subrepos'))}" >${_('with subrepos')}</label>
174 </span>
174 </span>
175 %endif
175 %endif
176 </div>
176 </div>
177 </div>
177 </div>
178 </div>
178 </div>
179 </div>
179 </div>
180 </div>
180 </div>
181
181
182 %if c.show_stats:
182 %if c.show_stats:
183 <div class="box box-right" style="min-height:455px">
183 <div class="box box-right" style="min-height:455px">
184 <!-- box / title -->
184 <!-- box / title -->
185 <div class="title">
185 <div class="title">
186 <h5>${_('Commit activity by day / author')}</h5>
186 <h5>${_('Commit activity by day / author')}</h5>
187 </div>
187 </div>
188
188
189 <div class="graph">
189 <div class="graph">
190 <div style="padding:0 10px 10px 17px;">
190 <div style="padding:0 10px 10px 17px;">
191 %if c.no_data:
191 %if c.no_data:
192 ${c.no_data_msg}
192 ${c.no_data_msg}
193 %if h.HasPermissionAll('hg.admin')('enable stats on from summary'):
193 %if h.HasPermissionAll('hg.admin')('enable stats on from summary'):
194 ${h.link_to(_('enable'),h.url('edit_repo',repo_name=c.repo_name),class_="ui-btn")}
194 ${h.link_to(_('enable'),h.url('edit_repo',repo_name=c.repo_name),class_="ui-btn")}
195 %endif
195 %endif
196 %else:
196 %else:
197 ${_('Stats gathered: ')} ${c.stats_percentage}%
197 ${_('Stats gathered: ')} ${c.stats_percentage}%
198 %endif
198 %endif
199 </div>
199 </div>
200 <div id="commit_history" style="width:450px;height:300px;float:left"></div>
200 <div id="commit_history" style="width:450px;height:300px;float:left"></div>
201 <div style="clear: both;height: 10px"></div>
201 <div style="clear: both;height: 10px"></div>
202 <div id="overview" style="width:450px;height:100px;float:left"></div>
202 <div id="overview" style="width:450px;height:100px;float:left"></div>
203
203
204 <div id="legend_data" style="clear:both;margin-top:10px;">
204 <div id="legend_data" style="clear:both;margin-top:10px;">
205 <div id="legend_container"></div>
205 <div id="legend_container"></div>
206 <div id="legend_choices">
206 <div id="legend_choices">
207 <table id="legend_choices_tables" class="noborder" style="font-size:smaller;color:#545454"></table>
207 <table id="legend_choices_tables" class="noborder" style="font-size:smaller;color:#545454"></table>
208 </div>
208 </div>
209 </div>
209 </div>
210 </div>
210 </div>
211 </div>
211 </div>
212 %endif
212 %endif
213
213
214 <div class="box">
214 <div class="box">
215 <div class="title">
215 <div class="title">
216 <div class="breadcrumbs">
216 <div class="breadcrumbs">
217 %if c.repo_changesets:
217 %if c.repo_changesets:
218 ${h.link_to(_('Shortlog'),h.url('shortlog_home',repo_name=c.repo_name))}
218 ${h.link_to(_('Shortlog'),h.url('shortlog_home',repo_name=c.repo_name))}
219 %else:
219 %else:
220 ${_('Quick start')}
220 ${_('Quick start')}
221 %endif
221 %endif
222 </div>
222 </div>
223 </div>
223 </div>
224 <div class="table">
224 <div class="table">
225 <div id="shortlog_data">
225 <div id="shortlog_data">
226 <%include file='../shortlog/shortlog_data.html'/>
226 <%include file='../shortlog/shortlog_data.html'/>
227 </div>
227 </div>
228 </div>
228 </div>
229 </div>
229 </div>
230
230
231 %if c.readme_data:
231 %if c.readme_data:
232 <div id="readme" class="box header-pos-fix" style="background-color: #FAFAFA">
232 <div id="readme" class="box header-pos-fix" style="background-color: #FAFAFA">
233 <div id="readme" class="title" title="${_("Readme file at revision '%s'" % c.rhodecode_db_repo.landing_rev)}">
233 <div id="readme" class="title" title="${_("Readme file at revision '%s'" % c.rhodecode_db_repo.landing_rev)}">
234 <div class="breadcrumbs">
234 <div class="breadcrumbs">
235 <a href="${h.url('files_home',repo_name=c.repo_name,revision='tip',f_path=c.readme_file)}">${c.readme_file}</a>
235 <a href="${h.url('files_home',repo_name=c.repo_name,revision='tip',f_path=c.readme_file)}">${c.readme_file}</a>
236 <a class="permalink" href="#readme" title="${_('Permalink to this readme')}">&para;</a>
236 <a class="permalink" href="#readme" title="${_('Permalink to this readme')}">&para;</a>
237 </div>
237 </div>
238 </div>
238 </div>
239 <div id="readme" class="readme">
239 <div id="readme" class="readme">
240 <div class="readme_box">
240 <div class="readme_box">
241 ${c.readme_data|n}
241 ${c.readme_data|n}
242 </div>
242 </div>
243 </div>
243 </div>
244 </div>
244 </div>
245 %endif
245 %endif
246
246
247 <script type="text/javascript">
247 <script type="text/javascript">
248 var clone_url = 'clone_url';
248 var clone_url = 'clone_url';
249 YUE.on(clone_url,'click',function(e){
249 YUE.on(clone_url,'click',function(e){
250 if(YUD.hasClass(clone_url,'selected')){
250 if(YUD.hasClass(clone_url,'selected')){
251 return
251 return
252 }
252 }
253 else{
253 else{
254 YUD.addClass(clone_url,'selected');
254 YUD.addClass(clone_url,'selected');
255 YUD.get(clone_url).select();
255 YUD.get(clone_url).select();
256 }
256 }
257 })
257 })
258
258
259 YUE.on('clone_by_name','click',function(e){
259 YUE.on('clone_by_name','click',function(e){
260 // show url by name and hide name button
260 // show url by name and hide name button
261 YUD.setStyle('clone_url','display','');
261 YUD.setStyle('clone_url','display','');
262 YUD.setStyle('clone_by_name','display','none');
262 YUD.setStyle('clone_by_name','display','none');
263
263
264 // hide url by id and show name button
264 // hide url by id and show name button
265 YUD.setStyle('clone_by_id','display','');
265 YUD.setStyle('clone_by_id','display','');
266 YUD.setStyle('clone_url_id','display','none');
266 YUD.setStyle('clone_url_id','display','none');
267
267
268 })
268 })
269 YUE.on('clone_by_id','click',function(e){
269 YUE.on('clone_by_id','click',function(e){
270
270
271 // show url by id and hide id button
271 // show url by id and hide id button
272 YUD.setStyle('clone_by_id','display','none');
272 YUD.setStyle('clone_by_id','display','none');
273 YUD.setStyle('clone_url_id','display','');
273 YUD.setStyle('clone_url_id','display','');
274
274
275 // hide url by name and show id button
275 // hide url by name and show id button
276 YUD.setStyle('clone_by_name','display','');
276 YUD.setStyle('clone_by_name','display','');
277 YUD.setStyle('clone_url','display','none');
277 YUD.setStyle('clone_url','display','none');
278 })
278 })
279
279
280
280
281 var tmpl_links = {};
281 var tmpl_links = {};
282 %for cnt,archive in enumerate(c.rhodecode_repo._get_archives()):
282 %for cnt,archive in enumerate(c.rhodecode_repo._get_archives()):
283 tmpl_links["${archive['type']}"] = '${h.link_to('__NAME__', h.url('files_archive_home',repo_name=c.dbrepo.repo_name, fname='__CS__'+archive['extension'],subrepos='__SUB__'),class_='archive_icon ui-btn')}';
283 tmpl_links["${archive['type']}"] = '${h.link_to('__NAME__', h.url('files_archive_home',repo_name=c.dbrepo.repo_name, fname='__CS__'+archive['extension'],subrepos='__SUB__'),class_='archive_icon ui-btn')}';
284 %endfor
284 %endfor
285
285
286 YUE.on(['download_options','archive_subrepos'],'change',function(e){
286 YUE.on(['download_options','archive_subrepos'],'change',function(e){
287 var sm = YUD.get('download_options');
287 var sm = YUD.get('download_options');
288 var new_cs = sm.options[sm.selectedIndex];
288 var new_cs = sm.options[sm.selectedIndex];
289
289
290 for(k in tmpl_links){
290 for(k in tmpl_links){
291 var s = YUD.get(k+'_link');
291 var s = YUD.get(k+'_link');
292 if(s){
292 if(s){
293 var title_tmpl = "${_('Download %s as %s') % ('__CS_NAME__','__CS_EXT__')}";
293 var title_tmpl = "${_('Download %s as %s') % ('__CS_NAME__','__CS_EXT__')}";
294 title_tmpl= title_tmpl.replace('__CS_NAME__',new_cs.text);
294 title_tmpl= title_tmpl.replace('__CS_NAME__',new_cs.text);
295 title_tmpl = title_tmpl.replace('__CS_EXT__',k);
295 title_tmpl = title_tmpl.replace('__CS_EXT__',k);
296
296
297 var url = tmpl_links[k].replace('__CS__',new_cs.value);
297 var url = tmpl_links[k].replace('__CS__',new_cs.value);
298 var subrepos = YUD.get('archive_subrepos').checked;
298 var subrepos = YUD.get('archive_subrepos').checked;
299 url = url.replace('__SUB__',subrepos);
299 url = url.replace('__SUB__',subrepos);
300 url = url.replace('__NAME__',title_tmpl);
300 url = url.replace('__NAME__',title_tmpl);
301 s.innerHTML = url
301 s.innerHTML = url
302 }
302 }
303 }
303 }
304 });
304 });
305 </script>
305 </script>
306 %if c.show_stats:
306 %if c.show_stats:
307 <script type="text/javascript">
307 <script type="text/javascript">
308 var data = ${c.trending_languages|n};
308 var data = ${c.trending_languages|n};
309 var total = 0;
309 var total = 0;
310 var no_data = true;
310 var no_data = true;
311 var tbl = document.createElement('table');
311 var tbl = document.createElement('table');
312 tbl.setAttribute('class','trending_language_tbl');
312 tbl.setAttribute('class','trending_language_tbl');
313 var cnt = 0;
313 var cnt = 0;
314 for (var i=0;i<data.length;i++){
314 for (var i=0;i<data.length;i++){
315 total+= data[i][1].count;
315 total+= data[i][1].count;
316 }
316 }
317 for (var i=0;i<data.length;i++){
317 for (var i=0;i<data.length;i++){
318 cnt += 1;
318 cnt += 1;
319 no_data = false;
319 no_data = false;
320
320
321 var hide = cnt>2;
321 var hide = cnt>2;
322 var tr = document.createElement('tr');
322 var tr = document.createElement('tr');
323 if (hide){
323 if (hide){
324 tr.setAttribute('style','display:none');
324 tr.setAttribute('style','display:none');
325 tr.setAttribute('class','stats_hidden');
325 tr.setAttribute('class','stats_hidden');
326 }
326 }
327 var k = data[i][0];
327 var k = data[i][0];
328 var obj = data[i][1];
328 var obj = data[i][1];
329 var percentage = Math.round((obj.count/total*100),2);
329 var percentage = Math.round((obj.count/total*100),2);
330
330
331 var td1 = document.createElement('td');
331 var td1 = document.createElement('td');
332 td1.width = 150;
332 td1.width = 150;
333 var trending_language_label = document.createElement('div');
333 var trending_language_label = document.createElement('div');
334 trending_language_label.innerHTML = obj.desc+" ("+k+")";
334 trending_language_label.innerHTML = obj.desc+" ("+k+")";
335 td1.appendChild(trending_language_label);
335 td1.appendChild(trending_language_label);
336
336
337 var td2 = document.createElement('td');
337 var td2 = document.createElement('td');
338 td2.setAttribute('style','padding-right:14px !important');
338 td2.setAttribute('style','padding-right:14px !important');
339 var trending_language = document.createElement('div');
339 var trending_language = document.createElement('div');
340 var nr_files = obj.count+" ${_('files')}";
340 var nr_files = obj.count+" ${_('files')}";
341
341
342 trending_language.title = k+" "+nr_files;
342 trending_language.title = k+" "+nr_files;
343
343
344 if (percentage>22){
344 if (percentage>22){
345 trending_language.innerHTML = "<b style='font-size:0.8em'>"+percentage+"% "+nr_files+ "</b>";
345 trending_language.innerHTML = "<b style='font-size:0.8em'>"+percentage+"% "+nr_files+ "</b>";
346 }
346 }
347 else{
347 else{
348 trending_language.innerHTML = "<b style='font-size:0.8em'>"+percentage+"%</b>";
348 trending_language.innerHTML = "<b style='font-size:0.8em'>"+percentage+"%</b>";
349 }
349 }
350
350
351 trending_language.setAttribute("class", 'trending_language top-right-rounded-corner bottom-right-rounded-corner');
351 trending_language.setAttribute("class", 'trending_language top-right-rounded-corner bottom-right-rounded-corner');
352 trending_language.style.width=percentage+"%";
352 trending_language.style.width=percentage+"%";
353 td2.appendChild(trending_language);
353 td2.appendChild(trending_language);
354
354
355 tr.appendChild(td1);
355 tr.appendChild(td1);
356 tr.appendChild(td2);
356 tr.appendChild(td2);
357 tbl.appendChild(tr);
357 tbl.appendChild(tr);
358 if(cnt == 3){
358 if(cnt == 3){
359 var show_more = document.createElement('tr');
359 var show_more = document.createElement('tr');
360 var td = document.createElement('td');
360 var td = document.createElement('td');
361 lnk = document.createElement('a');
361 lnk = document.createElement('a');
362
362
363 lnk.href='#';
363 lnk.href='#';
364 lnk.innerHTML = "${_('show more')}";
364 lnk.innerHTML = "${_('show more')}";
365 lnk.id='code_stats_show_more';
365 lnk.id='code_stats_show_more';
366 td.appendChild(lnk);
366 td.appendChild(lnk);
367
367
368 show_more.appendChild(td);
368 show_more.appendChild(td);
369 show_more.appendChild(document.createElement('td'));
369 show_more.appendChild(document.createElement('td'));
370 tbl.appendChild(show_more);
370 tbl.appendChild(show_more);
371 }
371 }
372
372
373 }
373 }
374
374
375 YUD.get('lang_stats').appendChild(tbl);
375 YUD.get('lang_stats').appendChild(tbl);
376 YUE.on('code_stats_show_more','click',function(){
376 YUE.on('code_stats_show_more','click',function(){
377 l = YUD.getElementsByClassName('stats_hidden')
377 l = YUD.getElementsByClassName('stats_hidden')
378 for (e in l){
378 for (e in l){
379 YUD.setStyle(l[e],'display','');
379 YUD.setStyle(l[e],'display','');
380 };
380 };
381 YUD.setStyle(YUD.get('code_stats_show_more'),
381 YUD.setStyle(YUD.get('code_stats_show_more'),
382 'display','none');
382 'display','none');
383 });
383 });
384 </script>
384 </script>
385 <script type="text/javascript">
385 <script type="text/javascript">
386 /**
386 /**
387 * Plots summary graph
387 * Plots summary graph
388 *
388 *
389 * @class SummaryPlot
389 * @class SummaryPlot
390 * @param {from} initial from for detailed graph
390 * @param {from} initial from for detailed graph
391 * @param {to} initial to for detailed graph
391 * @param {to} initial to for detailed graph
392 * @param {dataset}
392 * @param {dataset}
393 * @param {overview_dataset}
393 * @param {overview_dataset}
394 */
394 */
395 function SummaryPlot(from,to,dataset,overview_dataset) {
395 function SummaryPlot(from,to,dataset,overview_dataset) {
396 var initial_ranges = {
396 var initial_ranges = {
397 "xaxis":{
397 "xaxis":{
398 "from":from,
398 "from":from,
399 "to":to,
399 "to":to,
400 },
400 },
401 };
401 };
402 var dataset = dataset;
402 var dataset = dataset;
403 var overview_dataset = [overview_dataset];
403 var overview_dataset = [overview_dataset];
404 var choiceContainer = YUD.get("legend_choices");
404 var choiceContainer = YUD.get("legend_choices");
405 var choiceContainerTable = YUD.get("legend_choices_tables");
405 var choiceContainerTable = YUD.get("legend_choices_tables");
406 var plotContainer = YUD.get('commit_history');
406 var plotContainer = YUD.get('commit_history');
407 var overviewContainer = YUD.get('overview');
407 var overviewContainer = YUD.get('overview');
408
408
409 var plot_options = {
409 var plot_options = {
410 bars: {show:true,align:'center',lineWidth:4},
410 bars: {show:true,align:'center',lineWidth:4},
411 legend: {show:true, container:"legend_container"},
411 legend: {show:true, container:"legend_container"},
412 points: {show:true,radius:0,fill:false},
412 points: {show:true,radius:0,fill:false},
413 yaxis: {tickDecimals:0,},
413 yaxis: {tickDecimals:0,},
414 xaxis: {
414 xaxis: {
415 mode: "time",
415 mode: "time",
416 timeformat: "%d/%m",
416 timeformat: "%d/%m",
417 min:from,
417 min:from,
418 max:to,
418 max:to,
419 },
419 },
420 grid: {
420 grid: {
421 hoverable: true,
421 hoverable: true,
422 clickable: true,
422 clickable: true,
423 autoHighlight:true,
423 autoHighlight:true,
424 color: "#999"
424 color: "#999"
425 },
425 },
426 //selection: {mode: "x"}
426 //selection: {mode: "x"}
427 };
427 };
428 var overview_options = {
428 var overview_options = {
429 legend:{show:false},
429 legend:{show:false},
430 bars: {show:true,barWidth: 2,},
430 bars: {show:true,barWidth: 2,},
431 shadowSize: 0,
431 shadowSize: 0,
432 xaxis: {mode: "time", timeformat: "%d/%m/%y",},
432 xaxis: {mode: "time", timeformat: "%d/%m/%y",},
433 yaxis: {ticks: 3, min: 0,tickDecimals:0,},
433 yaxis: {ticks: 3, min: 0,tickDecimals:0,},
434 grid: {color: "#999",},
434 grid: {color: "#999",},
435 selection: {mode: "x"}
435 selection: {mode: "x"}
436 };
436 };
437
437
438 /**
438 /**
439 *get dummy data needed in few places
439 *get dummy data needed in few places
440 */
440 */
441 function getDummyData(label){
441 function getDummyData(label){
442 return {"label":label,
442 return {"label":label,
443 "data":[{"time":0,
443 "data":[{"time":0,
444 "commits":0,
444 "commits":0,
445 "added":0,
445 "added":0,
446 "changed":0,
446 "changed":0,
447 "removed":0,
447 "removed":0,
448 }],
448 }],
449 "schema":["commits"],
449 "schema":["commits"],
450 "color":'#ffffff',
450 "color":'#ffffff',
451 }
451 }
452 }
452 }
453
453
454 /**
454 /**
455 * generate checkboxes accordindly to data
455 * generate checkboxes accordindly to data
456 * @param keys
456 * @param keys
457 * @returns
457 * @returns
458 */
458 */
459 function generateCheckboxes(data) {
459 function generateCheckboxes(data) {
460 //append checkboxes
460 //append checkboxes
461 var i = 0;
461 var i = 0;
462 choiceContainerTable.innerHTML = '';
462 choiceContainerTable.innerHTML = '';
463 for(var pos in data) {
463 for(var pos in data) {
464
464
465 data[pos].color = i;
465 data[pos].color = i;
466 i++;
466 i++;
467 if(data[pos].label != ''){
467 if(data[pos].label != ''){
468 choiceContainerTable.innerHTML +=
468 choiceContainerTable.innerHTML +=
469 '<tr><td><input type="checkbox" id="id_user_{0}" name="{0}" checked="checked" /> \
469 '<tr><td><input type="checkbox" id="id_user_{0}" name="{0}" checked="checked" /> \
470 <label for="id_user_{0}">{0}</label></td></tr>'.format(data[pos].label);
470 <label for="id_user_{0}">{0}</label></td></tr>'.format(data[pos].label);
471 }
471 }
472 }
472 }
473 }
473 }
474
474
475 /**
475 /**
476 * ToolTip show
476 * ToolTip show
477 */
477 */
478 function showTooltip(x, y, contents) {
478 function showTooltip(x, y, contents) {
479 var div=document.getElementById('tooltip');
479 var div=document.getElementById('tooltip');
480 if(!div) {
480 if(!div) {
481 div = document.createElement('div');
481 div = document.createElement('div');
482 div.id="tooltip";
482 div.id="tooltip";
483 div.style.position="absolute";
483 div.style.position="absolute";
484 div.style.border='1px solid #fdd';
484 div.style.border='1px solid #fdd';
485 div.style.padding='2px';
485 div.style.padding='2px';
486 div.style.backgroundColor='#fee';
486 div.style.backgroundColor='#fee';
487 document.body.appendChild(div);
487 document.body.appendChild(div);
488 }
488 }
489 YUD.setStyle(div, 'opacity', 0);
489 YUD.setStyle(div, 'opacity', 0);
490 div.innerHTML = contents;
490 div.innerHTML = contents;
491 div.style.top=(y + 5) + "px";
491 div.style.top=(y + 5) + "px";
492 div.style.left=(x + 5) + "px";
492 div.style.left=(x + 5) + "px";
493
493
494 var anim = new YAHOO.util.Anim(div, {opacity: {to: 0.8}}, 0.2);
494 var anim = new YAHOO.util.Anim(div, {opacity: {to: 0.8}}, 0.2);
495 anim.animate();
495 anim.animate();
496 }
496 }
497
497
498 /**
498 /**
499 * This function will detect if selected period has some changesets
499 * This function will detect if selected period has some changesets
500 for this user if it does this data is then pushed for displaying
500 for this user if it does this data is then pushed for displaying
501 Additionally it will only display users that are selected by the checkbox
501 Additionally it will only display users that are selected by the checkbox
502 */
502 */
503 function getDataAccordingToRanges(ranges) {
503 function getDataAccordingToRanges(ranges) {
504
504
505 var data = [];
505 var data = [];
506 var new_dataset = {};
506 var new_dataset = {};
507 var keys = [];
507 var keys = [];
508 var max_commits = 0;
508 var max_commits = 0;
509 for(var key in dataset){
509 for(var key in dataset){
510
510
511 for(var ds in dataset[key].data){
511 for(var ds in dataset[key].data){
512 commit_data = dataset[key].data[ds];
512 commit_data = dataset[key].data[ds];
513 if (commit_data.time >= ranges.xaxis.from && commit_data.time <= ranges.xaxis.to){
513 if (commit_data.time >= ranges.xaxis.from && commit_data.time <= ranges.xaxis.to){
514
514
515 if(new_dataset[key] === undefined){
515 if(new_dataset[key] === undefined){
516 new_dataset[key] = {data:[],schema:["commits"],label:key};
516 new_dataset[key] = {data:[],schema:["commits"],label:key};
517 }
517 }
518 new_dataset[key].data.push(commit_data);
518 new_dataset[key].data.push(commit_data);
519 }
519 }
520 }
520 }
521 if (new_dataset[key] !== undefined){
521 if (new_dataset[key] !== undefined){
522 data.push(new_dataset[key]);
522 data.push(new_dataset[key]);
523 }
523 }
524 }
524 }
525
525
526 if (data.length > 0){
526 if (data.length > 0){
527 return data;
527 return data;
528 }
528 }
529 else{
529 else{
530 //just return dummy data for graph to plot itself
530 //just return dummy data for graph to plot itself
531 return [getDummyData('')];
531 return [getDummyData('')];
532 }
532 }
533 }
533 }
534
534
535 /**
535 /**
536 * redraw using new checkbox data
536 * redraw using new checkbox data
537 */
537 */
538 function plotchoiced(e,args){
538 function plotchoiced(e,args){
539 var cur_data = args[0];
539 var cur_data = args[0];
540 var cur_ranges = args[1];
540 var cur_ranges = args[1];
541
541
542 var new_data = [];
542 var new_data = [];
543 var inputs = choiceContainer.getElementsByTagName("input");
543 var inputs = choiceContainer.getElementsByTagName("input");
544
544
545 //show only checked labels
545 //show only checked labels
546 for(var i=0; i<inputs.length; i++) {
546 for(var i=0; i<inputs.length; i++) {
547 var checkbox_key = inputs[i].name;
547 var checkbox_key = inputs[i].name;
548
548
549 if(inputs[i].checked){
549 if(inputs[i].checked){
550 for(var d in cur_data){
550 for(var d in cur_data){
551 if(cur_data[d].label == checkbox_key){
551 if(cur_data[d].label == checkbox_key){
552 new_data.push(cur_data[d]);
552 new_data.push(cur_data[d]);
553 }
553 }
554 }
554 }
555 }
555 }
556 else{
556 else{
557 //push dummy data to not hide the label
557 //push dummy data to not hide the label
558 new_data.push(getDummyData(checkbox_key));
558 new_data.push(getDummyData(checkbox_key));
559 }
559 }
560 }
560 }
561
561
562 var new_options = YAHOO.lang.merge(plot_options, {
562 var new_options = YAHOO.lang.merge(plot_options, {
563 xaxis: {
563 xaxis: {
564 min: cur_ranges.xaxis.from,
564 min: cur_ranges.xaxis.from,
565 max: cur_ranges.xaxis.to,
565 max: cur_ranges.xaxis.to,
566 mode:"time",
566 mode:"time",
567 timeformat: "%d/%m",
567 timeformat: "%d/%m",
568 },
568 },
569 });
569 });
570 if (!new_data){
570 if (!new_data){
571 new_data = [[0,1]];
571 new_data = [[0,1]];
572 }
572 }
573 // do the zooming
573 // do the zooming
574 plot = YAHOO.widget.Flot(plotContainer, new_data, new_options);
574 plot = YAHOO.widget.Flot(plotContainer, new_data, new_options);
575
575
576 plot.subscribe("plotselected", plotselected);
576 plot.subscribe("plotselected", plotselected);
577
577
578 //resubscribe plothover
578 //resubscribe plothover
579 plot.subscribe("plothover", plothover);
579 plot.subscribe("plothover", plothover);
580
580
581 // don't fire event on the overview to prevent eternal loop
581 // don't fire event on the overview to prevent eternal loop
582 overview.setSelection(cur_ranges, true);
582 overview.setSelection(cur_ranges, true);
583
583
584 }
584 }
585
585
586 /**
586 /**
587 * plot only selected items from overview
587 * plot only selected items from overview
588 * @param ranges
588 * @param ranges
589 * @returns
589 * @returns
590 */
590 */
591 function plotselected(ranges,cur_data) {
591 function plotselected(ranges,cur_data) {
592 //updates the data for new plot
592 //updates the data for new plot
593 var data = getDataAccordingToRanges(ranges);
593 var data = getDataAccordingToRanges(ranges);
594 generateCheckboxes(data);
594 generateCheckboxes(data);
595
595
596 var new_options = YAHOO.lang.merge(plot_options, {
596 var new_options = YAHOO.lang.merge(plot_options, {
597 xaxis: {
597 xaxis: {
598 min: ranges.xaxis.from,
598 min: ranges.xaxis.from,
599 max: ranges.xaxis.to,
599 max: ranges.xaxis.to,
600 mode:"time",
600 mode:"time",
601 timeformat: "%d/%m",
601 timeformat: "%d/%m",
602 },
602 },
603 });
603 });
604 // do the zooming
604 // do the zooming
605 plot = YAHOO.widget.Flot(plotContainer, data, new_options);
605 plot = YAHOO.widget.Flot(plotContainer, data, new_options);
606
606
607 plot.subscribe("plotselected", plotselected);
607 plot.subscribe("plotselected", plotselected);
608
608
609 //resubscribe plothover
609 //resubscribe plothover
610 plot.subscribe("plothover", plothover);
610 plot.subscribe("plothover", plothover);
611
611
612 // don't fire event on the overview to prevent eternal loop
612 // don't fire event on the overview to prevent eternal loop
613 overview.setSelection(ranges, true);
613 overview.setSelection(ranges, true);
614
614
615 //resubscribe choiced
615 //resubscribe choiced
616 YUE.on(choiceContainer.getElementsByTagName("input"), "click", plotchoiced, [data, ranges]);
616 YUE.on(choiceContainer.getElementsByTagName("input"), "click", plotchoiced, [data, ranges]);
617 }
617 }
618
618
619 var previousPoint = null;
619 var previousPoint = null;
620
620
621 function plothover(o) {
621 function plothover(o) {
622 var pos = o.pos;
622 var pos = o.pos;
623 var item = o.item;
623 var item = o.item;
624
624
625 //YUD.get("x").innerHTML = pos.x.toFixed(2);
625 //YUD.get("x").innerHTML = pos.x.toFixed(2);
626 //YUD.get("y").innerHTML = pos.y.toFixed(2);
626 //YUD.get("y").innerHTML = pos.y.toFixed(2);
627 if (item) {
627 if (item) {
628 if (previousPoint != item.datapoint) {
628 if (previousPoint != item.datapoint) {
629 previousPoint = item.datapoint;
629 previousPoint = item.datapoint;
630
630
631 var tooltip = YUD.get("tooltip");
631 var tooltip = YUD.get("tooltip");
632 if(tooltip) {
632 if(tooltip) {
633 tooltip.parentNode.removeChild(tooltip);
633 tooltip.parentNode.removeChild(tooltip);
634 }
634 }
635 var x = item.datapoint.x.toFixed(2);
635 var x = item.datapoint.x.toFixed(2);
636 var y = item.datapoint.y.toFixed(2);
636 var y = item.datapoint.y.toFixed(2);
637
637
638 if (!item.series.label){
638 if (!item.series.label){
639 item.series.label = 'commits';
639 item.series.label = 'commits';
640 }
640 }
641 var d = new Date(x*1000);
641 var d = new Date(x*1000);
642 var fd = d.toDateString()
642 var fd = d.toDateString()
643 var nr_commits = parseInt(y);
643 var nr_commits = parseInt(y);
644
644
645 var cur_data = dataset[item.series.label].data[item.dataIndex];
645 var cur_data = dataset[item.series.label].data[item.dataIndex];
646 var added = cur_data.added;
646 var added = cur_data.added;
647 var changed = cur_data.changed;
647 var changed = cur_data.changed;
648 var removed = cur_data.removed;
648 var removed = cur_data.removed;
649
649
650 var nr_commits_suffix = " ${_('commits')} ";
650 var nr_commits_suffix = " ${_('commits')} ";
651 var added_suffix = " ${_('files added')} ";
651 var added_suffix = " ${_('files added')} ";
652 var changed_suffix = " ${_('files changed')} ";
652 var changed_suffix = " ${_('files changed')} ";
653 var removed_suffix = " ${_('files removed')} ";
653 var removed_suffix = " ${_('files removed')} ";
654
654
655
655
656 if(nr_commits == 1){nr_commits_suffix = " ${_('commit')} ";}
656 if(nr_commits == 1){nr_commits_suffix = " ${_('commit')} ";}
657 if(added==1){added_suffix=" ${_('file added')} ";}
657 if(added==1){added_suffix=" ${_('file added')} ";}
658 if(changed==1){changed_suffix=" ${_('file changed')} ";}
658 if(changed==1){changed_suffix=" ${_('file changed')} ";}
659 if(removed==1){removed_suffix=" ${_('file removed')} ";}
659 if(removed==1){removed_suffix=" ${_('file removed')} ";}
660
660
661 showTooltip(item.pageX, item.pageY, item.series.label + " on " + fd
661 showTooltip(item.pageX, item.pageY, item.series.label + " on " + fd
662 +'<br/>'+
662 +'<br/>'+
663 nr_commits + nr_commits_suffix+'<br/>'+
663 nr_commits + nr_commits_suffix+'<br/>'+
664 added + added_suffix +'<br/>'+
664 added + added_suffix +'<br/>'+
665 changed + changed_suffix + '<br/>'+
665 changed + changed_suffix + '<br/>'+
666 removed + removed_suffix + '<br/>');
666 removed + removed_suffix + '<br/>');
667 }
667 }
668 }
668 }
669 else {
669 else {
670 var tooltip = YUD.get("tooltip");
670 var tooltip = YUD.get("tooltip");
671
671
672 if(tooltip) {
672 if(tooltip) {
673 tooltip.parentNode.removeChild(tooltip);
673 tooltip.parentNode.removeChild(tooltip);
674 }
674 }
675 previousPoint = null;
675 previousPoint = null;
676 }
676 }
677 }
677 }
678
678
679 /**
679 /**
680 * MAIN EXECUTION
680 * MAIN EXECUTION
681 */
681 */
682
682
683 var data = getDataAccordingToRanges(initial_ranges);
683 var data = getDataAccordingToRanges(initial_ranges);
684 generateCheckboxes(data);
684 generateCheckboxes(data);
685
685
686 //main plot
686 //main plot
687 var plot = YAHOO.widget.Flot(plotContainer,data,plot_options);
687 var plot = YAHOO.widget.Flot(plotContainer,data,plot_options);
688
688
689 //overview
689 //overview
690 var overview = YAHOO.widget.Flot(overviewContainer,
690 var overview = YAHOO.widget.Flot(overviewContainer,
691 overview_dataset, overview_options);
691 overview_dataset, overview_options);
692
692
693 //show initial selection on overview
693 //show initial selection on overview
694 overview.setSelection(initial_ranges);
694 overview.setSelection(initial_ranges);
695
695
696 plot.subscribe("plotselected", plotselected);
696 plot.subscribe("plotselected", plotselected);
697 plot.subscribe("plothover", plothover)
697 plot.subscribe("plothover", plothover)
698
698
699 overview.subscribe("plotselected", function (ranges) {
699 overview.subscribe("plotselected", function (ranges) {
700 plot.setSelection(ranges);
700 plot.setSelection(ranges);
701 });
701 });
702
702
703 // user choices on overview
703 // user choices on overview
704 YUE.on(choiceContainer.getElementsByTagName("input"), "click", plotchoiced, [data, initial_ranges]);
704 YUE.on(choiceContainer.getElementsByTagName("input"), "click", plotchoiced, [data, initial_ranges]);
705 }
705 }
706 SummaryPlot(${c.ts_min},${c.ts_max},${c.commit_data|n},${c.overview_data|n});
706 SummaryPlot(${c.ts_min},${c.ts_max},${c.commit_data|n},${c.overview_data|n});
707 </script>
707 </script>
708 %endif
708 %endif
709
709
710 </%def>
710 </%def>
@@ -1,438 +1,437 b''
1 import os
1 import os
2 import unittest
2 import unittest
3 from rhodecode.tests import *
3 from rhodecode.tests import *
4
4
5 from rhodecode.model.repos_group import ReposGroupModel
5 from rhodecode.model.repos_group import ReposGroupModel
6 from rhodecode.model.repo import RepoModel
6 from rhodecode.model.repo import RepoModel
7 from rhodecode.model.db import RepoGroup, User, UsersGroupRepoGroupToPerm
7 from rhodecode.model.db import RepoGroup, User, UsersGroupRepoGroupToPerm
8 from rhodecode.model.user import UserModel
8 from rhodecode.model.user import UserModel
9
9
10 from rhodecode.model.meta import Session
10 from rhodecode.model.meta import Session
11 from rhodecode.model.users_group import UsersGroupModel
11 from rhodecode.model.users_group import UsersGroupModel
12 from rhodecode.lib.auth import AuthUser
12 from rhodecode.lib.auth import AuthUser
13
13
14
14
15 def _make_group(path, desc='desc', parent_id=None,
15 def _make_group(path, desc='desc', parent_id=None,
16 skip_if_exists=False):
16 skip_if_exists=False):
17
17
18 gr = RepoGroup.get_by_group_name(path)
18 gr = RepoGroup.get_by_group_name(path)
19 if gr and skip_if_exists:
19 if gr and skip_if_exists:
20 return gr
20 return gr
21
21
22 gr = ReposGroupModel().create(path, desc, parent_id)
22 gr = ReposGroupModel().create(path, desc, parent_id)
23 return gr
23 return gr
24
24
25
25
26 class TestPermissions(unittest.TestCase):
26 class TestPermissions(unittest.TestCase):
27 def __init__(self, methodName='runTest'):
27 def __init__(self, methodName='runTest'):
28 super(TestPermissions, self).__init__(methodName=methodName)
28 super(TestPermissions, self).__init__(methodName=methodName)
29
29
30 def setUp(self):
30 def setUp(self):
31 self.u1 = UserModel().create_or_update(
31 self.u1 = UserModel().create_or_update(
32 username=u'u1', password=u'qweqwe',
32 username=u'u1', password=u'qweqwe',
33 email=u'u1@rhodecode.org', firstname=u'u1', lastname=u'u1'
33 email=u'u1@rhodecode.org', firstname=u'u1', lastname=u'u1'
34 )
34 )
35 self.u2 = UserModel().create_or_update(
35 self.u2 = UserModel().create_or_update(
36 username=u'u2', password=u'qweqwe',
36 username=u'u2', password=u'qweqwe',
37 email=u'u2@rhodecode.org', firstname=u'u2', lastname=u'u2'
37 email=u'u2@rhodecode.org', firstname=u'u2', lastname=u'u2'
38 )
38 )
39 self.u3 = UserModel().create_or_update(
39 self.u3 = UserModel().create_or_update(
40 username=u'u3', password=u'qweqwe',
40 username=u'u3', password=u'qweqwe',
41 email=u'u3@rhodecode.org', firstname=u'u3', lastname=u'u3'
41 email=u'u3@rhodecode.org', firstname=u'u3', lastname=u'u3'
42 )
42 )
43 self.anon = User.get_by_username('default')
43 self.anon = User.get_by_username('default')
44 self.a1 = UserModel().create_or_update(
44 self.a1 = UserModel().create_or_update(
45 username=u'a1', password=u'qweqwe',
45 username=u'a1', password=u'qweqwe',
46 email=u'a1@rhodecode.org', firstname=u'a1', lastname=u'a1', admin=True
46 email=u'a1@rhodecode.org', firstname=u'a1', lastname=u'a1', admin=True
47 )
47 )
48 Session().commit()
48 Session().commit()
49
49
50 def tearDown(self):
50 def tearDown(self):
51 if hasattr(self, 'test_repo'):
51 if hasattr(self, 'test_repo'):
52 RepoModel().delete(repo=self.test_repo)
52 RepoModel().delete(repo=self.test_repo)
53 UserModel().delete(self.u1)
53 UserModel().delete(self.u1)
54 UserModel().delete(self.u2)
54 UserModel().delete(self.u2)
55 UserModel().delete(self.u3)
55 UserModel().delete(self.u3)
56 UserModel().delete(self.a1)
56 UserModel().delete(self.a1)
57 if hasattr(self, 'g1'):
57 if hasattr(self, 'g1'):
58 ReposGroupModel().delete(self.g1.group_id)
58 ReposGroupModel().delete(self.g1.group_id)
59 if hasattr(self, 'g2'):
59 if hasattr(self, 'g2'):
60 ReposGroupModel().delete(self.g2.group_id)
60 ReposGroupModel().delete(self.g2.group_id)
61
61
62 if hasattr(self, 'ug1'):
62 if hasattr(self, 'ug1'):
63 UsersGroupModel().delete(self.ug1, force=True)
63 UsersGroupModel().delete(self.ug1, force=True)
64
64
65 Session().commit()
65 Session().commit()
66
66
67 def test_default_perms_set(self):
67 def test_default_perms_set(self):
68 u1_auth = AuthUser(user_id=self.u1.user_id)
68 u1_auth = AuthUser(user_id=self.u1.user_id)
69 perms = {
69 perms = {
70 'repositories_groups': {},
70 'repositories_groups': {},
71 'global': set([u'hg.create.repository', u'repository.read',
71 'global': set([u'hg.create.repository', u'repository.read',
72 u'hg.register.manual_activate']),
72 u'hg.register.manual_activate']),
73 'repositories': {u'vcs_test_hg': u'repository.read'}
73 'repositories': {u'vcs_test_hg': u'repository.read'}
74 }
74 }
75 self.assertEqual(u1_auth.permissions['repositories'][HG_REPO],
75 self.assertEqual(u1_auth.permissions['repositories'][HG_REPO],
76 perms['repositories'][HG_REPO])
76 perms['repositories'][HG_REPO])
77 new_perm = 'repository.write'
77 new_perm = 'repository.write'
78 RepoModel().grant_user_permission(repo=HG_REPO, user=self.u1,
78 RepoModel().grant_user_permission(repo=HG_REPO, user=self.u1,
79 perm=new_perm)
79 perm=new_perm)
80 Session().commit()
80 Session().commit()
81
81
82 u1_auth = AuthUser(user_id=self.u1.user_id)
82 u1_auth = AuthUser(user_id=self.u1.user_id)
83 self.assertEqual(u1_auth.permissions['repositories'][HG_REPO],
83 self.assertEqual(u1_auth.permissions['repositories'][HG_REPO],
84 new_perm)
84 new_perm)
85
85
86 def test_default_admin_perms_set(self):
86 def test_default_admin_perms_set(self):
87 a1_auth = AuthUser(user_id=self.a1.user_id)
87 a1_auth = AuthUser(user_id=self.a1.user_id)
88 perms = {
88 perms = {
89 'repositories_groups': {},
89 'repositories_groups': {},
90 'global': set([u'hg.admin']),
90 'global': set([u'hg.admin']),
91 'repositories': {u'vcs_test_hg': u'repository.admin'}
91 'repositories': {u'vcs_test_hg': u'repository.admin'}
92 }
92 }
93 self.assertEqual(a1_auth.permissions['repositories'][HG_REPO],
93 self.assertEqual(a1_auth.permissions['repositories'][HG_REPO],
94 perms['repositories'][HG_REPO])
94 perms['repositories'][HG_REPO])
95 new_perm = 'repository.write'
95 new_perm = 'repository.write'
96 RepoModel().grant_user_permission(repo=HG_REPO, user=self.a1,
96 RepoModel().grant_user_permission(repo=HG_REPO, user=self.a1,
97 perm=new_perm)
97 perm=new_perm)
98 Session().commit()
98 Session().commit()
99 # cannot really downgrade admins permissions !? they still get's set as
99 # cannot really downgrade admins permissions !? they still get's set as
100 # admin !
100 # admin !
101 u1_auth = AuthUser(user_id=self.a1.user_id)
101 u1_auth = AuthUser(user_id=self.a1.user_id)
102 self.assertEqual(u1_auth.permissions['repositories'][HG_REPO],
102 self.assertEqual(u1_auth.permissions['repositories'][HG_REPO],
103 perms['repositories'][HG_REPO])
103 perms['repositories'][HG_REPO])
104
104
105 def test_default_group_perms(self):
105 def test_default_group_perms(self):
106 self.g1 = _make_group('test1', skip_if_exists=True)
106 self.g1 = _make_group('test1', skip_if_exists=True)
107 self.g2 = _make_group('test2', skip_if_exists=True)
107 self.g2 = _make_group('test2', skip_if_exists=True)
108 u1_auth = AuthUser(user_id=self.u1.user_id)
108 u1_auth = AuthUser(user_id=self.u1.user_id)
109 perms = {
109 perms = {
110 'repositories_groups': {u'test1': 'group.read', u'test2': 'group.read'},
110 'repositories_groups': {u'test1': 'group.read', u'test2': 'group.read'},
111 'global': set([u'hg.create.repository', u'repository.read', u'hg.register.manual_activate']),
111 'global': set([u'hg.create.repository', u'repository.read', u'hg.register.manual_activate']),
112 'repositories': {u'vcs_test_hg': u'repository.read'}
112 'repositories': {u'vcs_test_hg': u'repository.read'}
113 }
113 }
114 self.assertEqual(u1_auth.permissions['repositories'][HG_REPO],
114 self.assertEqual(u1_auth.permissions['repositories'][HG_REPO],
115 perms['repositories'][HG_REPO])
115 perms['repositories'][HG_REPO])
116 self.assertEqual(u1_auth.permissions['repositories_groups'],
116 self.assertEqual(u1_auth.permissions['repositories_groups'],
117 perms['repositories_groups'])
117 perms['repositories_groups'])
118
118
119 def test_default_admin_group_perms(self):
119 def test_default_admin_group_perms(self):
120 self.g1 = _make_group('test1', skip_if_exists=True)
120 self.g1 = _make_group('test1', skip_if_exists=True)
121 self.g2 = _make_group('test2', skip_if_exists=True)
121 self.g2 = _make_group('test2', skip_if_exists=True)
122 a1_auth = AuthUser(user_id=self.a1.user_id)
122 a1_auth = AuthUser(user_id=self.a1.user_id)
123 perms = {
123 perms = {
124 'repositories_groups': {u'test1': 'group.admin', u'test2': 'group.admin'},
124 'repositories_groups': {u'test1': 'group.admin', u'test2': 'group.admin'},
125 'global': set(['hg.admin']),
125 'global': set(['hg.admin']),
126 'repositories': {u'vcs_test_hg': 'repository.admin'}
126 'repositories': {u'vcs_test_hg': 'repository.admin'}
127 }
127 }
128
128
129 self.assertEqual(a1_auth.permissions['repositories'][HG_REPO],
129 self.assertEqual(a1_auth.permissions['repositories'][HG_REPO],
130 perms['repositories'][HG_REPO])
130 perms['repositories'][HG_REPO])
131 self.assertEqual(a1_auth.permissions['repositories_groups'],
131 self.assertEqual(a1_auth.permissions['repositories_groups'],
132 perms['repositories_groups'])
132 perms['repositories_groups'])
133
133
134 def test_propagated_permission_from_users_group_by_explicit_perms_exist(self):
134 def test_propagated_permission_from_users_group_by_explicit_perms_exist(self):
135 # make group
135 # make group
136 self.ug1 = UsersGroupModel().create('G1')
136 self.ug1 = UsersGroupModel().create('G1')
137 # add user to group
137 # add user to group
138
138
139 UsersGroupModel().add_user_to_group(self.ug1, self.u1)
139 UsersGroupModel().add_user_to_group(self.ug1, self.u1)
140
140
141 # set permission to lower
141 # set permission to lower
142 new_perm = 'repository.none'
142 new_perm = 'repository.none'
143 RepoModel().grant_user_permission(repo=HG_REPO, user=self.u1, perm=new_perm)
143 RepoModel().grant_user_permission(repo=HG_REPO, user=self.u1, perm=new_perm)
144 Session().commit()
144 Session().commit()
145 u1_auth = AuthUser(user_id=self.u1.user_id)
145 u1_auth = AuthUser(user_id=self.u1.user_id)
146 self.assertEqual(u1_auth.permissions['repositories'][HG_REPO],
146 self.assertEqual(u1_auth.permissions['repositories'][HG_REPO],
147 new_perm)
147 new_perm)
148
148
149 # grant perm for group this should not override permission from user
149 # grant perm for group this should not override permission from user
150 # since it has explicitly set
150 # since it has explicitly set
151 new_perm_gr = 'repository.write'
151 new_perm_gr = 'repository.write'
152 RepoModel().grant_users_group_permission(repo=HG_REPO,
152 RepoModel().grant_users_group_permission(repo=HG_REPO,
153 group_name=self.ug1,
153 group_name=self.ug1,
154 perm=new_perm_gr)
154 perm=new_perm_gr)
155 # check perms
155 # check perms
156 u1_auth = AuthUser(user_id=self.u1.user_id)
156 u1_auth = AuthUser(user_id=self.u1.user_id)
157 perms = {
157 perms = {
158 'repositories_groups': {},
158 'repositories_groups': {},
159 'global': set([u'hg.create.repository', u'repository.read',
159 'global': set([u'hg.create.repository', u'repository.read',
160 u'hg.register.manual_activate']),
160 u'hg.register.manual_activate']),
161 'repositories': {u'vcs_test_hg': u'repository.read'}
161 'repositories': {u'vcs_test_hg': u'repository.read'}
162 }
162 }
163 self.assertEqual(u1_auth.permissions['repositories'][HG_REPO],
163 self.assertEqual(u1_auth.permissions['repositories'][HG_REPO],
164 new_perm)
164 new_perm)
165 self.assertEqual(u1_auth.permissions['repositories_groups'],
165 self.assertEqual(u1_auth.permissions['repositories_groups'],
166 perms['repositories_groups'])
166 perms['repositories_groups'])
167
167
168 def test_propagated_permission_from_users_group(self):
168 def test_propagated_permission_from_users_group(self):
169 # make group
169 # make group
170 self.ug1 = UsersGroupModel().create('G1')
170 self.ug1 = UsersGroupModel().create('G1')
171 # add user to group
171 # add user to group
172
172
173 UsersGroupModel().add_user_to_group(self.ug1, self.u3)
173 UsersGroupModel().add_user_to_group(self.ug1, self.u3)
174
174
175 # grant perm for group this should override default permission from user
175 # grant perm for group this should override default permission from user
176 new_perm_gr = 'repository.write'
176 new_perm_gr = 'repository.write'
177 RepoModel().grant_users_group_permission(repo=HG_REPO,
177 RepoModel().grant_users_group_permission(repo=HG_REPO,
178 group_name=self.ug1,
178 group_name=self.ug1,
179 perm=new_perm_gr)
179 perm=new_perm_gr)
180 # check perms
180 # check perms
181 u3_auth = AuthUser(user_id=self.u3.user_id)
181 u3_auth = AuthUser(user_id=self.u3.user_id)
182 perms = {
182 perms = {
183 'repositories_groups': {},
183 'repositories_groups': {},
184 'global': set([u'hg.create.repository', u'repository.read',
184 'global': set([u'hg.create.repository', u'repository.read',
185 u'hg.register.manual_activate']),
185 u'hg.register.manual_activate']),
186 'repositories': {u'vcs_test_hg': u'repository.read'}
186 'repositories': {u'vcs_test_hg': u'repository.read'}
187 }
187 }
188 self.assertEqual(u3_auth.permissions['repositories'][HG_REPO],
188 self.assertEqual(u3_auth.permissions['repositories'][HG_REPO],
189 new_perm_gr)
189 new_perm_gr)
190 self.assertEqual(u3_auth.permissions['repositories_groups'],
190 self.assertEqual(u3_auth.permissions['repositories_groups'],
191 perms['repositories_groups'])
191 perms['repositories_groups'])
192
192
193 def test_propagated_permission_from_users_group_lower_weight(self):
193 def test_propagated_permission_from_users_group_lower_weight(self):
194 # make group
194 # make group
195 self.ug1 = UsersGroupModel().create('G1')
195 self.ug1 = UsersGroupModel().create('G1')
196 # add user to group
196 # add user to group
197 UsersGroupModel().add_user_to_group(self.ug1, self.u1)
197 UsersGroupModel().add_user_to_group(self.ug1, self.u1)
198
198
199 # set permission to lower
199 # set permission to lower
200 new_perm_h = 'repository.write'
200 new_perm_h = 'repository.write'
201 RepoModel().grant_user_permission(repo=HG_REPO, user=self.u1,
201 RepoModel().grant_user_permission(repo=HG_REPO, user=self.u1,
202 perm=new_perm_h)
202 perm=new_perm_h)
203 Session().commit()
203 Session().commit()
204 u1_auth = AuthUser(user_id=self.u1.user_id)
204 u1_auth = AuthUser(user_id=self.u1.user_id)
205 self.assertEqual(u1_auth.permissions['repositories'][HG_REPO],
205 self.assertEqual(u1_auth.permissions['repositories'][HG_REPO],
206 new_perm_h)
206 new_perm_h)
207
207
208 # grant perm for group this should NOT override permission from user
208 # grant perm for group this should NOT override permission from user
209 # since it's lower than granted
209 # since it's lower than granted
210 new_perm_l = 'repository.read'
210 new_perm_l = 'repository.read'
211 RepoModel().grant_users_group_permission(repo=HG_REPO,
211 RepoModel().grant_users_group_permission(repo=HG_REPO,
212 group_name=self.ug1,
212 group_name=self.ug1,
213 perm=new_perm_l)
213 perm=new_perm_l)
214 # check perms
214 # check perms
215 u1_auth = AuthUser(user_id=self.u1.user_id)
215 u1_auth = AuthUser(user_id=self.u1.user_id)
216 perms = {
216 perms = {
217 'repositories_groups': {},
217 'repositories_groups': {},
218 'global': set([u'hg.create.repository', u'repository.read',
218 'global': set([u'hg.create.repository', u'repository.read',
219 u'hg.register.manual_activate']),
219 u'hg.register.manual_activate']),
220 'repositories': {u'vcs_test_hg': u'repository.write'}
220 'repositories': {u'vcs_test_hg': u'repository.write'}
221 }
221 }
222 self.assertEqual(u1_auth.permissions['repositories'][HG_REPO],
222 self.assertEqual(u1_auth.permissions['repositories'][HG_REPO],
223 new_perm_h)
223 new_perm_h)
224 self.assertEqual(u1_auth.permissions['repositories_groups'],
224 self.assertEqual(u1_auth.permissions['repositories_groups'],
225 perms['repositories_groups'])
225 perms['repositories_groups'])
226
226
227 def test_repo_in_group_permissions(self):
227 def test_repo_in_group_permissions(self):
228 self.g1 = _make_group('group1', skip_if_exists=True)
228 self.g1 = _make_group('group1', skip_if_exists=True)
229 self.g2 = _make_group('group2', skip_if_exists=True)
229 self.g2 = _make_group('group2', skip_if_exists=True)
230 Session().commit()
230 Session().commit()
231 # both perms should be read !
231 # both perms should be read !
232 u1_auth = AuthUser(user_id=self.u1.user_id)
232 u1_auth = AuthUser(user_id=self.u1.user_id)
233 self.assertEqual(u1_auth.permissions['repositories_groups'],
233 self.assertEqual(u1_auth.permissions['repositories_groups'],
234 {u'group1': u'group.read', u'group2': u'group.read'})
234 {u'group1': u'group.read', u'group2': u'group.read'})
235
235
236 a1_auth = AuthUser(user_id=self.anon.user_id)
236 a1_auth = AuthUser(user_id=self.anon.user_id)
237 self.assertEqual(a1_auth.permissions['repositories_groups'],
237 self.assertEqual(a1_auth.permissions['repositories_groups'],
238 {u'group1': u'group.read', u'group2': u'group.read'})
238 {u'group1': u'group.read', u'group2': u'group.read'})
239
239
240 #Change perms to none for both groups
240 #Change perms to none for both groups
241 ReposGroupModel().grant_user_permission(repos_group=self.g1,
241 ReposGroupModel().grant_user_permission(repos_group=self.g1,
242 user=self.anon,
242 user=self.anon,
243 perm='group.none')
243 perm='group.none')
244 ReposGroupModel().grant_user_permission(repos_group=self.g2,
244 ReposGroupModel().grant_user_permission(repos_group=self.g2,
245 user=self.anon,
245 user=self.anon,
246 perm='group.none')
246 perm='group.none')
247
247
248 u1_auth = AuthUser(user_id=self.u1.user_id)
248 u1_auth = AuthUser(user_id=self.u1.user_id)
249 self.assertEqual(u1_auth.permissions['repositories_groups'],
249 self.assertEqual(u1_auth.permissions['repositories_groups'],
250 {u'group1': u'group.none', u'group2': u'group.none'})
250 {u'group1': u'group.none', u'group2': u'group.none'})
251
251
252 a1_auth = AuthUser(user_id=self.anon.user_id)
252 a1_auth = AuthUser(user_id=self.anon.user_id)
253 self.assertEqual(a1_auth.permissions['repositories_groups'],
253 self.assertEqual(a1_auth.permissions['repositories_groups'],
254 {u'group1': u'group.none', u'group2': u'group.none'})
254 {u'group1': u'group.none', u'group2': u'group.none'})
255
255
256 # add repo to group
256 # add repo to group
257 name = RepoGroup.url_sep().join([self.g1.group_name, 'test_perm'])
257 name = RepoGroup.url_sep().join([self.g1.group_name, 'test_perm'])
258 self.test_repo = RepoModel().create_repo(
258 self.test_repo = RepoModel().create_repo(
259 repo_name=name,
259 repo_name=name,
260 repo_type='hg',
260 repo_type='hg',
261 description='',
261 description='',
262 repos_group=self.g1,
262 repos_group=self.g1,
263 owner=self.u1,
263 owner=self.u1,
264 )
264 )
265 Session().commit()
265 Session().commit()
266
266
267 u1_auth = AuthUser(user_id=self.u1.user_id)
267 u1_auth = AuthUser(user_id=self.u1.user_id)
268 self.assertEqual(u1_auth.permissions['repositories_groups'],
268 self.assertEqual(u1_auth.permissions['repositories_groups'],
269 {u'group1': u'group.none', u'group2': u'group.none'})
269 {u'group1': u'group.none', u'group2': u'group.none'})
270
270
271 a1_auth = AuthUser(user_id=self.anon.user_id)
271 a1_auth = AuthUser(user_id=self.anon.user_id)
272 self.assertEqual(a1_auth.permissions['repositories_groups'],
272 self.assertEqual(a1_auth.permissions['repositories_groups'],
273 {u'group1': u'group.none', u'group2': u'group.none'})
273 {u'group1': u'group.none', u'group2': u'group.none'})
274
274
275 #grant permission for u2 !
275 #grant permission for u2 !
276 ReposGroupModel().grant_user_permission(repos_group=self.g1,
276 ReposGroupModel().grant_user_permission(repos_group=self.g1,
277 user=self.u2,
277 user=self.u2,
278 perm='group.read')
278 perm='group.read')
279 ReposGroupModel().grant_user_permission(repos_group=self.g2,
279 ReposGroupModel().grant_user_permission(repos_group=self.g2,
280 user=self.u2,
280 user=self.u2,
281 perm='group.read')
281 perm='group.read')
282 Session().commit()
282 Session().commit()
283 self.assertNotEqual(self.u1, self.u2)
283 self.assertNotEqual(self.u1, self.u2)
284 #u1 and anon should have not change perms while u2 should !
284 #u1 and anon should have not change perms while u2 should !
285 u1_auth = AuthUser(user_id=self.u1.user_id)
285 u1_auth = AuthUser(user_id=self.u1.user_id)
286 self.assertEqual(u1_auth.permissions['repositories_groups'],
286 self.assertEqual(u1_auth.permissions['repositories_groups'],
287 {u'group1': u'group.none', u'group2': u'group.none'})
287 {u'group1': u'group.none', u'group2': u'group.none'})
288
288
289 u2_auth = AuthUser(user_id=self.u2.user_id)
289 u2_auth = AuthUser(user_id=self.u2.user_id)
290 self.assertEqual(u2_auth.permissions['repositories_groups'],
290 self.assertEqual(u2_auth.permissions['repositories_groups'],
291 {u'group1': u'group.read', u'group2': u'group.read'})
291 {u'group1': u'group.read', u'group2': u'group.read'})
292
292
293 a1_auth = AuthUser(user_id=self.anon.user_id)
293 a1_auth = AuthUser(user_id=self.anon.user_id)
294 self.assertEqual(a1_auth.permissions['repositories_groups'],
294 self.assertEqual(a1_auth.permissions['repositories_groups'],
295 {u'group1': u'group.none', u'group2': u'group.none'})
295 {u'group1': u'group.none', u'group2': u'group.none'})
296
296
297 def test_repo_group_user_as_user_group_member(self):
297 def test_repo_group_user_as_user_group_member(self):
298 # create Group1
298 # create Group1
299 self.g1 = _make_group('group1', skip_if_exists=True)
299 self.g1 = _make_group('group1', skip_if_exists=True)
300 Session().commit()
300 Session().commit()
301 a1_auth = AuthUser(user_id=self.anon.user_id)
301 a1_auth = AuthUser(user_id=self.anon.user_id)
302
302
303 self.assertEqual(a1_auth.permissions['repositories_groups'],
303 self.assertEqual(a1_auth.permissions['repositories_groups'],
304 {u'group1': u'group.read'})
304 {u'group1': u'group.read'})
305
305
306 # set default permission to none
306 # set default permission to none
307 ReposGroupModel().grant_user_permission(repos_group=self.g1,
307 ReposGroupModel().grant_user_permission(repos_group=self.g1,
308 user=self.anon,
308 user=self.anon,
309 perm='group.none')
309 perm='group.none')
310 # make group
310 # make group
311 self.ug1 = UsersGroupModel().create('G1')
311 self.ug1 = UsersGroupModel().create('G1')
312 # add user to group
312 # add user to group
313 UsersGroupModel().add_user_to_group(self.ug1, self.u1)
313 UsersGroupModel().add_user_to_group(self.ug1, self.u1)
314 Session().commit()
314 Session().commit()
315
315
316 # check if user is in the group
316 # check if user is in the group
317 membrs = [x.user_id for x in UsersGroupModel().get(self.ug1.users_group_id).members]
317 membrs = [x.user_id for x in UsersGroupModel().get(self.ug1.users_group_id).members]
318 self.assertEqual(membrs, [self.u1.user_id])
318 self.assertEqual(membrs, [self.u1.user_id])
319 # add some user to that group
319 # add some user to that group
320
320
321 # check his permissions
321 # check his permissions
322 a1_auth = AuthUser(user_id=self.anon.user_id)
322 a1_auth = AuthUser(user_id=self.anon.user_id)
323 self.assertEqual(a1_auth.permissions['repositories_groups'],
323 self.assertEqual(a1_auth.permissions['repositories_groups'],
324 {u'group1': u'group.none'})
324 {u'group1': u'group.none'})
325
325
326 u1_auth = AuthUser(user_id=self.u1.user_id)
326 u1_auth = AuthUser(user_id=self.u1.user_id)
327 self.assertEqual(u1_auth.permissions['repositories_groups'],
327 self.assertEqual(u1_auth.permissions['repositories_groups'],
328 {u'group1': u'group.none'})
328 {u'group1': u'group.none'})
329
329
330 # grant ug1 read permissions for
330 # grant ug1 read permissions for
331 ReposGroupModel().grant_users_group_permission(repos_group=self.g1,
331 ReposGroupModel().grant_users_group_permission(repos_group=self.g1,
332 group_name=self.ug1,
332 group_name=self.ug1,
333 perm='group.read')
333 perm='group.read')
334 Session().commit()
334 Session().commit()
335 # check if the
335 # check if the
336 obj = Session().query(UsersGroupRepoGroupToPerm)\
336 obj = Session().query(UsersGroupRepoGroupToPerm)\
337 .filter(UsersGroupRepoGroupToPerm.group == self.g1)\
337 .filter(UsersGroupRepoGroupToPerm.group == self.g1)\
338 .filter(UsersGroupRepoGroupToPerm.users_group == self.ug1)\
338 .filter(UsersGroupRepoGroupToPerm.users_group == self.ug1)\
339 .scalar()
339 .scalar()
340 self.assertEqual(obj.permission.permission_name, 'group.read')
340 self.assertEqual(obj.permission.permission_name, 'group.read')
341
341
342 a1_auth = AuthUser(user_id=self.anon.user_id)
342 a1_auth = AuthUser(user_id=self.anon.user_id)
343
343
344 self.assertEqual(a1_auth.permissions['repositories_groups'],
344 self.assertEqual(a1_auth.permissions['repositories_groups'],
345 {u'group1': u'group.none'})
345 {u'group1': u'group.none'})
346
346
347 u1_auth = AuthUser(user_id=self.u1.user_id)
347 u1_auth = AuthUser(user_id=self.u1.user_id)
348 self.assertEqual(u1_auth.permissions['repositories_groups'],
348 self.assertEqual(u1_auth.permissions['repositories_groups'],
349 {u'group1': u'group.read'})
349 {u'group1': u'group.read'})
350
350
351 def test_inherited_permissions_from_default_on_user_enabled(self):
351 def test_inherited_permissions_from_default_on_user_enabled(self):
352 user_model = UserModel()
352 user_model = UserModel()
353 # enable fork and create on default user
353 # enable fork and create on default user
354 usr = 'default'
354 usr = 'default'
355 user_model.revoke_perm(usr, 'hg.create.none')
355 user_model.revoke_perm(usr, 'hg.create.none')
356 user_model.grant_perm(usr, 'hg.create.repository')
356 user_model.grant_perm(usr, 'hg.create.repository')
357 user_model.revoke_perm(usr, 'hg.fork.none')
357 user_model.revoke_perm(usr, 'hg.fork.none')
358 user_model.grant_perm(usr, 'hg.fork.repository')
358 user_model.grant_perm(usr, 'hg.fork.repository')
359 # make sure inherit flag is turned on
359 # make sure inherit flag is turned on
360 self.u1.inherit_default_permissions = True
360 self.u1.inherit_default_permissions = True
361 Session().commit()
361 Session().commit()
362 u1_auth = AuthUser(user_id=self.u1.user_id)
362 u1_auth = AuthUser(user_id=self.u1.user_id)
363 # this user will have inherited permissions from default user
363 # this user will have inherited permissions from default user
364 self.assertEqual(u1_auth.permissions['global'],
364 self.assertEqual(u1_auth.permissions['global'],
365 set(['hg.create.repository', 'hg.fork.repository',
365 set(['hg.create.repository', 'hg.fork.repository',
366 'hg.register.manual_activate',
366 'hg.register.manual_activate',
367 'repository.read']))
367 'repository.read']))
368
368
369 def test_inherited_permissions_from_default_on_user_disabled(self):
369 def test_inherited_permissions_from_default_on_user_disabled(self):
370 user_model = UserModel()
370 user_model = UserModel()
371 # disable fork and create on default user
371 # disable fork and create on default user
372 usr = 'default'
372 usr = 'default'
373 user_model.revoke_perm(usr, 'hg.create.repository')
373 user_model.revoke_perm(usr, 'hg.create.repository')
374 user_model.grant_perm(usr, 'hg.create.none')
374 user_model.grant_perm(usr, 'hg.create.none')
375 user_model.revoke_perm(usr, 'hg.fork.repository')
375 user_model.revoke_perm(usr, 'hg.fork.repository')
376 user_model.grant_perm(usr, 'hg.fork.none')
376 user_model.grant_perm(usr, 'hg.fork.none')
377 # make sure inherit flag is turned on
377 # make sure inherit flag is turned on
378 self.u1.inherit_default_permissions = True
378 self.u1.inherit_default_permissions = True
379 Session().commit()
379 Session().commit()
380 u1_auth = AuthUser(user_id=self.u1.user_id)
380 u1_auth = AuthUser(user_id=self.u1.user_id)
381 # this user will have inherited permissions from default user
381 # this user will have inherited permissions from default user
382 self.assertEqual(u1_auth.permissions['global'],
382 self.assertEqual(u1_auth.permissions['global'],
383 set(['hg.create.none', 'hg.fork.none',
383 set(['hg.create.none', 'hg.fork.none',
384 'hg.register.manual_activate',
384 'hg.register.manual_activate',
385 'repository.read']))
385 'repository.read']))
386
386
387 def test_non_inherited_permissions_from_default_on_user_enabled(self):
387 def test_non_inherited_permissions_from_default_on_user_enabled(self):
388 user_model = UserModel()
388 user_model = UserModel()
389 # enable fork and create on default user
389 # enable fork and create on default user
390 usr = 'default'
390 usr = 'default'
391 user_model.revoke_perm(usr, 'hg.create.none')
391 user_model.revoke_perm(usr, 'hg.create.none')
392 user_model.grant_perm(usr, 'hg.create.repository')
392 user_model.grant_perm(usr, 'hg.create.repository')
393 user_model.revoke_perm(usr, 'hg.fork.none')
393 user_model.revoke_perm(usr, 'hg.fork.none')
394 user_model.grant_perm(usr, 'hg.fork.repository')
394 user_model.grant_perm(usr, 'hg.fork.repository')
395
395
396 #disable global perms on specific user
396 #disable global perms on specific user
397 user_model.revoke_perm(self.u1, 'hg.create.repository')
397 user_model.revoke_perm(self.u1, 'hg.create.repository')
398 user_model.grant_perm(self.u1, 'hg.create.none')
398 user_model.grant_perm(self.u1, 'hg.create.none')
399 user_model.revoke_perm(self.u1, 'hg.fork.repository')
399 user_model.revoke_perm(self.u1, 'hg.fork.repository')
400 user_model.grant_perm(self.u1, 'hg.fork.none')
400 user_model.grant_perm(self.u1, 'hg.fork.none')
401
401
402 # make sure inherit flag is turned off
402 # make sure inherit flag is turned off
403 self.u1.inherit_default_permissions = False
403 self.u1.inherit_default_permissions = False
404 Session().commit()
404 Session().commit()
405 u1_auth = AuthUser(user_id=self.u1.user_id)
405 u1_auth = AuthUser(user_id=self.u1.user_id)
406 # this user will have non inherited permissions from he's
406 # this user will have non inherited permissions from he's
407 # explicitly set permissions
407 # explicitly set permissions
408 self.assertEqual(u1_auth.permissions['global'],
408 self.assertEqual(u1_auth.permissions['global'],
409 set(['hg.create.none', 'hg.fork.none',
409 set(['hg.create.none', 'hg.fork.none',
410 'hg.register.manual_activate',
410 'hg.register.manual_activate',
411 'repository.read']))
411 'repository.read']))
412
412
413 def test_non_inherited_permissions_from_default_on_user_disabled(self):
413 def test_non_inherited_permissions_from_default_on_user_disabled(self):
414 user_model = UserModel()
414 user_model = UserModel()
415 # disable fork and create on default user
415 # disable fork and create on default user
416 usr = 'default'
416 usr = 'default'
417 user_model.revoke_perm(usr, 'hg.create.repository')
417 user_model.revoke_perm(usr, 'hg.create.repository')
418 user_model.grant_perm(usr, 'hg.create.none')
418 user_model.grant_perm(usr, 'hg.create.none')
419 user_model.revoke_perm(usr, 'hg.fork.repository')
419 user_model.revoke_perm(usr, 'hg.fork.repository')
420 user_model.grant_perm(usr, 'hg.fork.none')
420 user_model.grant_perm(usr, 'hg.fork.none')
421
421
422 #enable global perms on specific user
422 #enable global perms on specific user
423 user_model.revoke_perm(self.u1, 'hg.create.none')
423 user_model.revoke_perm(self.u1, 'hg.create.none')
424 user_model.grant_perm(self.u1, 'hg.create.repository')
424 user_model.grant_perm(self.u1, 'hg.create.repository')
425 user_model.revoke_perm(self.u1, 'hg.fork.none')
425 user_model.revoke_perm(self.u1, 'hg.fork.none')
426 user_model.grant_perm(self.u1, 'hg.fork.repository')
426 user_model.grant_perm(self.u1, 'hg.fork.repository')
427
427
428 # make sure inherit flag is turned off
428 # make sure inherit flag is turned off
429 self.u1.inherit_default_permissions = False
429 self.u1.inherit_default_permissions = False
430 Session().commit()
430 Session().commit()
431 u1_auth = AuthUser(user_id=self.u1.user_id)
431 u1_auth = AuthUser(user_id=self.u1.user_id)
432 # this user will have non inherited permissions from he's
432 # this user will have non inherited permissions from he's
433 # explicitly set permissions
433 # explicitly set permissions
434 self.assertEqual(u1_auth.permissions['global'],
434 self.assertEqual(u1_auth.permissions['global'],
435 set(['hg.create.repository', 'hg.fork.repository',
435 set(['hg.create.repository', 'hg.fork.repository',
436 'hg.register.manual_activate',
436 'hg.register.manual_activate',
437 'repository.read']))
437 'repository.read']))
438
General Comments 0
You need to be logged in to leave comments. Login now