Show More
@@ -17,3 +17,4 b' List of contributors to RhodeCode projec' | |||||
17 | Matt Zuba <matt.zuba@goodwillaz.org> |
|
17 | Matt Zuba <matt.zuba@goodwillaz.org> | |
18 | Aras Pranckevicius <aras@unity3d.com> |
|
18 | Aras Pranckevicius <aras@unity3d.com> | |
19 | Tony Bussieres <t.bussieres@gmail.com> |
|
19 | Tony Bussieres <t.bussieres@gmail.com> | |
|
20 | Erwin Kroon <e.kroon@smartmetersolutions.nl> No newline at end of file |
@@ -648,6 +648,7 b' Here is a sample excerpt from an Apache ' | |||||
648 | threads=4 \ |
|
648 | threads=4 \ | |
649 | python-path=/home/web/rhodecode/pyenv/lib/python2.6/site-packages |
|
649 | python-path=/home/web/rhodecode/pyenv/lib/python2.6/site-packages | |
650 | WSGIScriptAlias / /home/web/rhodecode/dispatch.wsgi |
|
650 | WSGIScriptAlias / /home/web/rhodecode/dispatch.wsgi | |
|
651 | WSGIPassAuthorization On | |||
651 |
|
652 | |||
652 | Example wsgi dispatch script:: |
|
653 | Example wsgi dispatch script:: | |
653 |
|
654 |
@@ -1,5 +1,5 b'' | |||||
1 | Pylons==1.0.0 |
|
1 | Pylons==1.0.0 | |
2 |
Beaker==1.6. |
|
2 | Beaker==1.6.3 | |
3 | WebHelpers>=1.2 |
|
3 | WebHelpers>=1.2 | |
4 | formencode==1.2.4 |
|
4 | formencode==1.2.4 | |
5 | SQLAlchemy==0.7.4 |
|
5 | SQLAlchemy==0.7.4 |
@@ -38,7 +38,7 b" PLATFORM_OTHERS = ('Linux', 'Darwin', 'F" | |||||
38 |
|
38 | |||
39 | requirements = [ |
|
39 | requirements = [ | |
40 | "Pylons==1.0.0", |
|
40 | "Pylons==1.0.0", | |
41 |
"Beaker==1.6. |
|
41 | "Beaker==1.6.3", | |
42 | "WebHelpers>=1.2", |
|
42 | "WebHelpers>=1.2", | |
43 | "formencode==1.2.4", |
|
43 | "formencode==1.2.4", | |
44 | "SQLAlchemy==0.7.4", |
|
44 | "SQLAlchemy==0.7.4", |
@@ -248,7 +248,7 b' class SettingsController(BaseController)' | |||||
248 |
|
248 | |||
249 | if update: |
|
249 | if update: | |
250 | h.flash(_('Updated hooks'), category='success') |
|
250 | h.flash(_('Updated hooks'), category='success') | |
251 |
|
|
251 | self.sa.commit() | |
252 | except: |
|
252 | except: | |
253 | log.error(traceback.format_exc()) |
|
253 | log.error(traceback.format_exc()) | |
254 | h.flash(_('error occurred during hook creation'), |
|
254 | h.flash(_('error occurred during hook creation'), | |
@@ -285,7 +285,7 b' class SettingsController(BaseController)' | |||||
285 | if setting_id == 'hooks': |
|
285 | if setting_id == 'hooks': | |
286 | hook_id = request.POST.get('hook_id') |
|
286 | hook_id = request.POST.get('hook_id') | |
287 | RhodeCodeUi.delete(hook_id) |
|
287 | RhodeCodeUi.delete(hook_id) | |
288 |
|
288 | self.sa.commit() | ||
289 |
|
289 | |||
290 | @HasPermissionAllDecorator('hg.admin') |
|
290 | @HasPermissionAllDecorator('hg.admin') | |
291 | def show(self, setting_id, format='html'): |
|
291 | def show(self, setting_id, format='html'): |
@@ -221,17 +221,20 b' class ChangesetController(BaseRepoContro' | |||||
221 | lim = self.cut_off_limit |
|
221 | lim = self.cut_off_limit | |
222 | if cumulative_diff > self.cut_off_limit: |
|
222 | if cumulative_diff > self.cut_off_limit: | |
223 | lim = -1 |
|
223 | lim = -1 | |
224 |
size, cs1, cs2, diff, st = wrapped_diff( |
|
224 | size, cs1, cs2, diff, st = wrapped_diff( | |
|
225 | filenode_old=None, | |||
225 |
|
|
226 | filenode_new=node, | |
226 |
|
|
227 | cut_off_limit=lim, | |
227 |
|
|
228 | ignore_whitespace=ign_whitespace_lcl, | |
228 |
|
|
229 | line_context=line_context_lcl, | |
229 |
|
|
230 | enable_comments=enable_comments | |
|
231 | ) | |||
230 | cumulative_diff += size |
|
232 | cumulative_diff += size | |
231 | c.lines_added += st[0] |
|
233 | c.lines_added += st[0] | |
232 | c.lines_deleted += st[1] |
|
234 | c.lines_deleted += st[1] | |
233 |
c.changes[changeset.raw_id].append( |
|
235 | c.changes[changeset.raw_id].append( | |
234 | cs1, cs2, st)) |
|
236 | ('added', node, diff, cs1, cs2, st) | |
|
237 | ) | |||
235 |
|
238 | |||
236 | #================================================================== |
|
239 | #================================================================== | |
237 | # CHANGED FILES |
|
240 | # CHANGED FILES | |
@@ -249,24 +252,27 b' class ChangesetController(BaseRepoContro' | |||||
249 | lim = self.cut_off_limit |
|
252 | lim = self.cut_off_limit | |
250 | if cumulative_diff > self.cut_off_limit: |
|
253 | if cumulative_diff > self.cut_off_limit: | |
251 | lim = -1 |
|
254 | lim = -1 | |
252 |
size, cs1, cs2, diff, st = wrapped_diff( |
|
255 | size, cs1, cs2, diff, st = wrapped_diff( | |
|
256 | filenode_old=filenode_old, | |||
253 |
|
|
257 | filenode_new=node, | |
254 |
|
|
258 | cut_off_limit=lim, | |
255 |
|
|
259 | ignore_whitespace=ign_whitespace_lcl, | |
256 |
|
|
260 | line_context=line_context_lcl, | |
257 |
|
|
261 | enable_comments=enable_comments | |
|
262 | ) | |||
258 | cumulative_diff += size |
|
263 | cumulative_diff += size | |
259 | c.lines_added += st[0] |
|
264 | c.lines_added += st[0] | |
260 | c.lines_deleted += st[1] |
|
265 | c.lines_deleted += st[1] | |
261 |
c.changes[changeset.raw_id].append( |
|
266 | c.changes[changeset.raw_id].append( | |
262 | cs1, cs2, st)) |
|
267 | ('changed', node, diff, cs1, cs2, st) | |
263 |
|
268 | ) | ||
264 | #================================================================== |
|
269 | #================================================================== | |
265 | # REMOVED FILES |
|
270 | # REMOVED FILES | |
266 | #================================================================== |
|
271 | #================================================================== | |
267 | for node in changeset.removed: |
|
272 | for node in changeset.removed: | |
268 |
c.changes[changeset.raw_id].append( |
|
273 | c.changes[changeset.raw_id].append( | |
269 |
|
|
274 | ('removed', node, None, None, None, (0, 0)) | |
|
275 | ) | |||
270 |
|
276 | |||
271 | # count inline comments |
|
277 | # count inline comments | |
272 | for path, lines in c.inline_comments: |
|
278 | for path, lines in c.inline_comments: | |
@@ -311,7 +317,7 b' class ChangesetController(BaseRepoContro' | |||||
311 | format='gitdiff').raw_diff() |
|
317 | format='gitdiff').raw_diff() | |
312 |
|
318 | |||
313 | cs1 = None |
|
319 | cs1 = None | |
314 |
cs2 = node. |
|
320 | cs2 = node.changeset.raw_id | |
315 | c.changes.append(('added', node, diff, cs1, cs2)) |
|
321 | c.changes.append(('added', node, diff, cs1, cs2)) | |
316 |
|
322 | |||
317 | for node in c.changeset.changed: |
|
323 | for node in c.changeset.changed: | |
@@ -325,8 +331,8 b' class ChangesetController(BaseRepoContro' | |||||
325 | diff = diffs.DiffProcessor(f_gitdiff, |
|
331 | diff = diffs.DiffProcessor(f_gitdiff, | |
326 | format='gitdiff').raw_diff() |
|
332 | format='gitdiff').raw_diff() | |
327 |
|
333 | |||
328 |
cs1 = filenode_old. |
|
334 | cs1 = filenode_old.changeset.raw_id | |
329 |
cs2 = node. |
|
335 | cs2 = node.changeset.raw_id | |
330 | c.changes.append(('changed', node, diff, cs1, cs2)) |
|
336 | c.changes.append(('changed', node, diff, cs1, cs2)) | |
331 |
|
337 | |||
332 | response.content_type = 'text/plain' |
|
338 | response.content_type = 'text/plain' | |
@@ -335,8 +341,8 b' class ChangesetController(BaseRepoContro' | |||||
335 | response.content_disposition = 'attachment; filename=%s.patch' \ |
|
341 | response.content_disposition = 'attachment; filename=%s.patch' \ | |
336 | % revision |
|
342 | % revision | |
337 |
|
343 | |||
338 |
c.parent_tmpl = ''.join(['# Parent %s\n' % x.raw_id |
|
344 | c.parent_tmpl = ''.join(['# Parent %s\n' % x.raw_id | |
339 |
|
|
345 | for x in c.changeset.parents]) | |
340 |
|
346 | |||
341 | c.diffs = '' |
|
347 | c.diffs = '' | |
342 | for x in c.changes: |
|
348 | for x in c.changes: |
@@ -428,8 +428,9 b' class FilesController(BaseRepoController' | |||||
428 |
|
428 | |||
429 | diff_name = '%s_vs_%s.diff' % (diff1, diff2) |
|
429 | diff_name = '%s_vs_%s.diff' % (diff1, diff2) | |
430 | response.content_type = 'text/plain' |
|
430 | response.content_type = 'text/plain' | |
431 |
response.content_disposition = |
|
431 | response.content_disposition = ( | |
432 | % diff_name |
|
432 | 'attachment; filename=%s' % diff_name | |
|
433 | ) | |||
433 | return diff.raw_diff() |
|
434 | return diff.raw_diff() | |
434 |
|
435 | |||
435 | elif c.action == 'raw': |
|
436 | elif c.action == 'raw': |
@@ -83,8 +83,8 b' def wrapped_diff(filenode_old, filenode_' | |||||
83 | if not diff: |
|
83 | if not diff: | |
84 | diff = wrap_to_table(_('No changes detected')) |
|
84 | diff = wrap_to_table(_('No changes detected')) | |
85 |
|
85 | |||
86 |
cs1 = filenode_old. |
|
86 | cs1 = filenode_old.changeset.raw_id | |
87 |
cs2 = filenode_new. |
|
87 | cs2 = filenode_new.changeset.raw_id | |
88 |
|
88 | |||
89 | return size, cs1, cs2, diff, stats |
|
89 | return size, cs1, cs2, diff, stats | |
90 |
|
90 |
@@ -121,6 +121,7 b' class SimpleGit(BaseVCSController):' | |||||
121 | #====================================================================== |
|
121 | #====================================================================== | |
122 | # CHECK ANONYMOUS PERMISSION |
|
122 | # CHECK ANONYMOUS PERMISSION | |
123 | #====================================================================== |
|
123 | #====================================================================== | |
|
124 | ||||
124 | if action in ['pull', 'push']: |
|
125 | if action in ['pull', 'push']: | |
125 | anonymous_user = self.__get_user('default') |
|
126 | anonymous_user = self.__get_user('default') | |
126 | username = anonymous_user.username |
|
127 | username = anonymous_user.username | |
@@ -169,15 +170,13 b' class SimpleGit(BaseVCSController):' | |||||
169 | start_response) |
|
170 | start_response) | |
170 |
|
171 | |||
171 | #check permissions for this repository |
|
172 | #check permissions for this repository | |
172 | perm = self._check_permission(action, user, |
|
173 | perm = self._check_permission(action, user, repo_name) | |
173 | repo_name) |
|
|||
174 | if perm is not True: |
|
174 | if perm is not True: | |
175 | return HTTPForbidden()(environ, start_response) |
|
175 | return HTTPForbidden()(environ, start_response) | |
176 |
|
176 | |||
177 | #=================================================================== |
|
177 | #=================================================================== | |
178 | # GIT REQUEST HANDLING |
|
178 | # GIT REQUEST HANDLING | |
179 | #=================================================================== |
|
179 | #=================================================================== | |
180 |
|
||||
181 | repo_path = safe_str(os.path.join(self.basepath, repo_name)) |
|
180 | repo_path = safe_str(os.path.join(self.basepath, repo_name)) | |
182 | log.debug('Repository path is %s' % repo_path) |
|
181 | log.debug('Repository path is %s' % repo_path) | |
183 |
|
182 | |||
@@ -203,7 +202,6 b' class SimpleGit(BaseVCSController):' | |||||
203 | :param repo_name: name of the repository |
|
202 | :param repo_name: name of the repository | |
204 | :param repo_path: full path to the repository |
|
203 | :param repo_path: full path to the repository | |
205 | """ |
|
204 | """ | |
206 |
|
||||
207 | _d = {'/' + repo_name: Repo(repo_path)} |
|
205 | _d = {'/' + repo_name: Repo(repo_path)} | |
208 | backend = dulserver.DictBackend(_d) |
|
206 | backend = dulserver.DictBackend(_d) | |
209 | gitserve = HTTPGitApplication(backend) |
|
207 | gitserve = HTTPGitApplication(backend) | |
@@ -229,19 +227,24 b' class SimpleGit(BaseVCSController):' | |||||
229 | return User.get_by_username(username) |
|
227 | return User.get_by_username(username) | |
230 |
|
228 | |||
231 | def __get_action(self, environ): |
|
229 | def __get_action(self, environ): | |
232 | """Maps git request commands into a pull or push command. |
|
230 | """ | |
|
231 | Maps git request commands into a pull or push command. | |||
233 |
|
232 | |||
234 | :param environ: |
|
233 | :param environ: | |
235 | """ |
|
234 | """ | |
236 | service = environ['QUERY_STRING'].split('=') |
|
235 | service = environ['QUERY_STRING'].split('=') | |
|
236 | ||||
237 | if len(service) > 1: |
|
237 | if len(service) > 1: | |
238 | service_cmd = service[1] |
|
238 | service_cmd = service[1] | |
239 | mapping = { |
|
239 | mapping = { | |
240 | 'git-receive-pack': 'push', |
|
240 | 'git-receive-pack': 'push', | |
241 | 'git-upload-pack': 'pull', |
|
241 | 'git-upload-pack': 'pull', | |
242 | } |
|
242 | } | |
243 |
|
243 | op = mapping[service_cmd] | ||
244 | return mapping.get(service_cmd, |
|
244 | self._git_stored_op = op | |
245 | service_cmd if service_cmd else 'other') |
|
245 | return op | |
246 | else: |
|
246 | else: | |
247 | return 'other' |
|
247 | # try to fallback to stored variable as we don't know if the last | |
|
248 | # operation is pull/push | |||
|
249 | op = getattr(self, '_git_stored_op', 'pull') | |||
|
250 | return op |
@@ -247,7 +247,7 b' class GitChangeset(BaseChangeset):' | |||||
247 | iterating commits. |
|
247 | iterating commits. | |
248 | """ |
|
248 | """ | |
249 | cmd = 'log --pretty="format: %%H" --name-status -p %s -- "%s"' % ( |
|
249 | cmd = 'log --pretty="format: %%H" --name-status -p %s -- "%s"' % ( | |
250 |
|
|
250 | self.id, path | |
251 | ) |
|
251 | ) | |
252 | so, se = self.repository.run_git_command(cmd) |
|
252 | so, se = self.repository.run_git_command(cmd) | |
253 | ids = re.findall(r'\w{40}', so) |
|
253 | ids = re.findall(r'\w{40}', so) |
@@ -187,9 +187,8 b' class MercurialChangeset(BaseChangeset):' | |||||
187 | """ |
|
187 | """ | |
188 | Returns last commit of the file at the given ``path``. |
|
188 | Returns last commit of the file at the given ``path``. | |
189 | """ |
|
189 | """ | |
190 |
|
|
190 | node = self.get_node(path) | |
191 | changeset = self.repository.get_changeset(fctx.linkrev()) |
|
191 | return node.history[0] | |
192 | return changeset |
|
|||
193 |
|
192 | |||
194 | def get_file_history(self, path): |
|
193 | def get_file_history(self, path): | |
195 | """ |
|
194 | """ |
@@ -392,8 +392,8 b' class FileNode(Node):' | |||||
392 | """ |
|
392 | """ | |
393 | Returns True if file has binary content. |
|
393 | Returns True if file has binary content. | |
394 | """ |
|
394 | """ | |
395 | bin = '\0' in self.content |
|
395 | _bin = '\0' in self.content | |
396 | return bin |
|
396 | return _bin | |
397 |
|
397 | |||
398 | @LazyProperty |
|
398 | @LazyProperty | |
399 | def extension(self): |
|
399 | def extension(self): | |
@@ -406,6 +406,10 b' class FileNode(Node):' | |||||
406 | """ |
|
406 | """ | |
407 | return bool(self.mode & stat.S_IXUSR) |
|
407 | return bool(self.mode & stat.S_IXUSR) | |
408 |
|
408 | |||
|
409 | def __repr__(self): | |||
|
410 | return '<%s %r @ %s>' % (self.__class__.__name__, self.path, | |||
|
411 | self.changeset.short_id) | |||
|
412 | ||||
409 |
|
413 | |||
410 | class RemovedFileNode(FileNode): |
|
414 | class RemovedFileNode(FileNode): | |
411 | """ |
|
415 | """ | |
@@ -537,6 +541,10 b' class DirNode(Node):' | |||||
537 |
|
541 | |||
538 | return size |
|
542 | return size | |
539 |
|
543 | |||
|
544 | def __repr__(self): | |||
|
545 | return '<%s %r @ %s>' % (self.__class__.__name__, self.path, | |||
|
546 | self.changeset.short_id) | |||
|
547 | ||||
540 |
|
548 | |||
541 | class RootNode(DirNode): |
|
549 | class RootNode(DirNode): | |
542 | """ |
|
550 | """ |
@@ -20,12 +20,12 b' def get_udiff(filenode_old, filenode_new' | |||||
20 | Returns unified diff between given ``filenode_old`` and ``filenode_new``. |
|
20 | Returns unified diff between given ``filenode_old`` and ``filenode_new``. | |
21 | """ |
|
21 | """ | |
22 | try: |
|
22 | try: | |
23 |
filenode_old_date = filenode_old. |
|
23 | filenode_old_date = filenode_old.changeset.date | |
24 | except NodeError: |
|
24 | except NodeError: | |
25 | filenode_old_date = None |
|
25 | filenode_old_date = None | |
26 |
|
26 | |||
27 | try: |
|
27 | try: | |
28 |
filenode_new_date = filenode_new. |
|
28 | filenode_new_date = filenode_new.changeset.date | |
29 | except NodeError: |
|
29 | except NodeError: | |
30 | filenode_new_date = None |
|
30 | filenode_new_date = None | |
31 |
|
31 |
@@ -67,7 +67,7 b' class ChangesetCommentsModel(BaseModel):' | |||||
67 | repo = Repository.get(repo_id) |
|
67 | repo = Repository.get(repo_id) | |
68 | cs = repo.scm_instance.get_changeset(revision) |
|
68 | cs = repo.scm_instance.get_changeset(revision) | |
69 | desc = cs.message |
|
69 | desc = cs.message | |
70 | author = cs.author_email |
|
70 | author_email = cs.author_email | |
71 | comment = ChangesetComment() |
|
71 | comment = ChangesetComment() | |
72 | comment.repo = repo |
|
72 | comment.repo = repo | |
73 | comment.user_id = user_id |
|
73 | comment.user_id = user_id | |
@@ -92,22 +92,27 b' class ChangesetCommentsModel(BaseModel):' | |||||
92 | ) |
|
92 | ) | |
93 | ) |
|
93 | ) | |
94 | body = text |
|
94 | body = text | |
|
95 | ||||
|
96 | # get the current participants of this changeset | |||
95 | recipients = ChangesetComment.get_users(revision=revision) |
|
97 | recipients = ChangesetComment.get_users(revision=revision) | |
96 | # add changeset author |
|
|||
97 | recipients += [User.get_by_email(author)] |
|
|||
98 |
|
98 | |||
99 | NotificationModel().create(created_by=user_id, subject=subj, |
|
99 | # add changeset author if it's in rhodecode system | |
100 | body=body, recipients=recipients, |
|
100 | recipients += [User.get_by_email(author_email)] | |
101 | type_=Notification.TYPE_CHANGESET_COMMENT) |
|
101 | ||
|
102 | NotificationModel().create( | |||
|
103 | created_by=user_id, subject=subj, body=body, | |||
|
104 | recipients=recipients, type_=Notification.TYPE_CHANGESET_COMMENT | |||
|
105 | ) | |||
102 |
|
106 | |||
103 | mention_recipients = set(self._extract_mentions(body))\ |
|
107 | mention_recipients = set(self._extract_mentions(body))\ | |
104 | .difference(recipients) |
|
108 | .difference(recipients) | |
105 | if mention_recipients: |
|
109 | if mention_recipients: | |
106 | subj = _('[Mention]') + ' ' + subj |
|
110 | subj = _('[Mention]') + ' ' + subj | |
107 |
NotificationModel().create( |
|
111 | NotificationModel().create( | |
108 | body=body, |
|
112 | created_by=user_id, subject=subj, body=body, | |
109 |
|
|
113 | recipients=mention_recipients, | |
110 |
|
|
114 | type_=Notification.TYPE_CHANGESET_COMMENT | |
|
115 | ) | |||
111 |
|
116 | |||
112 | return comment |
|
117 | return comment | |
113 |
|
118 |
@@ -807,7 +807,9 b' class RepoGroup(Base, BaseModel):' | |||||
807 |
|
807 | |||
808 | @property |
|
808 | @property | |
809 | def repositories(self): |
|
809 | def repositories(self): | |
810 |
return Repository.query() |
|
810 | return Repository.query()\ | |
|
811 | .filter(Repository.group == self)\ | |||
|
812 | .order_by(Repository.repo_name) | |||
811 |
|
813 | |||
812 | @property |
|
814 | @property | |
813 | def repositories_recursive_count(self): |
|
815 | def repositories_recursive_count(self): |
@@ -85,13 +85,19 b' class NotificationModel(BaseModel):' | |||||
85 | if obj: |
|
85 | if obj: | |
86 | recipients_objs.append(obj) |
|
86 | recipients_objs.append(obj) | |
87 | recipients_objs = set(recipients_objs) |
|
87 | recipients_objs = set(recipients_objs) | |
|
88 | log.debug('sending notifications %s to %s' % ( | |||
|
89 | type_, recipients_objs) | |||
|
90 | ) | |||
88 | else: |
|
91 | else: | |
89 | # empty recipients means to all admins |
|
92 | # empty recipients means to all admins | |
90 | recipients_objs = User.query().filter(User.admin == True).all() |
|
93 | recipients_objs = User.query().filter(User.admin == True).all() | |
91 |
|
94 | log.debug('sending notifications %s to admins: %s' % ( | ||
92 | notif = Notification.create(created_by=created_by_obj, subject=subject, |
|
95 | type_, recipients_objs) | |
93 | body=body, recipients=recipients_objs, |
|
96 | ) | |
94 | type_=type_) |
|
97 | notif = Notification.create( | |
|
98 | created_by=created_by_obj, subject=subject, | |||
|
99 | body=body, recipients=recipients_objs, type_=type_ | |||
|
100 | ) | |||
95 |
|
101 | |||
96 | if with_email is False: |
|
102 | if with_email is False: | |
97 | return notif |
|
103 | return notif | |
@@ -163,10 +169,12 b' class NotificationModel(BaseModel):' | |||||
163 | of notification object |
|
169 | of notification object | |
164 | """ |
|
170 | """ | |
165 |
|
171 | |||
166 | _map = {notification.TYPE_CHANGESET_COMMENT:_('commented on commit'), |
|
172 | _map = { | |
|
173 | notification.TYPE_CHANGESET_COMMENT: _('commented on commit'), | |||
167 |
|
|
174 | notification.TYPE_MESSAGE: _('sent message'), | |
168 |
|
|
175 | notification.TYPE_MENTION: _('mentioned you'), | |
169 |
|
|
176 | notification.TYPE_REGISTRATION: _('registered in RhodeCode') | |
|
177 | } | |||
170 |
|
178 | |||
171 | DATETIME_FORMAT = "%Y-%m-%d %H:%M:%S" |
|
179 | DATETIME_FORMAT = "%Y-%m-%d %H:%M:%S" | |
172 |
|
180 | |||
@@ -176,9 +184,10 b' class NotificationModel(BaseModel):' | |||||
176 | else: |
|
184 | else: | |
177 | DTF = lambda d: datetime.datetime.strftime(d, DATETIME_FORMAT) |
|
185 | DTF = lambda d: datetime.datetime.strftime(d, DATETIME_FORMAT) | |
178 | when = DTF(notification.created_on) |
|
186 | when = DTF(notification.created_on) | |
179 | data = dict(user=notification.created_by_user.username, |
|
187 | data = dict( | |
180 | action=_map[notification.type_], |
|
188 | user=notification.created_by_user.username, | |
181 | when=when) |
|
189 | action=_map[notification.type_], when=when, | |
|
190 | ) | |||
182 | return tmpl % data |
|
191 | return tmpl % data | |
183 |
|
192 | |||
184 |
|
193 |
@@ -531,6 +531,13 b' class UserModel(BaseModel):' | |||||
531 | """ |
|
531 | """ | |
532 | user = self.__get_user(user) |
|
532 | user = self.__get_user(user) | |
533 | perm = self.__get_perm(perm) |
|
533 | perm = self.__get_perm(perm) | |
|
534 | # if this permission is already granted skip it | |||
|
535 | _perm = UserToPerm.query()\ | |||
|
536 | .filter(UserToPerm.user == user)\ | |||
|
537 | .filter(UserToPerm.permission == perm)\ | |||
|
538 | .scalar() | |||
|
539 | if _perm: | |||
|
540 | return | |||
534 | new = UserToPerm() |
|
541 | new = UserToPerm() | |
535 | new.user = user |
|
542 | new.user = user | |
536 | new.permission = perm |
|
543 | new.permission = perm | |
@@ -546,7 +553,9 b' class UserModel(BaseModel):' | |||||
546 | user = self.__get_user(user) |
|
553 | user = self.__get_user(user) | |
547 | perm = self.__get_perm(perm) |
|
554 | perm = self.__get_perm(perm) | |
548 |
|
555 | |||
549 |
obj = UserToPerm.query() |
|
556 | obj = UserToPerm.query()\ | |
550 |
.filter(UserToPerm. |
|
557 | .filter(UserToPerm.user == user)\ | |
|
558 | .filter(UserToPerm.permission == perm)\ | |||
|
559 | .scalar() | |||
551 | if obj: |
|
560 | if obj: | |
552 | self.sa.delete(obj) |
|
561 | self.sa.delete(obj) |
@@ -172,6 +172,14 b' class UsersGroupModel(BaseModel):' | |||||
172 |
|
172 | |||
173 | users_group = self.__get_users_group(users_group) |
|
173 | users_group = self.__get_users_group(users_group) | |
174 |
|
174 | |||
|
175 | # if this permission is already granted skip it | |||
|
176 | _perm = UsersGroupToPerm.query()\ | |||
|
177 | .filter(UsersGroupToPerm.users_group == users_group)\ | |||
|
178 | .filter(UsersGroupToPerm.permission == perm)\ | |||
|
179 | .scalar() | |||
|
180 | if _perm: | |||
|
181 | return | |||
|
182 | ||||
175 | new = UsersGroupToPerm() |
|
183 | new = UsersGroupToPerm() | |
176 | new.users_group = users_group |
|
184 | new.users_group = users_group | |
177 | new.permission = perm |
|
185 | new.permission = perm |
@@ -613,14 +613,18 b' var deleteNotification = function(url, n' | |||||
613 | * QUICK REPO MENU |
|
613 | * QUICK REPO MENU | |
614 | */ |
|
614 | */ | |
615 | var quick_repo_menu = function(){ |
|
615 | var quick_repo_menu = function(){ | |
616 |
YUE.on(YUQ('.quick_repo_menu'),' |
|
616 | YUE.on(YUQ('.quick_repo_menu'),'mouseenter',function(e){ | |
617 | var menu = e.currentTarget.firstElementChild.firstElementChild; |
|
617 | var menu = e.currentTarget.firstElementChild.firstElementChild; | |
618 | if(YUD.hasClass(menu,'hidden')){ |
|
618 | if(YUD.hasClass(menu,'hidden')){ | |
619 |
YUD. |
|
619 | YUD.replaceClass(e.currentTarget,'hidden', 'active'); | |
620 |
YUD.re |
|
620 | YUD.replaceClass(menu, 'hidden', 'active'); | |
621 |
} |
|
621 | } | |
622 | YUD.removeClass(e.currentTarget,'active'); |
|
622 | }) | |
623 | YUD.addClass(menu,'hidden'); |
|
623 | YUE.on(YUQ('.quick_repo_menu'),'mouseleave',function(e){ | |
|
624 | var menu = e.currentTarget.firstElementChild.firstElementChild; | |||
|
625 | if(YUD.hasClass(menu,'active')){ | |||
|
626 | YUD.replaceClass(e.currentTarget, 'active', 'hidden'); | |||
|
627 | YUD.replaceClass(menu, 'active', 'hidden'); | |||
624 | } |
|
628 | } | |
625 | }) |
|
629 | }) | |
626 | }; |
|
630 | }; |
@@ -1,8 +1,9 b'' | |||||
|
1 | %if h.is_hg(c.scm_type): | |||
1 | # ${c.scm_type.upper()} changeset patch |
|
2 | # ${c.scm_type.upper()} changeset patch | |
2 | # User ${c.changeset.author|n} |
|
3 | # User ${c.changeset.author|n} | |
3 | # Date ${c.changeset.date} |
|
4 | # Date ${c.changeset.date} | |
4 | # Node ID ${c.changeset.raw_id} |
|
5 | # Node ID ${c.changeset.raw_id} | |
5 | ${c.parent_tmpl} |
|
6 | ${c.parent_tmpl} | |
6 | ${c.changeset.message} |
|
7 | ${c.changeset.message} | |
7 |
|
8 | %endif | ||
8 | ${c.diffs|n} |
|
9 | ${c.diffs|n} |
@@ -34,8 +34,8 b'' | |||||
34 | <dd> |
|
34 | <dd> | |
35 | <div> |
|
35 | <div> | |
36 | ${h.form(h.url('files_diff_home',repo_name=c.repo_name,f_path=c.f_path),method='get')} |
|
36 | ${h.form(h.url('files_diff_home',repo_name=c.repo_name,f_path=c.f_path),method='get')} | |
37 |
${h.hidden('diff2',c.file. |
|
37 | ${h.hidden('diff2',c.file.changeset.raw_id)} | |
38 |
${h.select('diff1',c.file. |
|
38 | ${h.select('diff1',c.file.changeset.raw_id,c.file_history)} | |
39 | ${h.submit('diff','diff to revision',class_="ui-btn")} |
|
39 | ${h.submit('diff','diff to revision',class_="ui-btn")} | |
40 | ${h.submit('show_rev','show at revision',class_="ui-btn")} |
|
40 | ${h.submit('show_rev','show at revision',class_="ui-btn")} | |
41 | ${h.end_form()} |
|
41 | ${h.end_form()} | |
@@ -46,7 +46,7 b'' | |||||
46 | <div class="code-header"> |
|
46 | <div class="code-header"> | |
47 | <div class="stats"> |
|
47 | <div class="stats"> | |
48 | <div class="left"><img src="${h.url('/images/icons/file.png')}"/></div> |
|
48 | <div class="left"><img src="${h.url('/images/icons/file.png')}"/></div> | |
49 |
<div class="left item">${h.link_to("r%s:%s" % (c.file. |
|
49 | <div class="left item">${h.link_to("r%s:%s" % (c.file.changeset.revision,h.short_id(c.file.changeset.raw_id)),h.url('changeset_home',repo_name=c.repo_name,revision=c.file.changeset.raw_id))}</div> | |
50 | <div class="left item">${h.format_byte_size(c.file.size,binary=True)}</div> |
|
50 | <div class="left item">${h.format_byte_size(c.file.size,binary=True)}</div> | |
51 | <div class="left item last">${c.file.mimetype}</div> |
|
51 | <div class="left item last">${c.file.mimetype}</div> | |
52 | <div class="buttons"> |
|
52 | <div class="buttons"> |
@@ -47,7 +47,7 b'' | |||||
47 | <th>${_('Name')}</th> |
|
47 | <th>${_('Name')}</th> | |
48 | <th>${_('Size')}</th> |
|
48 | <th>${_('Size')}</th> | |
49 | <th>${_('Mimetype')}</th> |
|
49 | <th>${_('Mimetype')}</th> | |
50 | <th>${_('Revision')}</th> |
|
50 | <th>${_('Last Revision')}</th> | |
51 | <th>${_('Last modified')}</th> |
|
51 | <th>${_('Last modified')}</th> | |
52 | <th>${_('Last commiter')}</th> |
|
52 | <th>${_('Last commiter')}</th> | |
53 | </tr> |
|
53 | </tr> |
@@ -42,7 +42,7 b'' | |||||
42 | <div class="code-header"> |
|
42 | <div class="code-header"> | |
43 | <div class="stats"> |
|
43 | <div class="stats"> | |
44 | <div class="left"><img src="${h.url('/images/icons/file.png')}"/></div> |
|
44 | <div class="left"><img src="${h.url('/images/icons/file.png')}"/></div> | |
45 |
<div class="left item">${h.link_to("r%s:%s" % (c.file. |
|
45 | <div class="left item">${h.link_to("r%s:%s" % (c.file.changeset.revision,h.short_id(c.file.changeset.raw_id)),h.url('changeset_home',repo_name=c.repo_name,revision=c.file.changeset.raw_id))}</div> | |
46 | <div class="left item">${h.format_byte_size(c.file.size,binary=True)}</div> |
|
46 | <div class="left item">${h.format_byte_size(c.file.size,binary=True)}</div> | |
47 | <div class="left item last">${c.file.mimetype}</div> |
|
47 | <div class="left item last">${c.file.mimetype}</div> | |
48 | <div class="buttons"> |
|
48 | <div class="buttons"> |
@@ -3,8 +3,8 b'' | |||||
3 | <dd> |
|
3 | <dd> | |
4 | <div> |
|
4 | <div> | |
5 | ${h.form(h.url('files_diff_home',repo_name=c.repo_name,f_path=c.f_path),method='get')} |
|
5 | ${h.form(h.url('files_diff_home',repo_name=c.repo_name,f_path=c.f_path),method='get')} | |
6 |
${h.hidden('diff2',c.file. |
|
6 | ${h.hidden('diff2',c.file.changeset.raw_id)} | |
7 |
${h.select('diff1',c.file. |
|
7 | ${h.select('diff1',c.file.changeset.raw_id,c.file_history)} | |
8 | ${h.submit('diff','diff to revision',class_="ui-btn")} |
|
8 | ${h.submit('diff','diff to revision',class_="ui-btn")} | |
9 | ${h.submit('show_rev','show at revision',class_="ui-btn")} |
|
9 | ${h.submit('show_rev','show at revision',class_="ui-btn")} | |
10 | ${h.end_form()} |
|
10 | ${h.end_form()} | |
@@ -16,27 +16,27 b'' | |||||
16 | <div class="code-header"> |
|
16 | <div class="code-header"> | |
17 | <div class="stats"> |
|
17 | <div class="stats"> | |
18 | <div class="left img"><img src="${h.url('/images/icons/file.png')}"/></div> |
|
18 | <div class="left img"><img src="${h.url('/images/icons/file.png')}"/></div> | |
19 |
<div class="left item"><pre>${h.link_to("r%s:%s" % (c.file. |
|
19 | <div class="left item"><pre>${h.link_to("r%s:%s" % (c.file.changeset.revision,h.short_id(c.file.changeset.raw_id)),h.url('changeset_home',repo_name=c.repo_name,revision=c.file.changeset.raw_id))}</pre></div> | |
20 | <div class="left item"><pre>${h.format_byte_size(c.file.size,binary=True)}</pre></div> |
|
20 | <div class="left item"><pre>${h.format_byte_size(c.file.size,binary=True)}</pre></div> | |
21 | <div class="left item last"><pre>${c.file.mimetype}</pre></div> |
|
21 | <div class="left item last"><pre>${c.file.mimetype}</pre></div> | |
22 | <div class="buttons"> |
|
22 | <div class="buttons"> | |
23 |
${h.link_to(_('show annotation'),h.url('files_annotate_home',repo_name=c.repo_name,revision=c.file. |
|
23 | ${h.link_to(_('show annotation'),h.url('files_annotate_home',repo_name=c.repo_name,revision=c.file.changeset.raw_id,f_path=c.f_path),class_="ui-btn")} | |
24 |
${h.link_to(_('show as raw'),h.url('files_raw_home',repo_name=c.repo_name,revision=c.file. |
|
24 | ${h.link_to(_('show as raw'),h.url('files_raw_home',repo_name=c.repo_name,revision=c.file.changeset.raw_id,f_path=c.f_path),class_="ui-btn")} | |
25 |
${h.link_to(_('download as raw'),h.url('files_rawfile_home',repo_name=c.repo_name,revision=c.file. |
|
25 | ${h.link_to(_('download as raw'),h.url('files_rawfile_home',repo_name=c.repo_name,revision=c.file.changeset.raw_id,f_path=c.f_path),class_="ui-btn")} | |
26 | % if h.HasRepoPermissionAny('repository.write','repository.admin')(c.repo_name): |
|
26 | % if h.HasRepoPermissionAny('repository.write','repository.admin')(c.repo_name): | |
27 | % if not c.file.is_binary: |
|
27 | % if not c.file.is_binary: | |
28 |
${h.link_to(_('edit'),h.url('files_edit_home',repo_name=c.repo_name,revision=c.file. |
|
28 | ${h.link_to(_('edit'),h.url('files_edit_home',repo_name=c.repo_name,revision=c.file.changeset.raw_id,f_path=c.f_path),class_="ui-btn")} | |
29 | % endif |
|
29 | % endif | |
30 | % endif |
|
30 | % endif | |
31 | </div> |
|
31 | </div> | |
32 | </div> |
|
32 | </div> | |
33 | <div class="author"> |
|
33 | <div class="author"> | |
34 | <div class="gravatar"> |
|
34 | <div class="gravatar"> | |
35 |
<img alt="gravatar" src="${h.gravatar_url(h.email(c.file. |
|
35 | <img alt="gravatar" src="${h.gravatar_url(h.email(c.file.changeset.author),16)}"/> | |
36 | </div> |
|
36 | </div> | |
37 |
<div title="${c.file. |
|
37 | <div title="${c.file.changeset.author}" class="user">${h.person(c.file.changeset.author)}</div> | |
38 | </div> |
|
38 | </div> | |
39 |
<div class="commit">${h.urlify_commit(c.file. |
|
39 | <div class="commit">${h.urlify_commit(c.file.changeset.message,c.repo_name)}</div> | |
40 | </div> |
|
40 | </div> | |
41 | <div class="code-body"> |
|
41 | <div class="code-body"> | |
42 | %if c.file.is_binary: |
|
42 | %if c.file.is_binary: | |
@@ -46,7 +46,7 b'' | |||||
46 | ${h.pygmentize(c.file,linenos=True,anchorlinenos=True,lineanchors='L',cssclass="code-highlight")} |
|
46 | ${h.pygmentize(c.file,linenos=True,anchorlinenos=True,lineanchors='L',cssclass="code-highlight")} | |
47 | %else: |
|
47 | %else: | |
48 | ${_('File is too big to display')} ${h.link_to(_('show as raw'), |
|
48 | ${_('File is too big to display')} ${h.link_to(_('show as raw'), | |
49 |
h.url('files_raw_home',repo_name=c.repo_name,revision=c.file. |
|
49 | h.url('files_raw_home',repo_name=c.repo_name,revision=c.file.changeset.raw_id,f_path=c.f_path))} | |
50 | %endif |
|
50 | %endif | |
51 | <script type="text/javascript"> |
|
51 | <script type="text/javascript"> | |
52 | function highlight_lines(lines){ |
|
52 | function highlight_lines(lines){ |
@@ -75,7 +75,7 b' class TestFilesController(TestController' | |||||
75 |
|
75 | |||
76 | #test or history |
|
76 | #test or history | |
77 | response.mustcontain("""<optgroup label="Changesets"> |
|
77 | response.mustcontain("""<optgroup label="Changesets"> | |
78 |
<option |
|
78 | <option value="8911406ad776fdd3d0b9932a2e89677e57405a48">r167:8911406ad776 (default)</option> | |
79 | <option value="aa957ed78c35a1541f508d2ec90e501b0a9e3167">r165:aa957ed78c35 (default)</option> |
|
79 | <option value="aa957ed78c35a1541f508d2ec90e501b0a9e3167">r165:aa957ed78c35 (default)</option> | |
80 | <option value="48e11b73e94c0db33e736eaeea692f990cb0b5f1">r140:48e11b73e94c (default)</option> |
|
80 | <option value="48e11b73e94c0db33e736eaeea692f990cb0b5f1">r140:48e11b73e94c (default)</option> | |
81 | <option value="adf3cbf483298563b968a6c673cd5bde5f7d5eea">r126:adf3cbf48329 (default)</option> |
|
81 | <option value="adf3cbf483298563b968a6c673cd5bde5f7d5eea">r126:adf3cbf48329 (default)</option> | |
@@ -110,23 +110,20 b' class TestFilesController(TestController' | |||||
110 | <option value="3803844fdbd3b711175fc3da9bdacfcd6d29a6fb">r7:3803844fdbd3 (default)</option> |
|
110 | <option value="3803844fdbd3b711175fc3da9bdacfcd6d29a6fb">r7:3803844fdbd3 (default)</option> | |
111 | </optgroup> |
|
111 | </optgroup> | |
112 | <optgroup label="Branches"> |
|
112 | <optgroup label="Branches"> | |
113 | <option value="27cd5cce30c96924232dffcd24178a07ffeb5dfc">default</option> |
|
113 | <option selected="selected" value="27cd5cce30c96924232dffcd24178a07ffeb5dfc">default</option> | |
114 | <option value="97e8b885c04894463c51898e14387d80c30ed1ee">git</option> |
|
114 | <option value="97e8b885c04894463c51898e14387d80c30ed1ee">git</option> | |
115 | <option value="2e6a2bf9356ca56df08807f4ad86d480da72a8f4">web</option> |
|
115 | <option value="2e6a2bf9356ca56df08807f4ad86d480da72a8f4">web</option> | |
116 | </optgroup> |
|
116 | </optgroup> | |
117 | <optgroup label="Tags"> |
|
117 | <optgroup label="Tags"> | |
118 | <option value="27cd5cce30c96924232dffcd24178a07ffeb5dfc">tip</option> |
|
118 | <option selected="selected" value="27cd5cce30c96924232dffcd24178a07ffeb5dfc">tip</option> | |
119 | <option value="fd4bdb5e9b2a29b4393a4ac6caef48c17ee1a200">0.1.4</option> |
|
119 | <option value="fd4bdb5e9b2a29b4393a4ac6caef48c17ee1a200">0.1.4</option> | |
120 | <option value="17544fbfcd33ffb439e2b728b5d526b1ef30bfcf">0.1.3</option> |
|
120 | <option value="17544fbfcd33ffb439e2b728b5d526b1ef30bfcf">0.1.3</option> | |
121 | <option value="a7e60bff65d57ac3a1a1ce3b12a70f8a9e8a7720">0.1.2</option> |
|
121 | <option value="a7e60bff65d57ac3a1a1ce3b12a70f8a9e8a7720">0.1.2</option> | |
122 | <option value="eb3a60fc964309c1a318b8dfe26aa2d1586c85ae">0.1.1</option> |
|
122 | <option value="eb3a60fc964309c1a318b8dfe26aa2d1586c85ae">0.1.1</option> | |
123 |
</optgroup> |
|
123 | </optgroup> | |
|
124 | """) | |||
124 |
|
125 | |||
125 | response.mustcontain("""<div class="commit">Partially implemented #16. filecontent/commit message/author/node name are safe_unicode now. |
|
126 | response.mustcontain("""<div class="commit">merge</div>""") | |
126 | In addition some other __str__ are unicode as well |
|
|||
127 | Added test for unicode |
|
|||
128 | Improved test to clone into uniq repository. |
|
|||
129 | removed extra unicode conversion in diff.</div>""") |
|
|||
130 |
|
127 | |||
131 | response.mustcontain("""<span style="text-transform: uppercase;"><a href="#">branch: default</a></span>""") |
|
128 | response.mustcontain("""<span style="text-transform: uppercase;"><a href="#">branch: default</a></span>""") | |
132 |
|
129 | |||
@@ -139,7 +136,7 b' removed extra unicode conversion in diff' | |||||
139 |
|
136 | |||
140 |
|
137 | |||
141 | response.mustcontain("""<optgroup label="Changesets"> |
|
138 | response.mustcontain("""<optgroup label="Changesets"> | |
142 |
<option |
|
139 | <option value="8911406ad776fdd3d0b9932a2e89677e57405a48">r167:8911406ad776 (default)</option> | |
143 | <option value="aa957ed78c35a1541f508d2ec90e501b0a9e3167">r165:aa957ed78c35 (default)</option> |
|
140 | <option value="aa957ed78c35a1541f508d2ec90e501b0a9e3167">r165:aa957ed78c35 (default)</option> | |
144 | <option value="48e11b73e94c0db33e736eaeea692f990cb0b5f1">r140:48e11b73e94c (default)</option> |
|
141 | <option value="48e11b73e94c0db33e736eaeea692f990cb0b5f1">r140:48e11b73e94c (default)</option> | |
145 | <option value="adf3cbf483298563b968a6c673cd5bde5f7d5eea">r126:adf3cbf48329 (default)</option> |
|
142 | <option value="adf3cbf483298563b968a6c673cd5bde5f7d5eea">r126:adf3cbf48329 (default)</option> | |
@@ -174,18 +171,17 b' removed extra unicode conversion in diff' | |||||
174 | <option value="3803844fdbd3b711175fc3da9bdacfcd6d29a6fb">r7:3803844fdbd3 (default)</option> |
|
171 | <option value="3803844fdbd3b711175fc3da9bdacfcd6d29a6fb">r7:3803844fdbd3 (default)</option> | |
175 | </optgroup> |
|
172 | </optgroup> | |
176 | <optgroup label="Branches"> |
|
173 | <optgroup label="Branches"> | |
177 | <option value="27cd5cce30c96924232dffcd24178a07ffeb5dfc">default</option> |
|
174 | <option selected="selected" value="27cd5cce30c96924232dffcd24178a07ffeb5dfc">default</option> | |
178 | <option value="97e8b885c04894463c51898e14387d80c30ed1ee">git</option> |
|
175 | <option value="97e8b885c04894463c51898e14387d80c30ed1ee">git</option> | |
179 | <option value="2e6a2bf9356ca56df08807f4ad86d480da72a8f4">web</option> |
|
176 | <option value="2e6a2bf9356ca56df08807f4ad86d480da72a8f4">web</option> | |
180 | </optgroup> |
|
177 | </optgroup> | |
181 | <optgroup label="Tags"> |
|
178 | <optgroup label="Tags"> | |
182 | <option value="27cd5cce30c96924232dffcd24178a07ffeb5dfc">tip</option> |
|
179 | <option selected="selected" value="27cd5cce30c96924232dffcd24178a07ffeb5dfc">tip</option> | |
183 | <option value="fd4bdb5e9b2a29b4393a4ac6caef48c17ee1a200">0.1.4</option> |
|
180 | <option value="fd4bdb5e9b2a29b4393a4ac6caef48c17ee1a200">0.1.4</option> | |
184 | <option value="17544fbfcd33ffb439e2b728b5d526b1ef30bfcf">0.1.3</option> |
|
181 | <option value="17544fbfcd33ffb439e2b728b5d526b1ef30bfcf">0.1.3</option> | |
185 | <option value="a7e60bff65d57ac3a1a1ce3b12a70f8a9e8a7720">0.1.2</option> |
|
182 | <option value="a7e60bff65d57ac3a1a1ce3b12a70f8a9e8a7720">0.1.2</option> | |
186 | <option value="eb3a60fc964309c1a318b8dfe26aa2d1586c85ae">0.1.1</option> |
|
183 | <option value="eb3a60fc964309c1a318b8dfe26aa2d1586c85ae">0.1.1</option> | |
187 | </optgroup> |
|
184 | </optgroup>""") | |
188 | """) |
|
|||
189 |
|
185 | |||
190 | response.mustcontain("""<span style="text-transform: uppercase;"><a href="#">branch: default</a></span>""") |
|
186 | response.mustcontain("""<span style="text-transform: uppercase;"><a href="#">branch: default</a></span>""") | |
191 |
|
187 |
General Comments 0
You need to be logged in to leave comments.
Login now