diff --git a/rhodecode/controllers/api/api.py b/rhodecode/controllers/api/api.py --- a/rhodecode/controllers/api/api.py +++ b/rhodecode/controllers/api/api.py @@ -544,6 +544,15 @@ class ApiController(JSONRPCController): raise JSONRPCError('failed to create repository %s' % repo_name) @HasPermissionAnyDecorator('hg.admin') + def fork_repo(self, apiuser, repoid): + repo = RepoModel().get_repo(repoid) + if repo is None: + raise JSONRPCError('unknown repository "%s"' % (repo or repoid)) + + RepoModel().create_fork(form_data, cur_user) + + + @HasPermissionAnyDecorator('hg.admin') def delete_repo(self, apiuser, repo_name): """ Deletes a given repository diff --git a/rhodecode/controllers/feed.py b/rhodecode/controllers/feed.py --- a/rhodecode/controllers/feed.py +++ b/rhodecode/controllers/feed.py @@ -28,11 +28,12 @@ import logging from pylons import url, response, tmpl_context as c from pylons.i18n.translation import _ -from rhodecode.lib.utils2 import safe_unicode +from webhelpers.feedgenerator import Atom1Feed, Rss201rev2Feed + +from rhodecode.lib import helpers as h from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator from rhodecode.lib.base import BaseRepoController - -from webhelpers.feedgenerator import Atom1Feed, Rss201rev2Feed +from rhodecode.lib.diffs import DiffProcessor log = logging.getLogger(__name__) @@ -49,31 +50,36 @@ class FeedController(BaseRepoController) self.title = self.title = _('%s %s feed') % (c.rhodecode_name, '%s') self.language = 'en-us' self.ttl = "5" - self.feed_nr = 10 + self.feed_nr = 20 def _get_title(self, cs): - return "R%s:%s - %s" % ( - cs.revision, cs.short_id, cs.message + return "%s" % ( + h.shorter(cs.message, 160) ) def __changes(self, cs): changes = [] - a = [safe_unicode(n.path) for n in cs.added] - if a: - changes.append('\nA ' + '\nA '.join(a)) - - m = [safe_unicode(n.path) for n in cs.changed] - if m: - changes.append('\nM ' + '\nM '.join(m)) + diffprocessor = DiffProcessor(cs.diff()) + stats = diffprocessor.prepare(inline_diff=False) + for st in stats: + st.update({'added': st['stats'][0], + 'removed': st['stats'][1]}) + changes.append('\n %(operation)s %(filename)s ' + '(%(added)s lines added, %(removed)s lines removed)' + % st) + return changes - d = [safe_unicode(n.path) for n in cs.removed] - if d: - changes.append('\nD ' + '\nD '.join(d)) - - changes.append('') - - return ''.join(changes) + def __get_desc(self, cs): + desc_msg = [] + desc_msg.append('%s %s %s:
' % (cs.author, _('commited on'), + cs.date)) + desc_msg.append('
')
+        desc_msg.append(cs.message)
+        desc_msg.append('\n')
+        desc_msg.extend(self.__changes(cs))
+        desc_msg.append('
') + return desc_msg def atom(self, repo_name): """Produce an atom-1.0 feed via feedgenerator module""" @@ -87,15 +93,13 @@ class FeedController(BaseRepoController) ) for cs in reversed(list(c.rhodecode_repo[-self.feed_nr:])): - desc_msg = [] - desc_msg.append('%s - %s
' % (cs.author, cs.date))
-            desc_msg.append(self.__changes(cs))
-
             feed.add_item(title=self._get_title(cs),
                           link=url('changeset_home', repo_name=repo_name,
                                    revision=cs.raw_id, qualified=True),
                           author_name=cs.author,
-                          description=''.join(desc_msg))
+                          description=''.join(self.__get_desc(cs)),
+                          pubdate=cs.date,
+                          )
 
         response.content_type = feed.mime_type
         return feed.writeString('utf-8')
@@ -112,15 +116,12 @@ class FeedController(BaseRepoController)
         )
 
         for cs in reversed(list(c.rhodecode_repo[-self.feed_nr:])):
-            desc_msg = []
-            desc_msg.append('%s - %s
' % (cs.author, cs.date))
-            desc_msg.append(self.__changes(cs))
-
             feed.add_item(title=self._get_title(cs),
                           link=url('changeset_home', repo_name=repo_name,
                                    revision=cs.raw_id, qualified=True),
                           author_name=cs.author,
-                          description=''.join(desc_msg),
+                          description=''.join(self.__get_desc(cs)),
+                          pubdate=cs.date,
                          )
 
         response.content_type = feed.mime_type
diff --git a/rhodecode/lib/diffs.py b/rhodecode/lib/diffs.py
--- a/rhodecode/lib/diffs.py
+++ b/rhodecode/lib/diffs.py
@@ -128,7 +128,7 @@ class DiffProcessor(object):
     """
     _chunk_re = re.compile(r'@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@(.*)')
 
