##// END OF EJS Templates
merged beta into stable
marcink -
r2092:ecd59c28 merge default
parent child Browse files
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
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.2",
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 Session.commit()
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(filenode_old=None,
224 size, cs1, cs2, diff, st = wrapped_diff(
225 filenode_new=node,
225 filenode_old=None,
226 cut_off_limit=lim,
226 filenode_new=node,
227 ignore_whitespace=ign_whitespace_lcl,
227 cut_off_limit=lim,
228 line_context=line_context_lcl,
228 ignore_whitespace=ign_whitespace_lcl,
229 enable_comments=enable_comments)
229 line_context=line_context_lcl,
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(('added', node, diff,
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(filenode_old=filenode_old,
255 size, cs1, cs2, diff, st = wrapped_diff(
253 filenode_new=node,
256 filenode_old=filenode_old,
254 cut_off_limit=lim,
257 filenode_new=node,
255 ignore_whitespace=ign_whitespace_lcl,
258 cut_off_limit=lim,
256 line_context=line_context_lcl,
259 ignore_whitespace=ign_whitespace_lcl,
257 enable_comments=enable_comments)
260 line_context=line_context_lcl,
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(('changed', node, diff,
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(('removed', node, None,
273 c.changes[changeset.raw_id].append(
269 None, None, (0, 0)))
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.last_changeset.raw_id
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.last_changeset.raw_id
334 cs1 = filenode_old.changeset.raw_id
329 cs2 = node.last_changeset.raw_id
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 for x in
344 c.parent_tmpl = ''.join(['# Parent %s\n' % x.raw_id
339 c.changeset.parents])
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 = 'attachment; filename=%s' \
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.last_changeset.raw_id
86 cs1 = filenode_old.changeset.raw_id
87 cs2 = filenode_new.last_changeset.raw_id
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 '', path
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 fctx = self._get_filectx(path)
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 """
@@ -306,14 +306,14 b' class FileNode(Node):'
306 attribute to indicate that type should *NOT* be calculated).
306 attribute to indicate that type should *NOT* be calculated).
307 """
307 """
308 if hasattr(self, '_mimetype'):
308 if hasattr(self, '_mimetype'):
309 if (isinstance(self._mimetype,(tuple,list,)) and
309 if (isinstance(self._mimetype, (tuple, list,)) and
310 len(self._mimetype) == 2):
310 len(self._mimetype) == 2):
311 return self._mimetype
311 return self._mimetype
312 else:
312 else:
313 raise NodeError('given _mimetype attribute must be an 2 '
313 raise NodeError('given _mimetype attribute must be an 2 '
314 'element list or tuple')
314 'element list or tuple')
315
315
316 mtype,encoding = mimetypes.guess_type(self.name)
316 mtype, encoding = mimetypes.guess_type(self.name)
317
317
318 if mtype is None:
318 if mtype is None:
319 if self.is_binary:
319 if self.is_binary:
@@ -322,7 +322,7 b' class FileNode(Node):'
322 else:
322 else:
323 mtype = 'text/plain'
323 mtype = 'text/plain'
324 encoding = None
324 encoding = None
325 return mtype,encoding
325 return mtype, encoding
326
326
327 @LazyProperty
327 @LazyProperty
328 def mimetype(self):
328 def mimetype(self):
@@ -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 """
@@ -15,17 +15,17 b' from rhodecode.lib.vcs.exceptions import'
15 from rhodecode.lib.vcs.nodes import FileNode, NodeError
15 from rhodecode.lib.vcs.nodes import FileNode, NodeError
16
16
17
17
18 def get_udiff(filenode_old, filenode_new,show_whitespace=True):
18 def get_udiff(filenode_old, filenode_new, show_whitespace=True):
19 """
19 """
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.last_changeset.date
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.last_changeset.date
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(created_by=user_id, subject=subj,
111 NotificationModel().create(
108 body=body,
112 created_by=user_id, subject=subj, body=body,
109 recipients=mention_recipients,
113 recipients=mention_recipients,
110 type_=Notification.TYPE_CHANGESET_COMMENT)
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().filter(Repository.group == self)
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 = {
167 notification.TYPE_MESSAGE:_('sent message'),
173 notification.TYPE_CHANGESET_COMMENT: _('commented on commit'),
168 notification.TYPE_MENTION:_('mentioned you'),
174 notification.TYPE_MESSAGE: _('sent message'),
169 notification.TYPE_REGISTRATION:_('registered in RhodeCode')}
175 notification.TYPE_MENTION: _('mentioned you'),
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
@@ -194,10 +203,10 b' class EmailNotificationModel(BaseModel):'
194 self._tmpl_lookup = rhodecode.CONFIG['pylons.app_globals'].mako_lookup
203 self._tmpl_lookup = rhodecode.CONFIG['pylons.app_globals'].mako_lookup
195
204
196 self.email_types = {
205 self.email_types = {
197 self.TYPE_CHANGESET_COMMENT:'email_templates/changeset_comment.html',
206 self.TYPE_CHANGESET_COMMENT: 'email_templates/changeset_comment.html',
198 self.TYPE_PASSWORD_RESET:'email_templates/password_reset.html',
207 self.TYPE_PASSWORD_RESET: 'email_templates/password_reset.html',
199 self.TYPE_REGISTRATION:'email_templates/registration.html',
208 self.TYPE_REGISTRATION: 'email_templates/registration.html',
200 self.TYPE_DEFAULT:'email_templates/default.html'
209 self.TYPE_DEFAULT: 'email_templates/default.html'
201 }
210 }
202
211
203 def get_email_tmpl(self, type_, **kwargs):
212 def get_email_tmpl(self, type_, **kwargs):
@@ -210,7 +219,7 b' class EmailNotificationModel(BaseModel):'
210 base = self.email_types.get(type_, self.email_types[self.TYPE_DEFAULT])
219 base = self.email_types.get(type_, self.email_types[self.TYPE_DEFAULT])
211 email_template = self._tmpl_lookup.get_template(base)
220 email_template = self._tmpl_lookup.get_template(base)
212 # translator inject
221 # translator inject
213 _kwargs = {'_':_}
222 _kwargs = {'_': _}
214 _kwargs.update(kwargs)
223 _kwargs.update(kwargs)
215 log.debug('rendering tmpl %s with kwargs %s' % (base, _kwargs))
224 log.debug('rendering tmpl %s with kwargs %s' % (base, _kwargs))
216 return email_template.render(**_kwargs)
225 return email_template.render(**_kwargs)
@@ -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().filter(UserToPerm.user == user)\
556 obj = UserToPerm.query()\
550 .filter(UserToPerm.permission == perm).scalar()
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,16 +613,20 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'),'click',function(e){
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.addClass(e.currentTarget,'active');
619 YUD.replaceClass(e.currentTarget,'hidden', 'active');
620 YUD.removeClass(menu,'hidden');
620 YUD.replaceClass(menu, 'hidden', 'active');
621 }else{
621 }
622 YUD.removeClass(e.currentTarget,'active');
622 })
623 YUD.addClass(menu,'hidden');
623 YUE.on(YUQ('.quick_repo_menu'),'mouseleave',function(e){
624 }
624 var menu = e.currentTarget.firstElementChild.firstElementChild;
625 })
625 if(YUD.hasClass(menu,'active')){
626 YUD.replaceClass(e.currentTarget, 'active', 'hidden');
627 YUD.replaceClass(menu, 'active', 'hidden');
628 }
629 })
626 };
630 };
627
631
628
632
@@ -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.last_changeset.raw_id)}
37 ${h.hidden('diff2',c.file.changeset.raw_id)}
38 ${h.select('diff1',c.file.last_changeset.raw_id,c.file_history)}
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.last_changeset.revision,h.short_id(c.file.last_changeset.raw_id)),h.url('changeset_home',repo_name=c.repo_name,revision=c.file.last_changeset.raw_id))}</div>
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>
@@ -70,7 +70,7 b''
70 %for cnt,node in enumerate(c.file):
70 %for cnt,node in enumerate(c.file):
71 <tr class="parity${cnt%2}">
71 <tr class="parity${cnt%2}">
72 <td>
72 <td>
73 ${h.link_to(node.name,h.url('files_home',repo_name=c.repo_name,revision=c.changeset.raw_id,f_path=h.safe_unicode(node.path)),class_=file_class(node)+" ypjax-link")}
73 ${h.link_to(node.name,h.url('files_home',repo_name=c.repo_name,revision=c.changeset.raw_id,f_path=h.safe_unicode(node.path)),class_=file_class(node)+" ypjax-link")}
74 </td>
74 </td>
75 <td>
75 <td>
76 %if node.is_file():
76 %if node.is_file():
@@ -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.last_changeset.revision,h.short_id(c.file.last_changeset.raw_id)),h.url('changeset_home',repo_name=c.repo_name,revision=c.file.last_changeset.raw_id))}</div>
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.last_changeset.raw_id)}
6 ${h.hidden('diff2',c.file.changeset.raw_id)}
7 ${h.select('diff1',c.file.last_changeset.raw_id,c.file_history)}
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.last_changeset.revision,h.short_id(c.file.last_changeset.raw_id)),h.url('changeset_home',repo_name=c.repo_name,revision=c.file.last_changeset.raw_id))}</pre></div>
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.last_changeset.raw_id,f_path=c.f_path),class_="ui-btn")}
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.last_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.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.last_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.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.last_changeset.raw_id,f_path=c.f_path),class_="ui-btn")}
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.last_changeset.author),16)}"/>
35 <img alt="gravatar" src="${h.gravatar_url(h.email(c.file.changeset.author),16)}"/>
36 </div>
36 </div>
37 <div title="${c.file.last_changeset.author}" class="user">${h.person(c.file.last_changeset.author)}</div>
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.last_changeset.message,c.repo_name)}</div>
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.last_changeset.raw_id,f_path=c.f_path))}
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 selected="selected" value="8911406ad776fdd3d0b9932a2e89677e57405a48">r167:8911406ad776 (default)</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 selected="selected" value="8911406ad776fdd3d0b9932a2e89677e57405a48">r167:8911406ad776 (default)</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