-    def __init__(self, diff, differ='diff', format='udiff'):
+    def __init__(self, diff, differ='diff', format='gitdiff'):
         """
         :param diff:   a text in diff format or generator
         :param format: format of diff passed, `udiff` or `gitdiff`
@@ -171,7 +171,7 @@ class DiffProcessor(object):
 
     def _extract_rev(self, line1, line2):
         """
-        Extract the filename and revision hint from a line.
+        Extract the operation (A/M/D), filename and revision hint from a line.
         """
 
         try:
@@ -189,11 +189,15 @@ class DiffProcessor(object):
                 filename = (old_filename
                             if old_filename != '/dev/null' else new_filename)
 
-                return filename, new_rev, old_rev
+                operation = 'D' if new_filename == '/dev/null' else None
+                if not operation:
+                    operation = 'M' if old_filename != '/dev/null' else 'A'
+
+                return operation, filename, new_rev, old_rev
         except (ValueError, IndexError):
             pass
 
-        return None, None, None
+        return None, None, None, None
 
     def _parse_gitdiff(self, diffiterator):
         def line_decoder(l):
@@ -278,7 +282,7 @@ class DiffProcessor(object):
             do(line)
             do(next_)
 
-    def _parse_udiff(self):
+    def _parse_udiff(self, inline_diff=True):
         """
         Parse the diff an return data for the template.
         """
@@ -293,13 +297,16 @@ class DiffProcessor(object):
                     continue
 
                 chunks = []
-                filename, old_rev, new_rev = \
+                stats = [0, 0]
+                operation, filename, old_rev, new_rev = \
                     self._extract_rev(line, lineiter.next())
                 files.append({
                     'filename':         filename,
                     'old_revision':     old_rev,
                     'new_revision':     new_rev,
-                    'chunks':           chunks
+                    'chunks':           chunks,
+                    'operation':        operation,
+                    'stats':            stats,
                 })
 
                 line = lineiter.next()
@@ -331,7 +338,6 @@ class DiffProcessor(object):
                             })
 
                     line = lineiter.next()
-
                     while old_line < old_end or new_line < new_end:
                         if line:
                             command, line = line[0], line[1:]
@@ -345,9 +351,11 @@ class DiffProcessor(object):
                         elif command == '+':
                             affects_new = True
                             action = 'add'
+                            stats[0] += 1
                         elif command == '-':
                             affects_old = True
                             action = 'del'
+                            stats[1] += 1
                         else:
                             affects_old = affects_new = True
                             action = 'unmod'
@@ -371,13 +379,16 @@ class DiffProcessor(object):
                             })
 
                         line = lineiter.next()
-
         except StopIteration:
             pass
 
+        sorter = lambda info: {'A': 0, 'M': 1, 'D': 2}.get(info['operation'])
+        if inline_diff is False:
+            return sorted(files, key=sorter)
+
         # highlight inline changes
-        for _ in files:
-            for chunk in chunks:
+        for diff_data in files:
+            for chunk in diff_data['chunks']:
                 lineiter = iter(chunk)
                 try:
                     while 1:
@@ -391,14 +402,14 @@ class DiffProcessor(object):
                 except StopIteration:
                     pass
 
-        return files
+        return sorted(files, key=sorter)
 
-    def prepare(self):
+    def prepare(self, inline_diff=True):
         """
         Prepare the passed udiff for HTML rendering. It'l return a list
         of dicts
         """
-        return self._parse_udiff()
+        return self._parse_udiff(inline_diff=inline_diff)
 
     def _safe_id(self, idstring):
         """Make a string safe for including in an id attribute.
@@ -432,7 +443,7 @@ class DiffProcessor(object):
 
     def as_html(self, table_class='code-difftable', line_class='line',
                 new_lineno_class='lineno old', old_lineno_class='lineno new',
-                code_class='code', enable_comments=False):
+                code_class='code', enable_comments=False, diff_lines=None):
         """
         Return udiff as html table with customized css classes
         """
@@ -448,7 +459,8 @@ class DiffProcessor(object):
                 }
             else:
                 return label
-        diff_lines = self.prepare()
+        if diff_lines is None:
+            diff_lines = self.prepare()
         _html_empty = True
         _html = []
         _html.append('''\n''' % {