##// END OF EJS Templates
White space cleanup
marcink -
r3029:685ebc84 beta
parent child Browse files
Show More
@@ -1,183 +1,182 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.controllers.feed
3 rhodecode.controllers.feed
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 Feed controller for rhodecode
6 Feed controller for rhodecode
7
7
8 :created_on: Apr 23, 2010
8 :created_on: Apr 23, 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 logging
26 import logging
27
27
28 from pylons import url, response, tmpl_context as c
28 from pylons import url, response, tmpl_context as c
29 from pylons.i18n.translation import _
29 from pylons.i18n.translation import _
30
30
31 from beaker.cache import cache_region, region_invalidate
31 from beaker.cache import cache_region, region_invalidate
32 from webhelpers.feedgenerator import Atom1Feed, Rss201rev2Feed
32 from webhelpers.feedgenerator import Atom1Feed, Rss201rev2Feed
33
33
34 from rhodecode.lib import helpers as h
34 from rhodecode.lib import helpers as h
35 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
35 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
36 from rhodecode.lib.base import BaseRepoController
36 from rhodecode.lib.base import BaseRepoController
37 from rhodecode.lib.diffs import DiffProcessor, LimitedDiffContainer
37 from rhodecode.lib.diffs import DiffProcessor, LimitedDiffContainer
38 from rhodecode.model.db import CacheInvalidation
38 from rhodecode.model.db import CacheInvalidation
39 from rhodecode.lib.utils2 import safe_int, str2bool
39 from rhodecode.lib.utils2 import safe_int, str2bool
40
40
41 log = logging.getLogger(__name__)
41 log = logging.getLogger(__name__)
42
42
43
43
44 class FeedController(BaseRepoController):
44 class FeedController(BaseRepoController):
45
45
46 @LoginRequired(api_access=True)
46 @LoginRequired(api_access=True)
47 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
47 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
48 'repository.admin')
48 'repository.admin')
49 def __before__(self):
49 def __before__(self):
50 super(FeedController, self).__before__()
50 super(FeedController, self).__before__()
51 #common values for feeds
51 #common values for feeds
52 self.description = _('Changes on %s repository')
52 self.description = _('Changes on %s repository')
53 self.title = self.title = _('%s %s feed') % (c.rhodecode_name, '%s')
53 self.title = self.title = _('%s %s feed') % (c.rhodecode_name, '%s')
54 self.language = 'en-us'
54 self.language = 'en-us'
55 self.ttl = "5"
55 self.ttl = "5"
56 import rhodecode
56 import rhodecode
57 CONF = rhodecode.CONFIG
57 CONF = rhodecode.CONFIG
58 self.include_diff = str2bool(CONF.get('rss_include_diff', False))
58 self.include_diff = str2bool(CONF.get('rss_include_diff', False))
59 self.feed_nr = safe_int(CONF.get('rss_items_per_page', 20))
59 self.feed_nr = safe_int(CONF.get('rss_items_per_page', 20))
60 # we need to protect from parsing huge diffs here other way
60 # we need to protect from parsing huge diffs here other way
61 # we can kill the server
61 # we can kill the server
62 self.feed_diff_limit = safe_int(CONF.get('rss_cut_off_limit'), 32 * 1024)
62 self.feed_diff_limit = safe_int(CONF.get('rss_cut_off_limit'), 32 * 1024)
63
63
64 def _get_title(self, cs):
64 def _get_title(self, cs):
65 return "%s" % (
65 return "%s" % (
66 h.shorter(cs.message, 160)
66 h.shorter(cs.message, 160)
67 )
67 )
68
68
69 def __changes(self, cs):
69 def __changes(self, cs):
70 changes = []
70 changes = []
71 diff_processor = DiffProcessor(cs.diff(),
71 diff_processor = DiffProcessor(cs.diff(),
72 diff_limit=self.feed_diff_limit)
72 diff_limit=self.feed_diff_limit)
73 _parsed = diff_processor.prepare(inline_diff=False)
73 _parsed = diff_processor.prepare(inline_diff=False)
74 limited_diff = False
74 limited_diff = False
75 if isinstance(_parsed, LimitedDiffContainer):
75 if isinstance(_parsed, LimitedDiffContainer):
76 limited_diff = True
76 limited_diff = True
77
77
78 for st in _parsed:
78 for st in _parsed:
79 st.update({'added': st['stats'][0],
79 st.update({'added': st['stats'][0],
80 'removed': st['stats'][1]})
80 'removed': st['stats'][1]})
81 changes.append('\n %(operation)s %(filename)s '
81 changes.append('\n %(operation)s %(filename)s '
82 '(%(added)s lines added, %(removed)s lines removed)'
82 '(%(added)s lines added, %(removed)s lines removed)'
83 % st)
83 % st)
84 if limited_diff:
84 if limited_diff:
85 changes = changes + ['\n ' +
85 changes = changes + ['\n ' +
86 _('Changeset was too big and was cut off...')]
86 _('Changeset was too big and was cut off...')]
87 return diff_processor, changes
87 return diff_processor, changes
88
88
89 def __get_desc(self, cs):
89 def __get_desc(self, cs):
90 desc_msg = []
90 desc_msg = []
91 desc_msg.append('%s %s %s<br/>' % (h.person(cs.author),
91 desc_msg.append('%s %s %s<br/>' % (h.person(cs.author),
92 _('commited on'),
92 _('commited on'),
93 h.fmt_date(cs.date)))
93 h.fmt_date(cs.date)))
94 #branches, tags, bookmarks
94 #branches, tags, bookmarks
95 if cs.branch:
95 if cs.branch:
96 desc_msg.append('branch: %s<br/>' % cs.branch)
96 desc_msg.append('branch: %s<br/>' % cs.branch)
97 if h.is_hg(c.rhodecode_repo):
97 if h.is_hg(c.rhodecode_repo):
98 for book in cs.bookmarks:
98 for book in cs.bookmarks:
99 desc_msg.append('bookmark: %s<br/>' % book)
99 desc_msg.append('bookmark: %s<br/>' % book)
100 for tag in cs.tags:
100 for tag in cs.tags:
101 desc_msg.append('tag: %s<br/>' % tag)
101 desc_msg.append('tag: %s<br/>' % tag)
102 diff_processor, changes = self.__changes(cs)
102 diff_processor, changes = self.__changes(cs)
103 # rev link
103 # rev link
104 _url = url('changeset_home', repo_name=cs.repository.name,
104 _url = url('changeset_home', repo_name=cs.repository.name,
105 revision=cs.raw_id, qualified=True)
105 revision=cs.raw_id, qualified=True)
106 desc_msg.append('changesest: <a href="%s">%s</a>' % (_url, cs.raw_id[:8]))
106 desc_msg.append('changesest: <a href="%s">%s</a>' % (_url, cs.raw_id[:8]))
107
107
108 desc_msg.append('<pre>')
108 desc_msg.append('<pre>')
109 desc_msg.append(cs.message)
109 desc_msg.append(cs.message)
110 desc_msg.append('\n')
110 desc_msg.append('\n')
111 desc_msg.extend(changes)
111 desc_msg.extend(changes)
112 if self.include_diff:
112 if self.include_diff:
113 desc_msg.append('\n\n')
113 desc_msg.append('\n\n')
114 desc_msg.append(diff_processor.as_raw())
114 desc_msg.append(diff_processor.as_raw())
115 desc_msg.append('</pre>')
115 desc_msg.append('</pre>')
116 return desc_msg
116 return desc_msg
117
117
118 def atom(self, repo_name):
118 def atom(self, repo_name):
119 """Produce an atom-1.0 feed via feedgenerator module"""
119 """Produce an atom-1.0 feed via feedgenerator module"""
120
120
121 @cache_region('long_term')
121 @cache_region('long_term')
122 def _get_feed_from_cache(key):
122 def _get_feed_from_cache(key):
123 feed = Atom1Feed(
123 feed = Atom1Feed(
124 title=self.title % repo_name,
124 title=self.title % repo_name,
125 link=url('summary_home', repo_name=repo_name,
125 link=url('summary_home', repo_name=repo_name,
126 qualified=True),
126 qualified=True),
127 description=self.description % repo_name,
127 description=self.description % repo_name,
128 language=self.language,
128 language=self.language,
129 ttl=self.ttl
129 ttl=self.ttl
130 )
130 )
131
131
132 for cs in reversed(list(c.rhodecode_repo[-self.feed_nr:])):
132 for cs in reversed(list(c.rhodecode_repo[-self.feed_nr:])):
133 feed.add_item(title=self._get_title(cs),
133 feed.add_item(title=self._get_title(cs),
134 link=url('changeset_home', repo_name=repo_name,
134 link=url('changeset_home', repo_name=repo_name,
135 revision=cs.raw_id, qualified=True),
135 revision=cs.raw_id, qualified=True),
136 author_name=cs.author,
136 author_name=cs.author,
137 description=''.join(self.__get_desc(cs)),
137 description=''.join(self.__get_desc(cs)),
138 pubdate=cs.date,
138 pubdate=cs.date,
139 )
139 )
140
140
141 response.content_type = feed.mime_type
141 response.content_type = feed.mime_type
142 return feed.writeString('utf-8')
142 return feed.writeString('utf-8')
143
143
144 key = repo_name + '_ATOM'
144 key = repo_name + '_ATOM'
145 inv = CacheInvalidation.invalidate(key)
145 inv = CacheInvalidation.invalidate(key)
146 if inv is not None:
146 if inv is not None:
147 region_invalidate(_get_feed_from_cache, None, key)
147 region_invalidate(_get_feed_from_cache, None, key)
148 CacheInvalidation.set_valid(inv.cache_key)
148 CacheInvalidation.set_valid(inv.cache_key)
149 return _get_feed_from_cache(key)
149 return _get_feed_from_cache(key)
150
150
151 def rss(self, repo_name):
151 def rss(self, repo_name):
152 """Produce an rss2 feed via feedgenerator module"""
152 """Produce an rss2 feed via feedgenerator module"""
153
153
154 @cache_region('long_term')
154 @cache_region('long_term')
155 def _get_feed_from_cache(key):
155 def _get_feed_from_cache(key):
156 feed = Rss201rev2Feed(
156 feed = Rss201rev2Feed(
157 title=self.title % repo_name,
157 title=self.title % repo_name,
158 link=url('summary_home', repo_name=repo_name,
158 link=url('summary_home', repo_name=repo_name,
159 qualified=True),
159 qualified=True),
160 description=self.description % repo_name,
160 description=self.description % repo_name,
161 language=self.language,
161 language=self.language,
162 ttl=self.ttl
162 ttl=self.ttl
163 )
163 )
164
164
165 for cs in reversed(list(c.rhodecode_repo[-self.feed_nr:])):
165 for cs in reversed(list(c.rhodecode_repo[-self.feed_nr:])):
166 feed.add_item(title=self._get_title(cs),
166 feed.add_item(title=self._get_title(cs),
167 link=url('changeset_home', repo_name=repo_name,
167 link=url('changeset_home', repo_name=repo_name,
168 revision=cs.raw_id, qualified=True),
168 revision=cs.raw_id, qualified=True),
169 author_name=cs.author,
169 author_name=cs.author,
170 description=''.join(self.__get_desc(cs)),
170 description=''.join(self.__get_desc(cs)),
171 pubdate=cs.date,
171 pubdate=cs.date,
172 )
172 )
173
173
174 response.content_type = feed.mime_type
174 response.content_type = feed.mime_type
175 return feed.writeString('utf-8')
175 return feed.writeString('utf-8')
176
176
177 key = repo_name + '_RSS'
177 key = repo_name + '_RSS'
178 inv = CacheInvalidation.invalidate(key)
178 inv = CacheInvalidation.invalidate(key)
179 if inv is not None:
179 if inv is not None:
180 region_invalidate(_get_feed_from_cache, None, key)
180 region_invalidate(_get_feed_from_cache, None, key)
181 CacheInvalidation.set_valid(inv.cache_key)
181 CacheInvalidation.set_valid(inv.cache_key)
182 return _get_feed_from_cache(key)
182 return _get_feed_from_cache(key)
183
@@ -1,768 +1,768 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.lib.diffs
3 rhodecode.lib.diffs
4 ~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~
5
5
6 Set of diffing helpers, previously part of vcs
6 Set of diffing helpers, previously part of vcs
7
7
8
8
9 :created_on: Dec 4, 2011
9 :created_on: Dec 4, 2011
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 :original copyright: 2007-2008 by Armin Ronacher
12 :original copyright: 2007-2008 by Armin Ronacher
13 :license: GPLv3, see COPYING for more details.
13 :license: GPLv3, see COPYING for more details.
14 """
14 """
15 # This program is free software: you can redistribute it and/or modify
15 # This program is free software: you can redistribute it and/or modify
16 # it under the terms of the GNU General Public License as published by
16 # it under the terms of the GNU General Public License as published by
17 # the Free Software Foundation, either version 3 of the License, or
17 # the Free Software Foundation, either version 3 of the License, or
18 # (at your option) any later version.
18 # (at your option) any later version.
19 #
19 #
20 # This program is distributed in the hope that it will be useful,
20 # This program is distributed in the hope that it will be useful,
21 # but WITHOUT ANY WARRANTY; without even the implied warranty of
21 # but WITHOUT ANY WARRANTY; without even the implied warranty of
22 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 # GNU General Public License for more details.
23 # GNU General Public License for more details.
24 #
24 #
25 # You should have received a copy of the GNU General Public License
25 # You should have received a copy of the GNU General Public License
26 # along with this program. If not, see <http://www.gnu.org/licenses/>.
26 # along with this program. If not, see <http://www.gnu.org/licenses/>.
27
27
28 import re
28 import re
29 import difflib
29 import difflib
30 import logging
30 import logging
31 import traceback
31 import traceback
32
32
33 from itertools import tee, imap
33 from itertools import tee, imap
34
34
35 from mercurial import patch
35 from mercurial import patch
36 from mercurial.mdiff import diffopts
36 from mercurial.mdiff import diffopts
37 from mercurial.bundlerepo import bundlerepository
37 from mercurial.bundlerepo import bundlerepository
38
38
39 from pylons.i18n.translation import _
39 from pylons.i18n.translation import _
40
40
41 from rhodecode.lib.compat import BytesIO
41 from rhodecode.lib.compat import BytesIO
42 from rhodecode.lib.vcs.utils.hgcompat import localrepo
42 from rhodecode.lib.vcs.utils.hgcompat import localrepo
43 from rhodecode.lib.vcs.exceptions import VCSError
43 from rhodecode.lib.vcs.exceptions import VCSError
44 from rhodecode.lib.vcs.nodes import FileNode, SubModuleNode
44 from rhodecode.lib.vcs.nodes import FileNode, SubModuleNode
45 from rhodecode.lib.vcs.backends.base import EmptyChangeset
45 from rhodecode.lib.vcs.backends.base import EmptyChangeset
46 from rhodecode.lib.helpers import escape
46 from rhodecode.lib.helpers import escape
47 from rhodecode.lib.utils import make_ui
47 from rhodecode.lib.utils import make_ui
48 from rhodecode.lib.utils2 import safe_unicode
48 from rhodecode.lib.utils2 import safe_unicode
49
49
50 log = logging.getLogger(__name__)
50 log = logging.getLogger(__name__)
51
51
52
52
53 def wrap_to_table(str_):
53 def wrap_to_table(str_):
54 return '''<table class="code-difftable">
54 return '''<table class="code-difftable">
55 <tr class="line no-comment">
55 <tr class="line no-comment">
56 <td class="lineno new"></td>
56 <td class="lineno new"></td>
57 <td class="code no-comment"><pre>%s</pre></td>
57 <td class="code no-comment"><pre>%s</pre></td>
58 </tr>
58 </tr>
59 </table>''' % str_
59 </table>''' % str_
60
60
61
61
62 def wrapped_diff(filenode_old, filenode_new, cut_off_limit=None,
62 def wrapped_diff(filenode_old, filenode_new, cut_off_limit=None,
63 ignore_whitespace=True, line_context=3,
63 ignore_whitespace=True, line_context=3,
64 enable_comments=False):
64 enable_comments=False):
65 """
65 """
66 returns a wrapped diff into a table, checks for cut_off_limit and presents
66 returns a wrapped diff into a table, checks for cut_off_limit and presents
67 proper message
67 proper message
68 """
68 """
69
69
70 if filenode_old is None:
70 if filenode_old is None:
71 filenode_old = FileNode(filenode_new.path, '', EmptyChangeset())
71 filenode_old = FileNode(filenode_new.path, '', EmptyChangeset())
72
72
73 if filenode_old.is_binary or filenode_new.is_binary:
73 if filenode_old.is_binary or filenode_new.is_binary:
74 diff = wrap_to_table(_('binary file'))
74 diff = wrap_to_table(_('binary file'))
75 stats = (0, 0)
75 stats = (0, 0)
76 size = 0
76 size = 0
77
77
78 elif cut_off_limit != -1 and (cut_off_limit is None or
78 elif cut_off_limit != -1 and (cut_off_limit is None or
79 (filenode_old.size < cut_off_limit and filenode_new.size < cut_off_limit)):
79 (filenode_old.size < cut_off_limit and filenode_new.size < cut_off_limit)):
80
80
81 f_gitdiff = get_gitdiff(filenode_old, filenode_new,
81 f_gitdiff = get_gitdiff(filenode_old, filenode_new,
82 ignore_whitespace=ignore_whitespace,
82 ignore_whitespace=ignore_whitespace,
83 context=line_context)
83 context=line_context)
84 diff_processor = DiffProcessor(f_gitdiff, format='gitdiff')
84 diff_processor = DiffProcessor(f_gitdiff, format='gitdiff')
85
85
86 diff = diff_processor.as_html(enable_comments=enable_comments)
86 diff = diff_processor.as_html(enable_comments=enable_comments)
87 stats = diff_processor.stat()
87 stats = diff_processor.stat()
88 size = len(diff or '')
88 size = len(diff or '')
89 else:
89 else:
90 diff = wrap_to_table(_('Changeset was too big and was cut off, use '
90 diff = wrap_to_table(_('Changeset was too big and was cut off, use '
91 'diff menu to display this diff'))
91 'diff menu to display this diff'))
92 stats = (0, 0)
92 stats = (0, 0)
93 size = 0
93 size = 0
94 if not diff:
94 if not diff:
95 submodules = filter(lambda o: isinstance(o, SubModuleNode),
95 submodules = filter(lambda o: isinstance(o, SubModuleNode),
96 [filenode_new, filenode_old])
96 [filenode_new, filenode_old])
97 if submodules:
97 if submodules:
98 diff = wrap_to_table(escape('Submodule %r' % submodules[0]))
98 diff = wrap_to_table(escape('Submodule %r' % submodules[0]))
99 else:
99 else:
100 diff = wrap_to_table(_('No changes detected'))
100 diff = wrap_to_table(_('No changes detected'))
101
101
102 cs1 = filenode_old.changeset.raw_id
102 cs1 = filenode_old.changeset.raw_id
103 cs2 = filenode_new.changeset.raw_id
103 cs2 = filenode_new.changeset.raw_id
104
104
105 return size, cs1, cs2, diff, stats
105 return size, cs1, cs2, diff, stats
106
106
107
107
108 def get_gitdiff(filenode_old, filenode_new, ignore_whitespace=True, context=3):
108 def get_gitdiff(filenode_old, filenode_new, ignore_whitespace=True, context=3):
109 """
109 """
110 Returns git style diff between given ``filenode_old`` and ``filenode_new``.
110 Returns git style diff between given ``filenode_old`` and ``filenode_new``.
111
111
112 :param ignore_whitespace: ignore whitespaces in diff
112 :param ignore_whitespace: ignore whitespaces in diff
113 """
113 """
114 # make sure we pass in default context
114 # make sure we pass in default context
115 context = context or 3
115 context = context or 3
116 submodules = filter(lambda o: isinstance(o, SubModuleNode),
116 submodules = filter(lambda o: isinstance(o, SubModuleNode),
117 [filenode_new, filenode_old])
117 [filenode_new, filenode_old])
118 if submodules:
118 if submodules:
119 return ''
119 return ''
120
120
121 for filenode in (filenode_old, filenode_new):
121 for filenode in (filenode_old, filenode_new):
122 if not isinstance(filenode, FileNode):
122 if not isinstance(filenode, FileNode):
123 raise VCSError("Given object should be FileNode object, not %s"
123 raise VCSError("Given object should be FileNode object, not %s"
124 % filenode.__class__)
124 % filenode.__class__)
125
125
126 repo = filenode_new.changeset.repository
126 repo = filenode_new.changeset.repository
127 old_raw_id = getattr(filenode_old.changeset, 'raw_id', repo.EMPTY_CHANGESET)
127 old_raw_id = getattr(filenode_old.changeset, 'raw_id', repo.EMPTY_CHANGESET)
128 new_raw_id = getattr(filenode_new.changeset, 'raw_id', repo.EMPTY_CHANGESET)
128 new_raw_id = getattr(filenode_new.changeset, 'raw_id', repo.EMPTY_CHANGESET)
129
129
130 vcs_gitdiff = repo.get_diff(old_raw_id, new_raw_id, filenode_new.path,
130 vcs_gitdiff = repo.get_diff(old_raw_id, new_raw_id, filenode_new.path,
131 ignore_whitespace, context)
131 ignore_whitespace, context)
132 return vcs_gitdiff
132 return vcs_gitdiff
133
133
134 NEW_FILENODE = 1
134 NEW_FILENODE = 1
135 DEL_FILENODE = 2
135 DEL_FILENODE = 2
136 MOD_FILENODE = 3
136 MOD_FILENODE = 3
137 RENAMED_FILENODE = 4
137 RENAMED_FILENODE = 4
138 CHMOD_FILENODE = 5
138 CHMOD_FILENODE = 5
139
139
140
140
141 class DiffLimitExceeded(Exception):
141 class DiffLimitExceeded(Exception):
142 pass
142 pass
143
143
144
144
145 class LimitedDiffContainer(object):
145 class LimitedDiffContainer(object):
146
146
147 def __init__(self, diff_limit, cur_diff_size, diff):
147 def __init__(self, diff_limit, cur_diff_size, diff):
148 self.diff = diff
148 self.diff = diff
149 self.diff_limit = diff_limit
149 self.diff_limit = diff_limit
150 self.cur_diff_size = cur_diff_size
150 self.cur_diff_size = cur_diff_size
151
151
152 def __iter__(self):
152 def __iter__(self):
153 for l in self.diff:
153 for l in self.diff:
154 yield l
154 yield l
155
155
156
156
157 class DiffProcessor(object):
157 class DiffProcessor(object):
158 """
158 """
159 Give it a unified or git diff and it returns a list of the files that were
159 Give it a unified or git diff and it returns a list of the files that were
160 mentioned in the diff together with a dict of meta information that
160 mentioned in the diff together with a dict of meta information that
161 can be used to render it in a HTML template.
161 can be used to render it in a HTML template.
162 """
162 """
163 _chunk_re = re.compile(r'^@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@(.*)')
163 _chunk_re = re.compile(r'^@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@(.*)')
164 _newline_marker = re.compile(r'^\\ No newline at end of file')
164 _newline_marker = re.compile(r'^\\ No newline at end of file')
165 _git_header_re = re.compile(r"""
165 _git_header_re = re.compile(r"""
166 #^diff[ ]--git
166 #^diff[ ]--git
167 [ ]a/(?P<a_path>.+?)[ ]b/(?P<b_path>.+?)\n
167 [ ]a/(?P<a_path>.+?)[ ]b/(?P<b_path>.+?)\n
168 (?:^similarity[ ]index[ ](?P<similarity_index>\d+)%\n
168 (?:^similarity[ ]index[ ](?P<similarity_index>\d+)%\n
169 ^rename[ ]from[ ](?P<rename_from>\S+)\n
169 ^rename[ ]from[ ](?P<rename_from>\S+)\n
170 ^rename[ ]to[ ](?P<rename_to>\S+)(?:\n|$))?
170 ^rename[ ]to[ ](?P<rename_to>\S+)(?:\n|$))?
171 (?:^old[ ]mode[ ](?P<old_mode>\d+)\n
171 (?:^old[ ]mode[ ](?P<old_mode>\d+)\n
172 ^new[ ]mode[ ](?P<new_mode>\d+)(?:\n|$))?
172 ^new[ ]mode[ ](?P<new_mode>\d+)(?:\n|$))?
173 (?:^new[ ]file[ ]mode[ ](?P<new_file_mode>.+)(?:\n|$))?
173 (?:^new[ ]file[ ]mode[ ](?P<new_file_mode>.+)(?:\n|$))?
174 (?:^deleted[ ]file[ ]mode[ ](?P<deleted_file_mode>.+)(?:\n|$))?
174 (?:^deleted[ ]file[ ]mode[ ](?P<deleted_file_mode>.+)(?:\n|$))?
175 (?:^index[ ](?P<a_blob_id>[0-9A-Fa-f]+)
175 (?:^index[ ](?P<a_blob_id>[0-9A-Fa-f]+)
176 \.\.(?P<b_blob_id>[0-9A-Fa-f]+)[ ]?(?P<b_mode>.+)?(?:\n|$))?
176 \.\.(?P<b_blob_id>[0-9A-Fa-f]+)[ ]?(?P<b_mode>.+)?(?:\n|$))?
177 (?:^---[ ](a/(?P<a_file>.+)|/dev/null)(?:\n|$))?
177 (?:^---[ ](a/(?P<a_file>.+)|/dev/null)(?:\n|$))?
178 (?:^\+\+\+[ ](b/(?P<b_file>.+)|/dev/null)(?:\n|$))?
178 (?:^\+\+\+[ ](b/(?P<b_file>.+)|/dev/null)(?:\n|$))?
179 """, re.VERBOSE | re.MULTILINE)
179 """, re.VERBOSE | re.MULTILINE)
180 _hg_header_re = re.compile(r"""
180 _hg_header_re = re.compile(r"""
181 #^diff[ ]--git
181 #^diff[ ]--git
182 [ ]a/(?P<a_path>.+?)[ ]b/(?P<b_path>.+?)\n
182 [ ]a/(?P<a_path>.+?)[ ]b/(?P<b_path>.+?)\n
183 (?:^similarity[ ]index[ ](?P<similarity_index>\d+)%(?:\n|$))?
183 (?:^similarity[ ]index[ ](?P<similarity_index>\d+)%(?:\n|$))?
184 (?:^rename[ ]from[ ](?P<rename_from>\S+)\n
184 (?:^rename[ ]from[ ](?P<rename_from>\S+)\n
185 ^rename[ ]to[ ](?P<rename_to>\S+)(?:\n|$))?
185 ^rename[ ]to[ ](?P<rename_to>\S+)(?:\n|$))?
186 (?:^old[ ]mode[ ](?P<old_mode>\d+)\n
186 (?:^old[ ]mode[ ](?P<old_mode>\d+)\n
187 ^new[ ]mode[ ](?P<new_mode>\d+)(?:\n|$))?
187 ^new[ ]mode[ ](?P<new_mode>\d+)(?:\n|$))?
188 (?:^new[ ]file[ ]mode[ ](?P<new_file_mode>.+)(?:\n|$))?
188 (?:^new[ ]file[ ]mode[ ](?P<new_file_mode>.+)(?:\n|$))?
189 (?:^deleted[ ]file[ ]mode[ ](?P<deleted_file_mode>.+)(?:\n|$))?
189 (?:^deleted[ ]file[ ]mode[ ](?P<deleted_file_mode>.+)(?:\n|$))?
190 (?:^index[ ](?P<a_blob_id>[0-9A-Fa-f]+)
190 (?:^index[ ](?P<a_blob_id>[0-9A-Fa-f]+)
191 \.\.(?P<b_blob_id>[0-9A-Fa-f]+)[ ]?(?P<b_mode>.+)?(?:\n|$))?
191 \.\.(?P<b_blob_id>[0-9A-Fa-f]+)[ ]?(?P<b_mode>.+)?(?:\n|$))?
192 (?:^---[ ](a/(?P<a_file>.+)|/dev/null)(?:\n|$))?
192 (?:^---[ ](a/(?P<a_file>.+)|/dev/null)(?:\n|$))?
193 (?:^\+\+\+[ ](b/(?P<b_file>.+)|/dev/null)(?:\n|$))?
193 (?:^\+\+\+[ ](b/(?P<b_file>.+)|/dev/null)(?:\n|$))?
194 """, re.VERBOSE | re.MULTILINE)
194 """, re.VERBOSE | re.MULTILINE)
195
195
196 def __init__(self, diff, vcs='hg', format='gitdiff', diff_limit=None):
196 def __init__(self, diff, vcs='hg', format='gitdiff', diff_limit=None):
197 """
197 """
198 :param diff: a text in diff format
198 :param diff: a text in diff format
199 :param vcs: type of version controll hg or git
199 :param vcs: type of version controll hg or git
200 :param format: format of diff passed, `udiff` or `gitdiff`
200 :param format: format of diff passed, `udiff` or `gitdiff`
201 :param diff_limit: define the size of diff that is considered "big"
201 :param diff_limit: define the size of diff that is considered "big"
202 based on that parameter cut off will be triggered, set to None
202 based on that parameter cut off will be triggered, set to None
203 to show full diff
203 to show full diff
204 """
204 """
205 if not isinstance(diff, basestring):
205 if not isinstance(diff, basestring):
206 raise Exception('Diff must be a basestring got %s instead' % type(diff))
206 raise Exception('Diff must be a basestring got %s instead' % type(diff))
207
207
208 self._diff = diff
208 self._diff = diff
209 self._format = format
209 self._format = format
210 self.adds = 0
210 self.adds = 0
211 self.removes = 0
211 self.removes = 0
212 # calculate diff size
212 # calculate diff size
213 self.diff_size = len(diff)
213 self.diff_size = len(diff)
214 self.diff_limit = diff_limit
214 self.diff_limit = diff_limit
215 self.cur_diff_size = 0
215 self.cur_diff_size = 0
216 self.parsed = False
216 self.parsed = False
217 self.parsed_diff = []
217 self.parsed_diff = []
218 self.vcs = vcs
218 self.vcs = vcs
219
219
220 if format == 'gitdiff':
220 if format == 'gitdiff':
221 self.differ = self._highlight_line_difflib
221 self.differ = self._highlight_line_difflib
222 self._parser = self._parse_gitdiff
222 self._parser = self._parse_gitdiff
223 else:
223 else:
224 self.differ = self._highlight_line_udiff
224 self.differ = self._highlight_line_udiff
225 self._parser = self._parse_udiff
225 self._parser = self._parse_udiff
226
226
227 def _copy_iterator(self):
227 def _copy_iterator(self):
228 """
228 """
229 make a fresh copy of generator, we should not iterate thru
229 make a fresh copy of generator, we should not iterate thru
230 an original as it's needed for repeating operations on
230 an original as it's needed for repeating operations on
231 this instance of DiffProcessor
231 this instance of DiffProcessor
232 """
232 """
233 self.__udiff, iterator_copy = tee(self.__udiff)
233 self.__udiff, iterator_copy = tee(self.__udiff)
234 return iterator_copy
234 return iterator_copy
235
235
236 def _escaper(self, string):
236 def _escaper(self, string):
237 """
237 """
238 Escaper for diff escapes special chars and checks the diff limit
238 Escaper for diff escapes special chars and checks the diff limit
239
239
240 :param string:
240 :param string:
241 :type string:
241 :type string:
242 """
242 """
243
243
244 self.cur_diff_size += len(string)
244 self.cur_diff_size += len(string)
245
245
246 # escaper get's iterated on each .next() call and it checks if each
246 # escaper get's iterated on each .next() call and it checks if each
247 # parsed line doesn't exceed the diff limit
247 # parsed line doesn't exceed the diff limit
248 if self.diff_limit is not None and self.cur_diff_size > self.diff_limit:
248 if self.diff_limit is not None and self.cur_diff_size > self.diff_limit:
249 raise DiffLimitExceeded('Diff Limit Exceeded')
249 raise DiffLimitExceeded('Diff Limit Exceeded')
250
250
251 return safe_unicode(string).replace('&', '&amp;')\
251 return safe_unicode(string).replace('&', '&amp;')\
252 .replace('<', '&lt;')\
252 .replace('<', '&lt;')\
253 .replace('>', '&gt;')
253 .replace('>', '&gt;')
254
254
255 def _line_counter(self, l):
255 def _line_counter(self, l):
256 """
256 """
257 Checks each line and bumps total adds/removes for this diff
257 Checks each line and bumps total adds/removes for this diff
258
258
259 :param l:
259 :param l:
260 """
260 """
261 if l.startswith('+') and not l.startswith('+++'):
261 if l.startswith('+') and not l.startswith('+++'):
262 self.adds += 1
262 self.adds += 1
263 elif l.startswith('-') and not l.startswith('---'):
263 elif l.startswith('-') and not l.startswith('---'):
264 self.removes += 1
264 self.removes += 1
265 return safe_unicode(l)
265 return safe_unicode(l)
266
266
267 def _highlight_line_difflib(self, line, next_):
267 def _highlight_line_difflib(self, line, next_):
268 """
268 """
269 Highlight inline changes in both lines.
269 Highlight inline changes in both lines.
270 """
270 """
271
271
272 if line['action'] == 'del':
272 if line['action'] == 'del':
273 old, new = line, next_
273 old, new = line, next_
274 else:
274 else:
275 old, new = next_, line
275 old, new = next_, line
276
276
277 oldwords = re.split(r'(\W)', old['line'])
277 oldwords = re.split(r'(\W)', old['line'])
278 newwords = re.split(r'(\W)', new['line'])
278 newwords = re.split(r'(\W)', new['line'])
279
279
280 sequence = difflib.SequenceMatcher(None, oldwords, newwords)
280 sequence = difflib.SequenceMatcher(None, oldwords, newwords)
281
281
282 oldfragments, newfragments = [], []
282 oldfragments, newfragments = [], []
283 for tag, i1, i2, j1, j2 in sequence.get_opcodes():
283 for tag, i1, i2, j1, j2 in sequence.get_opcodes():
284 oldfrag = ''.join(oldwords[i1:i2])
284 oldfrag = ''.join(oldwords[i1:i2])
285 newfrag = ''.join(newwords[j1:j2])
285 newfrag = ''.join(newwords[j1:j2])
286 if tag != 'equal':
286 if tag != 'equal':
287 if oldfrag:
287 if oldfrag:
288 oldfrag = '<del>%s</del>' % oldfrag
288 oldfrag = '<del>%s</del>' % oldfrag
289 if newfrag:
289 if newfrag:
290 newfrag = '<ins>%s</ins>' % newfrag
290 newfrag = '<ins>%s</ins>' % newfrag
291 oldfragments.append(oldfrag)
291 oldfragments.append(oldfrag)
292 newfragments.append(newfrag)
292 newfragments.append(newfrag)
293
293
294 old['line'] = "".join(oldfragments)
294 old['line'] = "".join(oldfragments)
295 new['line'] = "".join(newfragments)
295 new['line'] = "".join(newfragments)
296
296
297 def _highlight_line_udiff(self, line, next_):
297 def _highlight_line_udiff(self, line, next_):
298 """
298 """
299 Highlight inline changes in both lines.
299 Highlight inline changes in both lines.
300 """
300 """
301 start = 0
301 start = 0
302 limit = min(len(line['line']), len(next_['line']))
302 limit = min(len(line['line']), len(next_['line']))
303 while start < limit and line['line'][start] == next_['line'][start]:
303 while start < limit and line['line'][start] == next_['line'][start]:
304 start += 1
304 start += 1
305 end = -1
305 end = -1
306 limit -= start
306 limit -= start
307 while -end <= limit and line['line'][end] == next_['line'][end]:
307 while -end <= limit and line['line'][end] == next_['line'][end]:
308 end -= 1
308 end -= 1
309 end += 1
309 end += 1
310 if start or end:
310 if start or end:
311 def do(l):
311 def do(l):
312 last = end + len(l['line'])
312 last = end + len(l['line'])
313 if l['action'] == 'add':
313 if l['action'] == 'add':
314 tag = 'ins'
314 tag = 'ins'
315 else:
315 else:
316 tag = 'del'
316 tag = 'del'
317 l['line'] = '%s<%s>%s</%s>%s' % (
317 l['line'] = '%s<%s>%s</%s>%s' % (
318 l['line'][:start],
318 l['line'][:start],
319 tag,
319 tag,
320 l['line'][start:last],
320 l['line'][start:last],
321 tag,
321 tag,
322 l['line'][last:]
322 l['line'][last:]
323 )
323 )
324 do(line)
324 do(line)
325 do(next_)
325 do(next_)
326
326
327 def _get_header(self, diff_chunk):
327 def _get_header(self, diff_chunk):
328 """
328 """
329 parses the diff header, and returns parts, and leftover diff
329 parses the diff header, and returns parts, and leftover diff
330 parts consists of 14 elements::
330 parts consists of 14 elements::
331
331
332 a_path, b_path, similarity_index, rename_from, rename_to,
332 a_path, b_path, similarity_index, rename_from, rename_to,
333 old_mode, new_mode, new_file_mode, deleted_file_mode,
333 old_mode, new_mode, new_file_mode, deleted_file_mode,
334 a_blob_id, b_blob_id, b_mode, a_file, b_file
334 a_blob_id, b_blob_id, b_mode, a_file, b_file
335
335
336 :param diff_chunk:
336 :param diff_chunk:
337 :type diff_chunk:
337 :type diff_chunk:
338 """
338 """
339
339
340 if self.vcs == 'git':
340 if self.vcs == 'git':
341 match = self._git_header_re.match(diff_chunk)
341 match = self._git_header_re.match(diff_chunk)
342 diff = diff_chunk[match.end():]
342 diff = diff_chunk[match.end():]
343 return match.groupdict(), imap(self._escaper, diff.splitlines(1))
343 return match.groupdict(), imap(self._escaper, diff.splitlines(1))
344 elif self.vcs == 'hg':
344 elif self.vcs == 'hg':
345 match = self._hg_header_re.match(diff_chunk)
345 match = self._hg_header_re.match(diff_chunk)
346 diff = diff_chunk[match.end():]
346 diff = diff_chunk[match.end():]
347 return match.groupdict(), imap(self._escaper, diff.splitlines(1))
347 return match.groupdict(), imap(self._escaper, diff.splitlines(1))
348 else:
348 else:
349 raise Exception('VCS type %s is not supported' % self.vcs)
349 raise Exception('VCS type %s is not supported' % self.vcs)
350
350
351 def _clean_line(self, line, command):
351 def _clean_line(self, line, command):
352 if command in ['+', '-', ' ']:
352 if command in ['+', '-', ' ']:
353 #only modify the line if it's actually a diff thing
353 #only modify the line if it's actually a diff thing
354 line = line[1:]
354 line = line[1:]
355 return line
355 return line
356
356
357 def _parse_gitdiff(self, inline_diff=True):
357 def _parse_gitdiff(self, inline_diff=True):
358 _files = []
358 _files = []
359 diff_container = lambda arg: arg
359 diff_container = lambda arg: arg
360
360
361 ##split the diff in chunks of separate --git a/file b/file chunks
361 ##split the diff in chunks of separate --git a/file b/file chunks
362 for raw_diff in ('\n' + self._diff).split('\ndiff --git')[1:]:
362 for raw_diff in ('\n' + self._diff).split('\ndiff --git')[1:]:
363 binary = False
363 binary = False
364 binary_msg = 'unknown binary'
364 binary_msg = 'unknown binary'
365 head, diff = self._get_header(raw_diff)
365 head, diff = self._get_header(raw_diff)
366
366
367 if not head['a_file'] and head['b_file']:
367 if not head['a_file'] and head['b_file']:
368 op = 'A'
368 op = 'A'
369 elif head['a_file'] and head['b_file']:
369 elif head['a_file'] and head['b_file']:
370 op = 'M'
370 op = 'M'
371 elif head['a_file'] and not head['b_file']:
371 elif head['a_file'] and not head['b_file']:
372 op = 'D'
372 op = 'D'
373 else:
373 else:
374 #probably we're dealing with a binary file 1
374 #probably we're dealing with a binary file 1
375 binary = True
375 binary = True
376 if head['deleted_file_mode']:
376 if head['deleted_file_mode']:
377 op = 'D'
377 op = 'D'
378 stats = ['b', DEL_FILENODE]
378 stats = ['b', DEL_FILENODE]
379 binary_msg = 'deleted binary file'
379 binary_msg = 'deleted binary file'
380 elif head['new_file_mode']:
380 elif head['new_file_mode']:
381 op = 'A'
381 op = 'A'
382 stats = ['b', NEW_FILENODE]
382 stats = ['b', NEW_FILENODE]
383 binary_msg = 'new binary file %s' % head['new_file_mode']
383 binary_msg = 'new binary file %s' % head['new_file_mode']
384 else:
384 else:
385 if head['new_mode'] and head['old_mode']:
385 if head['new_mode'] and head['old_mode']:
386 stats = ['b', CHMOD_FILENODE]
386 stats = ['b', CHMOD_FILENODE]
387 op = 'M'
387 op = 'M'
388 binary_msg = ('modified binary file chmod %s => %s'
388 binary_msg = ('modified binary file chmod %s => %s'
389 % (head['old_mode'], head['new_mode']))
389 % (head['old_mode'], head['new_mode']))
390 elif (head['rename_from'] and head['rename_to']
390 elif (head['rename_from'] and head['rename_to']
391 and head['rename_from'] != head['rename_to']):
391 and head['rename_from'] != head['rename_to']):
392 stats = ['b', RENAMED_FILENODE]
392 stats = ['b', RENAMED_FILENODE]
393 op = 'M'
393 op = 'M'
394 binary_msg = ('file renamed from %s to %s'
394 binary_msg = ('file renamed from %s to %s'
395 % (head['rename_from'], head['rename_to']))
395 % (head['rename_from'], head['rename_to']))
396 else:
396 else:
397 stats = ['b', MOD_FILENODE]
397 stats = ['b', MOD_FILENODE]
398 op = 'M'
398 op = 'M'
399 binary_msg = 'modified binary file'
399 binary_msg = 'modified binary file'
400
400
401 if not binary:
401 if not binary:
402 try:
402 try:
403 chunks, stats = self._parse_lines(diff)
403 chunks, stats = self._parse_lines(diff)
404 except DiffLimitExceeded:
404 except DiffLimitExceeded:
405 diff_container = lambda _diff: LimitedDiffContainer(
405 diff_container = lambda _diff: LimitedDiffContainer(
406 self.diff_limit,
406 self.diff_limit,
407 self.cur_diff_size,
407 self.cur_diff_size,
408 _diff)
408 _diff)
409 break
409 break
410 else:
410 else:
411 chunks = []
411 chunks = []
412 chunks.append([{
412 chunks.append([{
413 'old_lineno': '',
413 'old_lineno': '',
414 'new_lineno': '',
414 'new_lineno': '',
415 'action': 'binary',
415 'action': 'binary',
416 'line': binary_msg,
416 'line': binary_msg,
417 }])
417 }])
418
418
419 _files.append({
419 _files.append({
420 'filename': head['b_path'],
420 'filename': head['b_path'],
421 'old_revision': head['a_blob_id'],
421 'old_revision': head['a_blob_id'],
422 'new_revision': head['b_blob_id'],
422 'new_revision': head['b_blob_id'],
423 'chunks': chunks,
423 'chunks': chunks,
424 'operation': op,
424 'operation': op,
425 'stats': stats,
425 'stats': stats,
426 })
426 })
427
427
428 sorter = lambda info: {'A': 0, 'M': 1, 'D': 2}.get(info['operation'])
428 sorter = lambda info: {'A': 0, 'M': 1, 'D': 2}.get(info['operation'])
429
429
430 if inline_diff is False:
430 if inline_diff is False:
431 return diff_container(sorted(_files, key=sorter))
431 return diff_container(sorted(_files, key=sorter))
432
432
433 # highlight inline changes
433 # highlight inline changes
434 for diff_data in _files:
434 for diff_data in _files:
435 for chunk in diff_data['chunks']:
435 for chunk in diff_data['chunks']:
436 lineiter = iter(chunk)
436 lineiter = iter(chunk)
437 try:
437 try:
438 while 1:
438 while 1:
439 line = lineiter.next()
439 line = lineiter.next()
440 if line['action'] not in ['unmod', 'context']:
440 if line['action'] not in ['unmod', 'context']:
441 nextline = lineiter.next()
441 nextline = lineiter.next()
442 if nextline['action'] in ['unmod', 'context'] or \
442 if nextline['action'] in ['unmod', 'context'] or \
443 nextline['action'] == line['action']:
443 nextline['action'] == line['action']:
444 continue
444 continue
445 self.differ(line, nextline)
445 self.differ(line, nextline)
446 except StopIteration:
446 except StopIteration:
447 pass
447 pass
448
448
449 return diff_container(sorted(_files, key=sorter))
449 return diff_container(sorted(_files, key=sorter))
450
450
451 def _parse_udiff(self, inline_diff=True):
451 def _parse_udiff(self, inline_diff=True):
452 raise NotImplementedError()
452 raise NotImplementedError()
453
453
454 def _parse_lines(self, diff):
454 def _parse_lines(self, diff):
455 """
455 """
456 Parse the diff an return data for the template.
456 Parse the diff an return data for the template.
457 """
457 """
458
458
459 lineiter = iter(diff)
459 lineiter = iter(diff)
460 stats = [0, 0]
460 stats = [0, 0]
461
461
462 try:
462 try:
463 chunks = []
463 chunks = []
464 line = lineiter.next()
464 line = lineiter.next()
465
465
466 while line:
466 while line:
467 lines = []
467 lines = []
468 chunks.append(lines)
468 chunks.append(lines)
469
469
470 match = self._chunk_re.match(line)
470 match = self._chunk_re.match(line)
471
471
472 if not match:
472 if not match:
473 break
473 break
474
474
475 gr = match.groups()
475 gr = match.groups()
476 (old_line, old_end,
476 (old_line, old_end,
477 new_line, new_end) = [int(x or 1) for x in gr[:-1]]
477 new_line, new_end) = [int(x or 1) for x in gr[:-1]]
478 old_line -= 1
478 old_line -= 1
479 new_line -= 1
479 new_line -= 1
480
480
481 context = len(gr) == 5
481 context = len(gr) == 5
482 old_end += old_line
482 old_end += old_line
483 new_end += new_line
483 new_end += new_line
484
484
485 if context:
485 if context:
486 # skip context only if it's first line
486 # skip context only if it's first line
487 if int(gr[0]) > 1:
487 if int(gr[0]) > 1:
488 lines.append({
488 lines.append({
489 'old_lineno': '...',
489 'old_lineno': '...',
490 'new_lineno': '...',
490 'new_lineno': '...',
491 'action': 'context',
491 'action': 'context',
492 'line': line,
492 'line': line,
493 })
493 })
494
494
495 line = lineiter.next()
495 line = lineiter.next()
496
496
497 while old_line < old_end or new_line < new_end:
497 while old_line < old_end or new_line < new_end:
498 command = ' '
498 command = ' '
499 if line:
499 if line:
500 command = line[0]
500 command = line[0]
501
501
502 affects_old = affects_new = False
502 affects_old = affects_new = False
503
503
504 # ignore those if we don't expect them
504 # ignore those if we don't expect them
505 if command in '#@':
505 if command in '#@':
506 continue
506 continue
507 elif command == '+':
507 elif command == '+':
508 affects_new = True
508 affects_new = True
509 action = 'add'
509 action = 'add'
510 stats[0] += 1
510 stats[0] += 1
511 elif command == '-':
511 elif command == '-':
512 affects_old = True
512 affects_old = True
513 action = 'del'
513 action = 'del'
514 stats[1] += 1
514 stats[1] += 1
515 else:
515 else:
516 affects_old = affects_new = True
516 affects_old = affects_new = True
517 action = 'unmod'
517 action = 'unmod'
518
518
519 if not self._newline_marker.match(line):
519 if not self._newline_marker.match(line):
520 old_line += affects_old
520 old_line += affects_old
521 new_line += affects_new
521 new_line += affects_new
522 lines.append({
522 lines.append({
523 'old_lineno': affects_old and old_line or '',
523 'old_lineno': affects_old and old_line or '',
524 'new_lineno': affects_new and new_line or '',
524 'new_lineno': affects_new and new_line or '',
525 'action': action,
525 'action': action,
526 'line': self._clean_line(line, command)
526 'line': self._clean_line(line, command)
527 })
527 })
528
528
529 line = lineiter.next()
529 line = lineiter.next()
530
530
531 if self._newline_marker.match(line):
531 if self._newline_marker.match(line):
532 # we need to append to lines, since this is not
532 # we need to append to lines, since this is not
533 # counted in the line specs of diff
533 # counted in the line specs of diff
534 lines.append({
534 lines.append({
535 'old_lineno': '...',
535 'old_lineno': '...',
536 'new_lineno': '...',
536 'new_lineno': '...',
537 'action': 'context',
537 'action': 'context',
538 'line': self._clean_line(line, command)
538 'line': self._clean_line(line, command)
539 })
539 })
540
540
541 except StopIteration:
541 except StopIteration:
542 pass
542 pass
543 return chunks, stats
543 return chunks, stats
544
544
545 def _safe_id(self, idstring):
545 def _safe_id(self, idstring):
546 """Make a string safe for including in an id attribute.
546 """Make a string safe for including in an id attribute.
547
547
548 The HTML spec says that id attributes 'must begin with
548 The HTML spec says that id attributes 'must begin with
549 a letter ([A-Za-z]) and may be followed by any number
549 a letter ([A-Za-z]) and may be followed by any number
550 of letters, digits ([0-9]), hyphens ("-"), underscores
550 of letters, digits ([0-9]), hyphens ("-"), underscores
551 ("_"), colons (":"), and periods (".")'. These regexps
551 ("_"), colons (":"), and periods (".")'. These regexps
552 are slightly over-zealous, in that they remove colons
552 are slightly over-zealous, in that they remove colons
553 and periods unnecessarily.
553 and periods unnecessarily.
554
554
555 Whitespace is transformed into underscores, and then
555 Whitespace is transformed into underscores, and then
556 anything which is not a hyphen or a character that
556 anything which is not a hyphen or a character that
557 matches \w (alphanumerics and underscore) is removed.
557 matches \w (alphanumerics and underscore) is removed.
558
558
559 """
559 """
560 # Transform all whitespace to underscore
560 # Transform all whitespace to underscore
561 idstring = re.sub(r'\s', "_", '%s' % idstring)
561 idstring = re.sub(r'\s', "_", '%s' % idstring)
562 # Remove everything that is not a hyphen or a member of \w
562 # Remove everything that is not a hyphen or a member of \w
563 idstring = re.sub(r'(?!-)\W', "", idstring).lower()
563 idstring = re.sub(r'(?!-)\W', "", idstring).lower()
564 return idstring
564 return idstring
565
565
566 def prepare(self, inline_diff=True):
566 def prepare(self, inline_diff=True):
567 """
567 """
568 Prepare the passed udiff for HTML rendering. It'l return a list
568 Prepare the passed udiff for HTML rendering. It'l return a list
569 of dicts with diff information
569 of dicts with diff information
570 """
570 """
571 parsed = self._parser(inline_diff=inline_diff)
571 parsed = self._parser(inline_diff=inline_diff)
572 self.parsed = True
572 self.parsed = True
573 self.parsed_diff = parsed
573 self.parsed_diff = parsed
574 return parsed
574 return parsed
575
575
576 def as_raw(self, diff_lines=None):
576 def as_raw(self, diff_lines=None):
577 """
577 """
578 Returns raw string diff
578 Returns raw string diff
579 """
579 """
580 return self._diff
580 return self._diff
581 #return u''.join(imap(self._line_counter, self._diff.splitlines(1)))
581 #return u''.join(imap(self._line_counter, self._diff.splitlines(1)))
582
582
583 def as_html(self, table_class='code-difftable', line_class='line',
583 def as_html(self, table_class='code-difftable', line_class='line',
584 new_lineno_class='lineno old', old_lineno_class='lineno new',
584 new_lineno_class='lineno old', old_lineno_class='lineno new',
585 code_class='code', enable_comments=False, parsed_lines=None):
585 code_class='code', enable_comments=False, parsed_lines=None):
586 """
586 """
587 Return given diff as html table with customized css classes
587 Return given diff as html table with customized css classes
588 """
588 """
589 def _link_to_if(condition, label, url):
589 def _link_to_if(condition, label, url):
590 """
590 """
591 Generates a link if condition is meet or just the label if not.
591 Generates a link if condition is meet or just the label if not.
592 """
592 """
593
593
594 if condition:
594 if condition:
595 return '''<a href="%(url)s">%(label)s</a>''' % {
595 return '''<a href="%(url)s">%(label)s</a>''' % {
596 'url': url,
596 'url': url,
597 'label': label
597 'label': label
598 }
598 }
599 else:
599 else:
600 return label
600 return label
601 if not self.parsed:
601 if not self.parsed:
602 self.prepare()
602 self.prepare()
603
603
604 diff_lines = self.parsed_diff
604 diff_lines = self.parsed_diff
605 if parsed_lines:
605 if parsed_lines:
606 diff_lines = parsed_lines
606 diff_lines = parsed_lines
607
607
608 _html_empty = True
608 _html_empty = True
609 _html = []
609 _html = []
610 _html.append('''<table class="%(table_class)s">\n''' % {
610 _html.append('''<table class="%(table_class)s">\n''' % {
611 'table_class': table_class
611 'table_class': table_class
612 })
612 })
613
613
614 for diff in diff_lines:
614 for diff in diff_lines:
615 for line in diff['chunks']:
615 for line in diff['chunks']:
616 _html_empty = False
616 _html_empty = False
617 for change in line:
617 for change in line:
618 _html.append('''<tr class="%(lc)s %(action)s">\n''' % {
618 _html.append('''<tr class="%(lc)s %(action)s">\n''' % {
619 'lc': line_class,
619 'lc': line_class,
620 'action': change['action']
620 'action': change['action']
621 })
621 })
622 anchor_old_id = ''
622 anchor_old_id = ''
623 anchor_new_id = ''
623 anchor_new_id = ''
624 anchor_old = "%(filename)s_o%(oldline_no)s" % {
624 anchor_old = "%(filename)s_o%(oldline_no)s" % {
625 'filename': self._safe_id(diff['filename']),
625 'filename': self._safe_id(diff['filename']),
626 'oldline_no': change['old_lineno']
626 'oldline_no': change['old_lineno']
627 }
627 }
628 anchor_new = "%(filename)s_n%(oldline_no)s" % {
628 anchor_new = "%(filename)s_n%(oldline_no)s" % {
629 'filename': self._safe_id(diff['filename']),
629 'filename': self._safe_id(diff['filename']),
630 'oldline_no': change['new_lineno']
630 'oldline_no': change['new_lineno']
631 }
631 }
632 cond_old = (change['old_lineno'] != '...' and
632 cond_old = (change['old_lineno'] != '...' and
633 change['old_lineno'])
633 change['old_lineno'])
634 cond_new = (change['new_lineno'] != '...' and
634 cond_new = (change['new_lineno'] != '...' and
635 change['new_lineno'])
635 change['new_lineno'])
636 if cond_old:
636 if cond_old:
637 anchor_old_id = 'id="%s"' % anchor_old
637 anchor_old_id = 'id="%s"' % anchor_old
638 if cond_new:
638 if cond_new:
639 anchor_new_id = 'id="%s"' % anchor_new
639 anchor_new_id = 'id="%s"' % anchor_new
640 ###########################################################
640 ###########################################################
641 # OLD LINE NUMBER
641 # OLD LINE NUMBER
642 ###########################################################
642 ###########################################################
643 _html.append('''\t<td %(a_id)s class="%(olc)s">''' % {
643 _html.append('''\t<td %(a_id)s class="%(olc)s">''' % {
644 'a_id': anchor_old_id,
644 'a_id': anchor_old_id,
645 'olc': old_lineno_class
645 'olc': old_lineno_class
646 })
646 })
647
647
648 _html.append('''%(link)s''' % {
648 _html.append('''%(link)s''' % {
649 'link': _link_to_if(True, change['old_lineno'],
649 'link': _link_to_if(True, change['old_lineno'],
650 '#%s' % anchor_old)
650 '#%s' % anchor_old)
651 })
651 })
652 _html.append('''</td>\n''')
652 _html.append('''</td>\n''')
653 ###########################################################
653 ###########################################################
654 # NEW LINE NUMBER
654 # NEW LINE NUMBER
655 ###########################################################
655 ###########################################################
656
656
657 _html.append('''\t<td %(a_id)s class="%(nlc)s">''' % {
657 _html.append('''\t<td %(a_id)s class="%(nlc)s">''' % {
658 'a_id': anchor_new_id,
658 'a_id': anchor_new_id,
659 'nlc': new_lineno_class
659 'nlc': new_lineno_class
660 })
660 })
661
661
662 _html.append('''%(link)s''' % {
662 _html.append('''%(link)s''' % {
663 'link': _link_to_if(True, change['new_lineno'],
663 'link': _link_to_if(True, change['new_lineno'],
664 '#%s' % anchor_new)
664 '#%s' % anchor_new)
665 })
665 })
666 _html.append('''</td>\n''')
666 _html.append('''</td>\n''')
667 ###########################################################
667 ###########################################################
668 # CODE
668 # CODE
669 ###########################################################
669 ###########################################################
670 comments = '' if enable_comments else 'no-comment'
670 comments = '' if enable_comments else 'no-comment'
671 _html.append('''\t<td class="%(cc)s %(inc)s">''' % {
671 _html.append('''\t<td class="%(cc)s %(inc)s">''' % {
672 'cc': code_class,
672 'cc': code_class,
673 'inc': comments
673 'inc': comments
674 })
674 })
675 _html.append('''\n\t\t<pre>%(code)s</pre>\n''' % {
675 _html.append('''\n\t\t<pre>%(code)s</pre>\n''' % {
676 'code': change['line']
676 'code': change['line']
677 })
677 })
678
678
679 _html.append('''\t</td>''')
679 _html.append('''\t</td>''')
680 _html.append('''\n</tr>\n''')
680 _html.append('''\n</tr>\n''')
681 _html.append('''</table>''')
681 _html.append('''</table>''')
682 if _html_empty:
682 if _html_empty:
683 return None
683 return None
684 return ''.join(_html)
684 return ''.join(_html)
685
685
686 def stat(self):
686 def stat(self):
687 """
687 """
688 Returns tuple of added, and removed lines for this instance
688 Returns tuple of added, and removed lines for this instance
689 """
689 """
690 return self.adds, self.removes
690 return self.adds, self.removes
691
691
692
692
693 class InMemoryBundleRepo(bundlerepository):
693 class InMemoryBundleRepo(bundlerepository):
694 def __init__(self, ui, path, bundlestream):
694 def __init__(self, ui, path, bundlestream):
695 self._tempparent = None
695 self._tempparent = None
696 localrepo.localrepository.__init__(self, ui, path)
696 localrepo.localrepository.__init__(self, ui, path)
697 self.ui.setconfig('phases', 'publish', False)
697 self.ui.setconfig('phases', 'publish', False)
698
698
699 self.bundle = bundlestream
699 self.bundle = bundlestream
700
700
701 # dict with the mapping 'filename' -> position in the bundle
701 # dict with the mapping 'filename' -> position in the bundle
702 self.bundlefilespos = {}
702 self.bundlefilespos = {}
703
703
704
704
705 def differ(org_repo, org_ref, other_repo, other_ref, discovery_data=None,
705 def differ(org_repo, org_ref, other_repo, other_ref, discovery_data=None,
706 remote_compare=False, context=3, ignore_whitespace=False):
706 remote_compare=False, context=3, ignore_whitespace=False):
707 """
707 """
708 General differ between branches, bookmarks, revisions of two remote or
708 General differ between branches, bookmarks, revisions of two remote or
709 local but related repositories
709 local but related repositories
710
710
711 :param org_repo:
711 :param org_repo:
712 :param org_ref:
712 :param org_ref:
713 :param other_repo:
713 :param other_repo:
714 :type other_repo:
714 :type other_repo:
715 :type other_ref:
715 :type other_ref:
716 """
716 """
717
717
718 org_repo_scm = org_repo.scm_instance
718 org_repo_scm = org_repo.scm_instance
719 other_repo_scm = other_repo.scm_instance
719 other_repo_scm = other_repo.scm_instance
720
720
721 org_repo = org_repo_scm._repo
721 org_repo = org_repo_scm._repo
722 other_repo = other_repo_scm._repo
722 other_repo = other_repo_scm._repo
723
723
724 org_ref = org_ref[1]
724 org_ref = org_ref[1]
725 other_ref = other_ref[1]
725 other_ref = other_ref[1]
726
726
727 if org_repo == other_repo:
727 if org_repo == other_repo:
728 log.debug('running diff between %s@%s and %s@%s'
728 log.debug('running diff between %s@%s and %s@%s'
729 % (org_repo.path, org_ref, other_repo.path, other_ref))
729 % (org_repo.path, org_ref, other_repo.path, other_ref))
730 _diff = org_repo_scm.get_diff(rev1=org_ref, rev2=other_ref,
730 _diff = org_repo_scm.get_diff(rev1=org_ref, rev2=other_ref,
731 ignore_whitespace=ignore_whitespace, context=context)
731 ignore_whitespace=ignore_whitespace, context=context)
732 return _diff
732 return _diff
733
733
734 elif remote_compare:
734 elif remote_compare:
735 opts = diffopts(git=True, ignorews=ignore_whitespace, context=context)
735 opts = diffopts(git=True, ignorews=ignore_whitespace, context=context)
736 common, incoming, rheads = discovery_data
736 common, incoming, rheads = discovery_data
737 org_repo_peer = localrepo.locallegacypeer(org_repo.local())
737 org_repo_peer = localrepo.locallegacypeer(org_repo.local())
738 # create a bundle (uncompressed if other repo is not local)
738 # create a bundle (uncompressed if other repo is not local)
739 if org_repo_peer.capable('getbundle'):
739 if org_repo_peer.capable('getbundle'):
740 # disable repo hooks here since it's just bundle !
740 # disable repo hooks here since it's just bundle !
741 # patch and reset hooks section of UI config to not run any
741 # patch and reset hooks section of UI config to not run any
742 # hooks on fetching archives with subrepos
742 # hooks on fetching archives with subrepos
743 for k, _ in org_repo.ui.configitems('hooks'):
743 for k, _ in org_repo.ui.configitems('hooks'):
744 org_repo.ui.setconfig('hooks', k, None)
744 org_repo.ui.setconfig('hooks', k, None)
745 unbundle = org_repo.getbundle('incoming', common=None,
745 unbundle = org_repo.getbundle('incoming', common=None,
746 heads=None)
746 heads=None)
747
747
748 buf = BytesIO()
748 buf = BytesIO()
749 while True:
749 while True:
750 chunk = unbundle._stream.read(1024 * 4)
750 chunk = unbundle._stream.read(1024 * 4)
751 if not chunk:
751 if not chunk:
752 break
752 break
753 buf.write(chunk)
753 buf.write(chunk)
754
754
755 buf.seek(0)
755 buf.seek(0)
756 # replace chunked _stream with data that can do tell() and seek()
756 # replace chunked _stream with data that can do tell() and seek()
757 unbundle._stream = buf
757 unbundle._stream = buf
758
758
759 ui = make_ui('db')
759 ui = make_ui('db')
760 bundlerepo = InMemoryBundleRepo(ui, path=org_repo.root,
760 bundlerepo = InMemoryBundleRepo(ui, path=org_repo.root,
761 bundlestream=unbundle)
761 bundlestream=unbundle)
762
762
763 return ''.join(patch.diff(bundlerepo,
763 return ''.join(patch.diff(bundlerepo,
764 node1=other_repo[other_ref].node(),
764 node1=other_repo[other_ref].node(),
765 node2=org_repo[org_ref].node(),
765 node2=org_repo[org_ref].node(),
766 opts=opts))
766 opts=opts))
767
767
768 return '' No newline at end of file
768 return ''
@@ -1,122 +1,122 b''
1 import datetime
1 import datetime
2 import functools
2 import functools
3 import decimal
3 import decimal
4 import imp
4 import imp
5
5
6 __all__ = ['json', 'simplejson', 'stdlibjson']
6 __all__ = ['json', 'simplejson', 'stdlibjson']
7
7
8
8
9 def _is_aware(value):
9 def _is_aware(value):
10 """
10 """
11 Determines if a given datetime.time is aware.
11 Determines if a given datetime.time is aware.
12
12
13 The logic is described in Python's docs:
13 The logic is described in Python's docs:
14 http://docs.python.org/library/datetime.html#datetime.tzinfo
14 http://docs.python.org/library/datetime.html#datetime.tzinfo
15 """
15 """
16 return (value.tzinfo is not None
16 return (value.tzinfo is not None
17 and value.tzinfo.utcoffset(value) is not None)
17 and value.tzinfo.utcoffset(value) is not None)
18
18
19
19
20 def _obj_dump(obj):
20 def _obj_dump(obj):
21 """
21 """
22 Custom function for dumping objects to JSON, if obj has __json__ attribute
22 Custom function for dumping objects to JSON, if obj has __json__ attribute
23 or method defined it will be used for serialization
23 or method defined it will be used for serialization
24
24
25 :param obj:
25 :param obj:
26 """
26 """
27
27
28 if isinstance(obj, complex):
28 if isinstance(obj, complex):
29 return [obj.real, obj.imag]
29 return [obj.real, obj.imag]
30 # See "Date Time String Format" in the ECMA-262 specification.
30 # See "Date Time String Format" in the ECMA-262 specification.
31 # some code borrowed from django 1.4
31 # some code borrowed from django 1.4
32 elif isinstance(obj, datetime.datetime):
32 elif isinstance(obj, datetime.datetime):
33 r = obj.isoformat()
33 r = obj.isoformat()
34 if obj.microsecond:
34 if obj.microsecond:
35 r = r[:23] + r[26:]
35 r = r[:23] + r[26:]
36 if r.endswith('+00:00'):
36 if r.endswith('+00:00'):
37 r = r[:-6] + 'Z'
37 r = r[:-6] + 'Z'
38 return r
38 return r
39 elif isinstance(obj, datetime.date):
39 elif isinstance(obj, datetime.date):
40 return obj.isoformat()
40 return obj.isoformat()
41 elif isinstance(obj, decimal.Decimal):
41 elif isinstance(obj, decimal.Decimal):
42 return str(obj)
42 return str(obj)
43 elif isinstance(obj, datetime.time):
43 elif isinstance(obj, datetime.time):
44 if _is_aware(obj):
44 if _is_aware(obj):
45 raise ValueError("JSON can't represent timezone-aware times.")
45 raise ValueError("JSON can't represent timezone-aware times.")
46 r = obj.isoformat()
46 r = obj.isoformat()
47 if obj.microsecond:
47 if obj.microsecond:
48 r = r[:12]
48 r = r[:12]
49 return r
49 return r
50 elif isinstance(obj, set):
50 elif isinstance(obj, set):
51 return list(obj)
51 return list(obj)
52 elif hasattr(obj, '__json__'):
52 elif hasattr(obj, '__json__'):
53 if callable(obj.__json__):
53 if callable(obj.__json__):
54 return obj.__json__()
54 return obj.__json__()
55 else:
55 else:
56 return obj.__json__
56 return obj.__json__
57 else:
57 else:
58 raise NotImplementedError
58 raise NotImplementedError
59
59
60
60
61 # Import simplejson
61 # Import simplejson
62 try:
62 try:
63 # import simplejson initially
63 # import simplejson initially
64 _sj = imp.load_module('_sj', *imp.find_module('simplejson'))
64 _sj = imp.load_module('_sj', *imp.find_module('simplejson'))
65
65
66 def extended_encode(obj):
66 def extended_encode(obj):
67 try:
67 try:
68 return _obj_dump(obj)
68 return _obj_dump(obj)
69 except NotImplementedError:
69 except NotImplementedError:
70 pass
70 pass
71 raise TypeError("%r is not JSON serializable" % (obj,))
71 raise TypeError("%r is not JSON serializable" % (obj,))
72 # we handle decimals our own it makes unified behavior of json vs
72 # we handle decimals our own it makes unified behavior of json vs
73 # simplejson
73 # simplejson
74 sj_version = [int(x) for x in _sj.__version__.split('.')]
74 sj_version = [int(x) for x in _sj.__version__.split('.')]
75 major, minor = sj_version[0], sj_version[1]
75 major, minor = sj_version[0], sj_version[1]
76 if major < 2 or (major == 2 and minor < 1):
76 if major < 2 or (major == 2 and minor < 1):
77 # simplejson < 2.1 doesnt support use_decimal
77 # simplejson < 2.1 doesnt support use_decimal
78 _sj.dumps = functools.partial(_sj.dumps,
78 _sj.dumps = functools.partial(_sj.dumps,
79 default=extended_encode)
79 default=extended_encode)
80 _sj.dump = functools.partial(_sj.dump,
80 _sj.dump = functools.partial(_sj.dump,
81 default=extended_encode)
81 default=extended_encode)
82 else:
82 else:
83 _sj.dumps = functools.partial(_sj.dumps,
83 _sj.dumps = functools.partial(_sj.dumps,
84 default=extended_encode,
84 default=extended_encode,
85 use_decimal=False)
85 use_decimal=False)
86 _sj.dump = functools.partial(_sj.dump,
86 _sj.dump = functools.partial(_sj.dump,
87 default=extended_encode,
87 default=extended_encode,
88 use_decimal=False)
88 use_decimal=False)
89 simplejson = _sj
89 simplejson = _sj
90
90
91 except ImportError:
91 except ImportError:
92 # no simplejson set it to None
92 # no simplejson set it to None
93 simplejson = None
93 simplejson = None
94
94
95
95
96 try:
96 try:
97 # simplejson not found try out regular json module
97 # simplejson not found try out regular json module
98 _json = imp.load_module('_json', *imp.find_module('json'))
98 _json = imp.load_module('_json', *imp.find_module('json'))
99
99
100 # extended JSON encoder for json
100 # extended JSON encoder for json
101 class ExtendedEncoder(_json.JSONEncoder):
101 class ExtendedEncoder(_json.JSONEncoder):
102 def default(self, obj):
102 def default(self, obj):
103 try:
103 try:
104 return _obj_dump(obj)
104 return _obj_dump(obj)
105 except NotImplementedError:
105 except NotImplementedError:
106 pass
106 pass
107 raise TypeError("%r is not JSON serializable" % (obj,))
107 raise TypeError("%r is not JSON serializable" % (obj,))
108 # monkey-patch JSON encoder to use extended version
108 # monkey-patch JSON encoder to use extended version
109 _json.dumps = functools.partial(_json.dumps, cls=ExtendedEncoder)
109 _json.dumps = functools.partial(_json.dumps, cls=ExtendedEncoder)
110 _json.dump = functools.partial(_json.dump, cls=ExtendedEncoder)
110 _json.dump = functools.partial(_json.dump, cls=ExtendedEncoder)
111
111
112 stdlibjson = _json
112 stdlibjson = _json
113 except ImportError:
113 except ImportError:
114 stdlibjson = None
114 stdlibjson = None
115
115
116 # set all available json modules
116 # set all available json modules
117 if simplejson:
117 if simplejson:
118 json = _sj
118 json = _sj
119 elif stdlibjson:
119 elif stdlibjson:
120 json = _json
120 json = _json
121 else:
121 else:
122 raise ImportError('Could not find any json modules') No newline at end of file
122 raise ImportError('Could not find any json modules')
@@ -1,291 +1,291 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 <table>
199 <table>
200 <tr>
200 <tr>
201 <th>${_('Prefix')}</th>
201 <th>${_('Prefix')}</th>
202 <th>${_('Key')}</th>
202 <th>${_('Key')}</th>
203 <th>${_('Active')}</th>
203 <th>${_('Active')}</th>
204 </tr>
204 </tr>
205 %for cache in c.repo_info.cache_keys:
205 %for cache in c.repo_info.cache_keys:
206 <tr>
206 <tr>
207 <td>${cache.prefix or '-'}</td>
207 <td>${cache.prefix or '-'}</td>
208 <td>${cache.cache_key}</td>
208 <td>${cache.cache_key}</td>
209 <td>${h.bool2icon(cache.cache_active)}</td>
209 <td>${h.bool2icon(cache.cache_active)}</td>
210 </tr>
210 </tr>
211 %endfor
211 %endfor
212 </table>
212 </table>
213 </div>
213 </div>
214 </div>
214 </div>
215 </div>
215 </div>
216 ${h.end_form()}
216 ${h.end_form()}
217
217
218 <h3>${_('Public journal')}</h3>
218 <h3>${_('Public journal')}</h3>
219 ${h.form(url('repo_public_journal', repo_name=c.repo_info.repo_name),method='put')}
219 ${h.form(url('repo_public_journal', repo_name=c.repo_info.repo_name),method='put')}
220 <div class="form">
220 <div class="form">
221 ${h.hidden('auth_token',str(h.get_token()))}
221 ${h.hidden('auth_token',str(h.get_token()))}
222 <div class="field">
222 <div class="field">
223 %if c.in_public_journal:
223 %if c.in_public_journal:
224 ${h.submit('set_public_%s' % c.repo_info.repo_name,_('Remove from public journal'),class_="ui-btn")}
224 ${h.submit('set_public_%s' % c.repo_info.repo_name,_('Remove from public journal'),class_="ui-btn")}
225 %else:
225 %else:
226 ${h.submit('set_public_%s' % c.repo_info.repo_name,_('Add to public journal'),class_="ui-btn")}
226 ${h.submit('set_public_%s' % c.repo_info.repo_name,_('Add to public journal'),class_="ui-btn")}
227 %endif
227 %endif
228 </div>
228 </div>
229 <div class="field" style="border:none;color:#888">
229 <div class="field" style="border:none;color:#888">
230 <ul>
230 <ul>
231 <li>${_('All actions made on this repository will be accessible to everyone in public journal')}
231 <li>${_('All actions made on this repository will be accessible to everyone in public journal')}
232 </li>
232 </li>
233 </ul>
233 </ul>
234 </div>
234 </div>
235 </div>
235 </div>
236 ${h.end_form()}
236 ${h.end_form()}
237
237
238 <h3>${_('Locking')}</h3>
238 <h3>${_('Locking')}</h3>
239 ${h.form(url('repo_locking', repo_name=c.repo_info.repo_name),method='put')}
239 ${h.form(url('repo_locking', repo_name=c.repo_info.repo_name),method='put')}
240 <div class="form">
240 <div class="form">
241 <div class="fields">
241 <div class="fields">
242 %if c.repo_info.locked[0]:
242 %if c.repo_info.locked[0]:
243 ${h.submit('set_unlock' ,_('Unlock locked repo'),class_="ui-btn",onclick="return confirm('"+_('Confirm to unlock repository')+"');")}
243 ${h.submit('set_unlock' ,_('Unlock locked repo'),class_="ui-btn",onclick="return confirm('"+_('Confirm to unlock repository')+"');")}
244 ${'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])))}
244 ${'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])))}
245 %else:
245 %else:
246 ${h.submit('set_lock',_('lock repo'),class_="ui-btn",onclick="return confirm('"+_('Confirm to lock repository')+"');")}
246 ${h.submit('set_lock',_('lock repo'),class_="ui-btn",onclick="return confirm('"+_('Confirm to lock repository')+"');")}
247 ${_('Repository is not locked')}
247 ${_('Repository is not locked')}
248 %endif
248 %endif
249 </div>
249 </div>
250 <div class="field" style="border:none;color:#888">
250 <div class="field" style="border:none;color:#888">
251 <ul>
251 <ul>
252 <li>${_('Force locking on repository. Works only when anonymous access is disabled')}
252 <li>${_('Force locking on repository. Works only when anonymous access is disabled')}
253 </li>
253 </li>
254 </ul>
254 </ul>
255 </div>
255 </div>
256 </div>
256 </div>
257 ${h.end_form()}
257 ${h.end_form()}
258
258
259 <h3>${_('Set as fork of')}</h3>
259 <h3>${_('Set as fork of')}</h3>
260 ${h.form(url('repo_as_fork', repo_name=c.repo_info.repo_name),method='put')}
260 ${h.form(url('repo_as_fork', repo_name=c.repo_info.repo_name),method='put')}
261 <div class="form">
261 <div class="form">
262 <div class="fields">
262 <div class="fields">
263 ${h.select('id_fork_of','',c.repos_list,class_="medium")}
263 ${h.select('id_fork_of','',c.repos_list,class_="medium")}
264 ${h.submit('set_as_fork_%s' % c.repo_info.repo_name,_('set'),class_="ui-btn",)}
264 ${h.submit('set_as_fork_%s' % c.repo_info.repo_name,_('set'),class_="ui-btn",)}
265 </div>
265 </div>
266 <div class="field" style="border:none;color:#888">
266 <div class="field" style="border:none;color:#888">
267 <ul>
267 <ul>
268 <li>${_('''Manually set this repository as a fork of another from the list''')}</li>
268 <li>${_('''Manually set this repository as a fork of another from the list''')}</li>
269 </ul>
269 </ul>
270 </div>
270 </div>
271 </div>
271 </div>
272 ${h.end_form()}
272 ${h.end_form()}
273
273
274 <h3>${_('Delete')}</h3>
274 <h3>${_('Delete')}</h3>
275 ${h.form(url('repo', repo_name=c.repo_info.repo_name),method='delete')}
275 ${h.form(url('repo', repo_name=c.repo_info.repo_name),method='delete')}
276 <div class="form">
276 <div class="form">
277 <div class="fields">
277 <div class="fields">
278 ${h.submit('remove_%s' % c.repo_info.repo_name,_('Remove this repository'),class_="ui-btn red",onclick="return confirm('"+_('Confirm to delete this repository')+"');")}
278 ${h.submit('remove_%s' % c.repo_info.repo_name,_('Remove this repository'),class_="ui-btn red",onclick="return confirm('"+_('Confirm to delete this repository')+"');")}
279 </div>
279 </div>
280 <div class="field" style="border:none;color:#888">
280 <div class="field" style="border:none;color:#888">
281 <ul>
281 <ul>
282 <li>${_('''This repository will be renamed in a special way in order to be unaccesible for RhodeCode and VCS systems.
282 <li>${_('''This repository will be renamed in a special way in order to be unaccesible for RhodeCode and VCS systems.
283 If you need fully delete it from filesystem please do it manually''')}
283 If you need fully delete it from filesystem please do it manually''')}
284 </li>
284 </li>
285 </ul>
285 </ul>
286 </div>
286 </div>
287 </div>
287 </div>
288 ${h.end_form()}
288 ${h.end_form()}
289 </div>
289 </div>
290
290
291 </%def>
291 </%def>
@@ -1,228 +1,228 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
148
149 <div class="group_members_wrap">
149 <div class="group_members_wrap">
150 % if c.group_members_obj:
150 % if c.group_members_obj:
151 <ul class="group_members">
151 <ul class="group_members">
152 %for user in c.group_members_obj:
152 %for user in c.group_members_obj:
153 <li>
153 <li>
154 <div class="group_member">
154 <div class="group_member">
155 <div class="gravatar"><img alt="gravatar" src="${h.gravatar_url(user.email,24)}"/> </div>
155 <div class="gravatar"><img alt="gravatar" src="${h.gravatar_url(user.email,24)}"/> </div>
156 <div>${h.link_to(user.username, h.url('edit_user',id=user.user_id))}</div>
156 <div>${h.link_to(user.username, h.url('edit_user',id=user.user_id))}</div>
157 <div>${user.full_name}</div>
157 <div>${user.full_name}</div>
158 </div>
158 </div>
159 </li>
159 </li>
160 %endfor
160 %endfor
161 </ul>
161 </ul>
162 %else:
162 %else:
163 <span class="empty_data">${_('No members yet')}</span>
163 <span class="empty_data">${_('No members yet')}</span>
164 %endif
164 %endif
165 </div>
165 </div>
166 </div>
166 </div>
167
167
168 <div class="box box-left">
168 <div class="box box-left">
169 <!-- box / title -->
169 <!-- box / title -->
170 <div class="title">
170 <div class="title">
171 <h5>${_('Permissions defined for this group')}</h5>
171 <h5>${_('Permissions defined for this group')}</h5>
172 </div>
172 </div>
173 ## permissions overview
173 ## permissions overview
174 <div id="perms" class="table">
174 <div id="perms" class="table">
175 %for section in sorted(c.users_group.permissions.keys()):
175 %for section in sorted(c.users_group.permissions.keys()):
176 <div class="perms_section_head">${section.replace("_"," ").capitalize()}</div>
176 <div class="perms_section_head">${section.replace("_"," ").capitalize()}</div>
177 %if not c.users_group.permissions:
177 %if not c.users_group.permissions:
178 <span class="empty_data">${_('No permissions set yet')}</span>
178 <span class="empty_data">${_('No permissions set yet')}</span>
179 %else:
179 %else:
180 <div id='tbl_list_wrap_${section}' class="yui-skin-sam">
180 <div id='tbl_list_wrap_${section}' class="yui-skin-sam">
181 <table id="tbl_list_repository">
181 <table id="tbl_list_repository">
182 <thead>
182 <thead>
183 <tr>
183 <tr>
184 <th class="left">${_('Name')}</th>
184 <th class="left">${_('Name')}</th>
185 <th class="left">${_('Permission')}</th>
185 <th class="left">${_('Permission')}</th>
186 <th class="left">${_('Edit Permission')}</th>
186 <th class="left">${_('Edit Permission')}</th>
187 </thead>
187 </thead>
188 <tbody>
188 <tbody>
189 %for k in c.users_group.permissions[section]:
189 %for k in c.users_group.permissions[section]:
190 <%
190 <%
191 section_perm = c.users_group.permissions[section].get(k)
191 section_perm = c.users_group.permissions[section].get(k)
192 _perm = section_perm.split('.')[-1]
192 _perm = section_perm.split('.')[-1]
193 %>
193 %>
194 <tr>
194 <tr>
195 <td>
195 <td>
196 %if section == 'repositories':
196 %if section == 'repositories':
197 <a href="${h.url('summary_home',repo_name=k)}">${k}</a>
197 <a href="${h.url('summary_home',repo_name=k)}">${k}</a>
198 %elif section == 'repositories_groups':
198 %elif section == 'repositories_groups':
199 <a href="${h.url('repos_group_home',group_name=k)}">${k}</a>
199 <a href="${h.url('repos_group_home',group_name=k)}">${k}</a>
200 %endif
200 %endif
201 </td>
201 </td>
202 <td>
202 <td>
203 <span class="perm_tag ${_perm}">${section_perm}</span>
203 <span class="perm_tag ${_perm}">${section_perm}</span>
204 </td>
204 </td>
205 <td>
205 <td>
206 %if section == 'repositories':
206 %if section == 'repositories':
207 <a href="${h.url('edit_repo',repo_name=k,anchor='permissions_manage')}">${_('edit')}</a>
207 <a href="${h.url('edit_repo',repo_name=k,anchor='permissions_manage')}">${_('edit')}</a>
208 %elif section == 'repositories_groups':
208 %elif section == 'repositories_groups':
209 <a href="${h.url('edit_repos_group',id=k,anchor='permissions_manage')}">${_('edit')}</a>
209 <a href="${h.url('edit_repos_group',id=k,anchor='permissions_manage')}">${_('edit')}</a>
210 %else:
210 %else:
211 --
211 --
212 %endif
212 %endif
213 </td>
213 </td>
214 </tr>
214 </tr>
215 %endfor
215 %endfor
216 </tbody>
216 </tbody>
217 </table>
217 </table>
218 </div>
218 </div>
219 %endif
219 %endif
220 %endfor
220 %endfor
221 </div>
221 </div>
222 </div>
222 </div>
223
223
224
224
225 <script type="text/javascript">
225 <script type="text/javascript">
226 MultiSelectWidget('users_group_members','available_members','edit_users_group');
226 MultiSelectWidget('users_group_members','available_members','edit_users_group');
227 </script>
227 </script>
228 </%def>
228 </%def>
@@ -1,305 +1,305 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2
2
3 <%inherit file="/base/base.html"/>
3 <%inherit file="/base/base.html"/>
4
4
5 <%def name="title()">
5 <%def name="title()">
6 ${_('%s Changelog') % c.repo_name} - ${c.rhodecode_name}
6 ${_('%s Changelog') % c.repo_name} - ${c.rhodecode_name}
7 </%def>
7 </%def>
8
8
9 <%def name="breadcrumbs_links()">
9 <%def name="breadcrumbs_links()">
10 ${h.link_to(_(u'Home'),h.url('/'))}
10 ${h.link_to(_(u'Home'),h.url('/'))}
11 &raquo;
11 &raquo;
12 ${h.link_to(c.repo_name,h.url('summary_home',repo_name=c.repo_name))}
12 ${h.link_to(c.repo_name,h.url('summary_home',repo_name=c.repo_name))}
13 &raquo;
13 &raquo;
14 <% size = c.size if c.size <= c.total_cs else c.total_cs %>
14 <% size = c.size if c.size <= c.total_cs else c.total_cs %>
15 ${_('Changelog')} - ${ungettext('showing %d out of %d revision', 'showing %d out of %d revisions', size) % (size, c.total_cs)}
15 ${_('Changelog')} - ${ungettext('showing %d out of %d revision', 'showing %d out of %d revisions', size) % (size, c.total_cs)}
16 </%def>
16 </%def>
17
17
18 <%def name="page_nav()">
18 <%def name="page_nav()">
19 ${self.menu('changelog')}
19 ${self.menu('changelog')}
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 </div>
27 </div>
28 <div class="table">
28 <div class="table">
29 % if c.pagination:
29 % if c.pagination:
30 <div id="graph">
30 <div id="graph">
31 <div id="graph_nodes">
31 <div id="graph_nodes">
32 <canvas id="graph_canvas"></canvas>
32 <canvas id="graph_canvas"></canvas>
33 </div>
33 </div>
34 <div id="graph_content">
34 <div id="graph_content">
35 <div class="info_box" style="clear: both;padding: 10px 6px;vertical-align: right;text-align: right;">
35 <div class="info_box" style="clear: both;padding: 10px 6px;vertical-align: right;text-align: right;">
36 <a href="#" class="ui-btn small" id="rev_range_container" style="display:none"></a>
36 <a href="#" class="ui-btn small" id="rev_range_container" style="display:none"></a>
37 <a href="#" class="ui-btn small" id="rev_range_clear" style="display:none">${_('Clear selection')}</a>
37 <a href="#" class="ui-btn small" id="rev_range_clear" style="display:none">${_('Clear selection')}</a>
38
38
39 %if c.rhodecode_db_repo.fork:
39 %if c.rhodecode_db_repo.fork:
40 <a title="${_('compare fork with %s' % c.rhodecode_db_repo.fork.repo_name)}" href="${h.url('compare_url',repo_name=c.repo_name,org_ref_type='branch',org_ref=request.GET.get('branch') or 'default',other_ref_type='branch',other_ref='default',repo=c.rhodecode_db_repo.fork.repo_name)}" class="ui-btn small">${_('Compare fork with parent')}</a>
40 <a title="${_('compare fork with %s' % c.rhodecode_db_repo.fork.repo_name)}" href="${h.url('compare_url',repo_name=c.repo_name,org_ref_type='branch',org_ref=request.GET.get('branch') or 'default',other_ref_type='branch',other_ref='default',repo=c.rhodecode_db_repo.fork.repo_name)}" class="ui-btn small">${_('Compare fork with parent')}</a>
41 %endif
41 %endif
42 %if h.is_hg(c.rhodecode_repo):
42 %if h.is_hg(c.rhodecode_repo):
43 <a id="open_new_pr" href="${h.url('pullrequest_home',repo_name=c.repo_name)}" class="ui-btn small">${_('Open new pull request')}</a>
43 <a id="open_new_pr" href="${h.url('pullrequest_home',repo_name=c.repo_name)}" class="ui-btn small">${_('Open new pull request')}</a>
44 %endif
44 %endif
45 </div>
45 </div>
46 <div class="container_header">
46 <div class="container_header">
47 ${h.form(h.url.current(),method='get')}
47 ${h.form(h.url.current(),method='get')}
48 <div class="info_box" style="float:left">
48 <div class="info_box" style="float:left">
49 ${h.submit('set',_('Show'),class_="ui-btn")}
49 ${h.submit('set',_('Show'),class_="ui-btn")}
50 ${h.text('size',size=1,value=c.size)}
50 ${h.text('size',size=1,value=c.size)}
51 ${_('revisions')}
51 ${_('revisions')}
52 </div>
52 </div>
53 ${h.end_form()}
53 ${h.end_form()}
54 <div style="float:right">${h.select('branch_filter',c.branch_name,c.branch_filters)}</div>
54 <div style="float:right">${h.select('branch_filter',c.branch_name,c.branch_filters)}</div>
55 </div>
55 </div>
56
56
57 %for cnt,cs in enumerate(c.pagination):
57 %for cnt,cs in enumerate(c.pagination):
58 <div id="chg_${cnt+1}" class="container ${'tablerow%s' % (cnt%2)}">
58 <div id="chg_${cnt+1}" class="container ${'tablerow%s' % (cnt%2)}">
59 <div class="left">
59 <div class="left">
60 <div>
60 <div>
61 ${h.checkbox(cs.raw_id,class_="changeset_range")}
61 ${h.checkbox(cs.raw_id,class_="changeset_range")}
62 <span class="tooltip" title="${h.tooltip(h.age(cs.date))}"><a href="${h.url('changeset_home',repo_name=c.repo_name,revision=cs.raw_id)}"><span class="changeset_id">${cs.revision}:<span class="changeset_hash">${h.short_id(cs.raw_id)}</span></span></a></span>
62 <span class="tooltip" title="${h.tooltip(h.age(cs.date))}"><a href="${h.url('changeset_home',repo_name=c.repo_name,revision=cs.raw_id)}"><span class="changeset_id">${cs.revision}:<span class="changeset_hash">${h.short_id(cs.raw_id)}</span></span></a></span>
63 </div>
63 </div>
64 <div class="author">
64 <div class="author">
65 <div class="gravatar">
65 <div class="gravatar">
66 <img alt="gravatar" src="${h.gravatar_url(h.email_or_none(cs.author),16)}"/>
66 <img alt="gravatar" src="${h.gravatar_url(h.email_or_none(cs.author),16)}"/>
67 </div>
67 </div>
68 <div title="${cs.author}" class="user">${h.shorter(h.person(cs.author),22)}</div>
68 <div title="${cs.author}" class="user">${h.shorter(h.person(cs.author),22)}</div>
69 </div>
69 </div>
70 <div class="date">${h.fmt_date(cs.date)}</div>
70 <div class="date">${h.fmt_date(cs.date)}</div>
71 </div>
71 </div>
72 <div class="mid">
72 <div class="mid">
73 <div class="message">${h.urlify_commit(cs.message, c.repo_name,h.url('changeset_home',repo_name=c.repo_name,revision=cs.raw_id))}</div>
73 <div class="message">${h.urlify_commit(cs.message, c.repo_name,h.url('changeset_home',repo_name=c.repo_name,revision=cs.raw_id))}</div>
74 <div class="expand"><span class="expandtext">&darr; ${_('show more')} &darr;</span></div>
74 <div class="expand"><span class="expandtext">&darr; ${_('show more')} &darr;</span></div>
75 </div>
75 </div>
76 <div class="right">
76 <div class="right">
77 <div class="changes">
77 <div class="changes">
78 <div id="changed_total_${cs.raw_id}" style="float:right;" class="changed_total tooltip" title="${h.tooltip(_('Affected number of files, click to show more details'))}">${len(cs.affected_files)}</div>
78 <div id="changed_total_${cs.raw_id}" style="float:right;" class="changed_total tooltip" title="${h.tooltip(_('Affected number of files, click to show more details'))}">${len(cs.affected_files)}</div>
79 <div class="comments-container">
79 <div class="comments-container">
80 %if len(c.comments.get(cs.raw_id,[])) > 0:
80 %if len(c.comments.get(cs.raw_id,[])) > 0:
81 <div class="comments-cnt" title="${('comments')}">
81 <div class="comments-cnt" title="${('comments')}">
82 <a href="${h.url('changeset_home',repo_name=c.repo_name,revision=cs.raw_id,anchor='comment-%s' % c.comments[cs.raw_id][0].comment_id)}">
82 <a href="${h.url('changeset_home',repo_name=c.repo_name,revision=cs.raw_id,anchor='comment-%s' % c.comments[cs.raw_id][0].comment_id)}">
83 <div class="comments-cnt">${len(c.comments[cs.raw_id])}</div>
83 <div class="comments-cnt">${len(c.comments[cs.raw_id])}</div>
84 <img src="${h.url('/images/icons/comments.png')}">
84 <img src="${h.url('/images/icons/comments.png')}">
85 </a>
85 </a>
86 </div>
86 </div>
87 %endif
87 %endif
88 </div>
88 </div>
89 <div class="changeset-status-container">
89 <div class="changeset-status-container">
90 %if c.statuses.get(cs.raw_id):
90 %if c.statuses.get(cs.raw_id):
91 <div title="${_('Changeset status')}" class="changeset-status-lbl">${c.statuses.get(cs.raw_id)[1]}</div>
91 <div title="${_('Changeset status')}" class="changeset-status-lbl">${c.statuses.get(cs.raw_id)[1]}</div>
92 <div class="changeset-status-ico">
92 <div class="changeset-status-ico">
93 %if c.statuses.get(cs.raw_id)[2]:
93 %if c.statuses.get(cs.raw_id)[2]:
94 <a class="tooltip" title="${_('Click to open associated pull request')}" href="${h.url('pullrequest_show',repo_name=c.statuses.get(cs.raw_id)[3],pull_request_id=c.statuses.get(cs.raw_id)[2])}"><img src="${h.url('/images/icons/flag_status_%s.png' % c.statuses.get(cs.raw_id)[0])}" /></a>
94 <a class="tooltip" title="${_('Click to open associated pull request')}" href="${h.url('pullrequest_show',repo_name=c.statuses.get(cs.raw_id)[3],pull_request_id=c.statuses.get(cs.raw_id)[2])}"><img src="${h.url('/images/icons/flag_status_%s.png' % c.statuses.get(cs.raw_id)[0])}" /></a>
95 %else:
95 %else:
96 <img src="${h.url('/images/icons/flag_status_%s.png' % c.statuses.get(cs.raw_id)[0])}" />
96 <img src="${h.url('/images/icons/flag_status_%s.png' % c.statuses.get(cs.raw_id)[0])}" />
97 %endif
97 %endif
98 </div>
98 </div>
99 %endif
99 %endif
100 </div>
100 </div>
101 </div>
101 </div>
102 %if cs.parents:
102 %if cs.parents:
103 %for p_cs in reversed(cs.parents):
103 %for p_cs in reversed(cs.parents):
104 <div class="parent">${_('Parent')}
104 <div class="parent">${_('Parent')}
105 <span class="changeset_id">${p_cs.revision}:<span class="changeset_hash">${h.link_to(h.short_id(p_cs.raw_id),
105 <span class="changeset_id">${p_cs.revision}:<span class="changeset_hash">${h.link_to(h.short_id(p_cs.raw_id),
106 h.url('changeset_home',repo_name=c.repo_name,revision=p_cs.raw_id),title=p_cs.message)}</span></span>
106 h.url('changeset_home',repo_name=c.repo_name,revision=p_cs.raw_id),title=p_cs.message)}</span></span>
107 </div>
107 </div>
108 %endfor
108 %endfor
109 %else:
109 %else:
110 <div class="parent">${_('No parents')}</div>
110 <div class="parent">${_('No parents')}</div>
111 %endif
111 %endif
112
112
113 <span class="logtags">
113 <span class="logtags">
114 %if len(cs.parents)>1:
114 %if len(cs.parents)>1:
115 <span class="merge">${_('merge')}</span>
115 <span class="merge">${_('merge')}</span>
116 %endif
116 %endif
117 %if cs.branch:
117 %if cs.branch:
118 <span class="branchtag" title="${'%s %s' % (_('branch'),cs.branch)}">
118 <span class="branchtag" title="${'%s %s' % (_('branch'),cs.branch)}">
119 ${h.link_to(h.shorter(cs.branch),h.url('files_home',repo_name=c.repo_name,revision=cs.raw_id))}
119 ${h.link_to(h.shorter(cs.branch),h.url('files_home',repo_name=c.repo_name,revision=cs.raw_id))}
120 </span>
120 </span>
121 %endif
121 %endif
122 %if h.is_hg(c.rhodecode_repo):
122 %if h.is_hg(c.rhodecode_repo):
123 %for book in cs.bookmarks:
123 %for book in cs.bookmarks:
124 <span class="bookbook" title="${'%s %s' % (_('bookmark'),book)}">
124 <span class="bookbook" title="${'%s %s' % (_('bookmark'),book)}">
125 ${h.link_to(h.shorter(book),h.url('files_home',repo_name=c.repo_name,revision=cs.raw_id))}
125 ${h.link_to(h.shorter(book),h.url('files_home',repo_name=c.repo_name,revision=cs.raw_id))}
126 </span>
126 </span>
127 %endfor
127 %endfor
128 %endif
128 %endif
129 %for tag in cs.tags:
129 %for tag in cs.tags:
130 <span class="tagtag" title="${'%s %s' % (_('tag'),tag)}">
130 <span class="tagtag" title="${'%s %s' % (_('tag'),tag)}">
131 ${h.link_to(h.shorter(tag),h.url('files_home',repo_name=c.repo_name,revision=cs.raw_id))}</span>
131 ${h.link_to(h.shorter(tag),h.url('files_home',repo_name=c.repo_name,revision=cs.raw_id))}</span>
132 %endfor
132 %endfor
133 </span>
133 </span>
134 </div>
134 </div>
135 </div>
135 </div>
136
136
137 %endfor
137 %endfor
138 <div class="pagination-wh pagination-left">
138 <div class="pagination-wh pagination-left">
139 ${c.pagination.pager('$link_previous ~2~ $link_next')}
139 ${c.pagination.pager('$link_previous ~2~ $link_next')}
140 </div>
140 </div>
141 </div>
141 </div>
142 </div>
142 </div>
143
143
144 <script type="text/javascript" src="${h.url('/js/graph.js')}"></script>
144 <script type="text/javascript" src="${h.url('/js/graph.js')}"></script>
145 <script type="text/javascript">
145 <script type="text/javascript">
146 YAHOO.util.Event.onDOMReady(function(){
146 YAHOO.util.Event.onDOMReady(function(){
147
147
148 //Monitor range checkboxes and build a link to changesets
148 //Monitor range checkboxes and build a link to changesets
149 //ranges
149 //ranges
150 var checkboxes = YUD.getElementsByClassName('changeset_range');
150 var checkboxes = YUD.getElementsByClassName('changeset_range');
151 var url_tmpl = "${h.url('changeset_home',repo_name=c.repo_name,revision='__REVRANGE__')}";
151 var url_tmpl = "${h.url('changeset_home',repo_name=c.repo_name,revision='__REVRANGE__')}";
152 YUE.on(checkboxes,'click',function(e){
152 YUE.on(checkboxes,'click',function(e){
153 var clicked_cb = e.currentTarget;
153 var clicked_cb = e.currentTarget;
154 var checked_checkboxes = [];
154 var checked_checkboxes = [];
155 for (pos in checkboxes){
155 for (pos in checkboxes){
156 if(checkboxes[pos].checked){
156 if(checkboxes[pos].checked){
157 checked_checkboxes.push(checkboxes[pos]);
157 checked_checkboxes.push(checkboxes[pos]);
158 }
158 }
159 }
159 }
160 if(YUD.get('open_new_pr')){
160 if(YUD.get('open_new_pr')){
161 if(checked_checkboxes.length>0){
161 if(checked_checkboxes.length>0){
162 // modify open pull request to show we have selected cs
162 // modify open pull request to show we have selected cs
163 YUD.get('open_new_pr').innerHTML = _TM['Open new pull request for selected changesets'];
163 YUD.get('open_new_pr').innerHTML = _TM['Open new pull request for selected changesets'];
164
164
165 }else{
165 }else{
166 YUD.get('open_new_pr').innerHTML = _TM['Open new pull request'];
166 YUD.get('open_new_pr').innerHTML = _TM['Open new pull request'];
167 }
167 }
168 }
168 }
169
169
170 if(checked_checkboxes.length>1){
170 if(checked_checkboxes.length>1){
171 var rev_end = checked_checkboxes[0].name;
171 var rev_end = checked_checkboxes[0].name;
172 var rev_start = checked_checkboxes[checked_checkboxes.length-1].name;
172 var rev_start = checked_checkboxes[checked_checkboxes.length-1].name;
173
173
174 // now select all checkboxes in the middle.
174 // now select all checkboxes in the middle.
175 var checked = false;
175 var checked = false;
176 for (var i=0; i<checkboxes.length; i++){
176 for (var i=0; i<checkboxes.length; i++){
177 var cb = checkboxes[i];
177 var cb = checkboxes[i];
178 var rev = cb.name;
178 var rev = cb.name;
179
179
180 if (rev == rev_end){
180 if (rev == rev_end){
181 checked = true;
181 checked = true;
182 }
182 }
183 if (checked){
183 if (checked){
184 cb.checked = true;
184 cb.checked = true;
185 }
185 }
186 else{
186 else{
187 cb.checked = false;
187 cb.checked = false;
188 }
188 }
189 if (rev == rev_start){
189 if (rev == rev_start){
190 checked = false;
190 checked = false;
191 }
191 }
192
192
193 }
193 }
194
194
195 var url = url_tmpl.replace('__REVRANGE__',
195 var url = url_tmpl.replace('__REVRANGE__',
196 rev_start+'...'+rev_end);
196 rev_start+'...'+rev_end);
197
197
198 var link = _TM['Show selected changes __S -> __E'];
198 var link = _TM['Show selected changes __S -> __E'];
199 link = link.replace('__S',rev_start.substr(0,6));
199 link = link.replace('__S',rev_start.substr(0,6));
200 link = link.replace('__E',rev_end.substr(0,6));
200 link = link.replace('__E',rev_end.substr(0,6));
201 YUD.get('rev_range_container').href = url;
201 YUD.get('rev_range_container').href = url;
202 YUD.get('rev_range_container').innerHTML = link;
202 YUD.get('rev_range_container').innerHTML = link;
203 YUD.setStyle('rev_range_container','display','');
203 YUD.setStyle('rev_range_container','display','');
204 YUD.setStyle('rev_range_clear','display','');
204 YUD.setStyle('rev_range_clear','display','');
205
205
206 YUD.get('open_new_pr').href += '?rev_start={0}&rev_end={1}'.format(rev_start,rev_end);
206 YUD.get('open_new_pr').href += '?rev_start={0}&rev_end={1}'.format(rev_start,rev_end);
207
207
208 }
208 }
209 else{
209 else{
210 YUD.setStyle('rev_range_container','display','none');
210 YUD.setStyle('rev_range_container','display','none');
211 YUD.setStyle('rev_range_clear','display','none');
211 YUD.setStyle('rev_range_clear','display','none');
212 }
212 }
213 });
213 });
214 YUE.on('rev_range_clear','click',function(e){
214 YUE.on('rev_range_clear','click',function(e){
215 for (var i=0; i<checkboxes.length; i++){
215 for (var i=0; i<checkboxes.length; i++){
216 var cb = checkboxes[i];
216 var cb = checkboxes[i];
217 cb.checked = false;
217 cb.checked = false;
218 }
218 }
219 YUE.preventDefault(e);
219 YUE.preventDefault(e);
220 })
220 })
221 var msgs = YUQ('.message');
221 var msgs = YUQ('.message');
222 // get first element height
222 // get first element height
223 var el = YUQ('#graph_content .container')[0];
223 var el = YUQ('#graph_content .container')[0];
224 var row_h = el.clientHeight;
224 var row_h = el.clientHeight;
225 for(var i=0;i<msgs.length;i++){
225 for(var i=0;i<msgs.length;i++){
226 var m = msgs[i];
226 var m = msgs[i];
227
227
228 var h = m.clientHeight;
228 var h = m.clientHeight;
229 var pad = YUD.getStyle(m,'padding');
229 var pad = YUD.getStyle(m,'padding');
230 if(h > row_h){
230 if(h > row_h){
231 var offset = row_h - (h+12);
231 var offset = row_h - (h+12);
232 YUD.setStyle(m.nextElementSibling,'display','block');
232 YUD.setStyle(m.nextElementSibling,'display','block');
233 YUD.setStyle(m.nextElementSibling,'margin-top',offset+'px');
233 YUD.setStyle(m.nextElementSibling,'margin-top',offset+'px');
234 };
234 };
235 }
235 }
236 YUE.on(YUQ('.expand'),'click',function(e){
236 YUE.on(YUQ('.expand'),'click',function(e){
237 var elem = e.currentTarget.parentNode.parentNode;
237 var elem = e.currentTarget.parentNode.parentNode;
238 YUD.setStyle(e.currentTarget,'display','none');
238 YUD.setStyle(e.currentTarget,'display','none');
239 YUD.setStyle(elem,'height','auto');
239 YUD.setStyle(elem,'height','auto');
240
240
241 //redraw the graph, line_count and jsdata are global vars
241 //redraw the graph, line_count and jsdata are global vars
242 set_canvas(100);
242 set_canvas(100);
243
243
244 var r = new BranchRenderer();
244 var r = new BranchRenderer();
245 r.render(jsdata,100,line_count);
245 r.render(jsdata,100,line_count);
246
246
247 })
247 })
248
248
249 // Fetch changeset details
249 // Fetch changeset details
250 YUE.on(YUD.getElementsByClassName('changed_total'),'click',function(e){
250 YUE.on(YUD.getElementsByClassName('changed_total'),'click',function(e){
251 var id = e.currentTarget.id;
251 var id = e.currentTarget.id;
252 var url = "${h.url('changelog_details',repo_name=c.repo_name,cs='__CS__')}";
252 var url = "${h.url('changelog_details',repo_name=c.repo_name,cs='__CS__')}";
253 var url = url.replace('__CS__',id.replace('changed_total_',''));
253 var url = url.replace('__CS__',id.replace('changed_total_',''));
254 ypjax(url,id,function(){tooltip_activate()});
254 ypjax(url,id,function(){tooltip_activate()});
255 });
255 });
256
256
257 // change branch filter
257 // change branch filter
258 YUE.on(YUD.get('branch_filter'),'change',function(e){
258 YUE.on(YUD.get('branch_filter'),'change',function(e){
259 var selected_branch = e.currentTarget.options[e.currentTarget.selectedIndex].value;
259 var selected_branch = e.currentTarget.options[e.currentTarget.selectedIndex].value;
260 var url_main = "${h.url('changelog_home',repo_name=c.repo_name)}";
260 var url_main = "${h.url('changelog_home',repo_name=c.repo_name)}";
261 var url = "${h.url('changelog_home',repo_name=c.repo_name,branch='__BRANCH__')}";
261 var url = "${h.url('changelog_home',repo_name=c.repo_name,branch='__BRANCH__')}";
262 var url = url.replace('__BRANCH__',selected_branch);
262 var url = url.replace('__BRANCH__',selected_branch);
263 if(selected_branch != ''){
263 if(selected_branch != ''){
264 window.location = url;
264 window.location = url;
265 }else{
265 }else{
266 window.location = url_main;
266 window.location = url_main;
267 }
267 }
268
268
269 });
269 });
270
270
271 function set_canvas(width) {
271 function set_canvas(width) {
272 var c = document.getElementById('graph_nodes');
272 var c = document.getElementById('graph_nodes');
273 var t = document.getElementById('graph_content');
273 var t = document.getElementById('graph_content');
274 canvas = document.getElementById('graph_canvas');
274 canvas = document.getElementById('graph_canvas');
275 var div_h = t.clientHeight;
275 var div_h = t.clientHeight;
276 c.style.height=div_h+'px';
276 c.style.height=div_h+'px';
277 canvas.setAttribute('height',div_h);
277 canvas.setAttribute('height',div_h);
278 c.style.height=width+'px';
278 c.style.height=width+'px';
279 canvas.setAttribute('width',width);
279 canvas.setAttribute('width',width);
280 };
280 };
281 var heads = 1;
281 var heads = 1;
282 var line_count = 0;
282 var line_count = 0;
283 var jsdata = ${c.jsdata|n};
283 var jsdata = ${c.jsdata|n};
284
284
285 for (var i=0;i<jsdata.length;i++) {
285 for (var i=0;i<jsdata.length;i++) {
286 var in_l = jsdata[i][2];
286 var in_l = jsdata[i][2];
287 for (var j in in_l) {
287 for (var j in in_l) {
288 var m = in_l[j][1];
288 var m = in_l[j][1];
289 if (m > line_count)
289 if (m > line_count)
290 line_count = m;
290 line_count = m;
291 }
291 }
292 }
292 }
293 set_canvas(100);
293 set_canvas(100);
294
294
295 var r = new BranchRenderer();
295 var r = new BranchRenderer();
296 r.render(jsdata,100,line_count);
296 r.render(jsdata,100,line_count);
297
297
298 });
298 });
299 </script>
299 </script>
300 %else:
300 %else:
301 ${_('There are no changes yet')}
301 ${_('There are no changes yet')}
302 %endif
302 %endif
303 </div>
303 </div>
304 </div>
304 </div>
305 </%def>
305 </%def>
@@ -1,93 +1,93 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 ${c.repo_name} ${_('Compare')} ${'%s@%s' % (c.org_repo.repo_name, c.org_ref)} -> ${'%s@%s' % (c.other_repo.repo_name, c.other_ref)}
5 ${c.repo_name} ${_('Compare')} ${'%s@%s' % (c.org_repo.repo_name, c.org_ref)} -> ${'%s@%s' % (c.other_repo.repo_name, c.other_ref)}
6 </%def>
6 </%def>
7
7
8 <%def name="breadcrumbs_links()">
8 <%def name="breadcrumbs_links()">
9 ${h.link_to(_(u'Home'),h.url('/'))}
9 ${h.link_to(_(u'Home'),h.url('/'))}
10 &raquo;
10 &raquo;
11 ${h.link_to(c.repo_name,h.url('summary_home',repo_name=c.repo_name))}
11 ${h.link_to(c.repo_name,h.url('summary_home',repo_name=c.repo_name))}
12 &raquo;
12 &raquo;
13 ${_('Compare')}
13 ${_('Compare')}
14 </%def>
14 </%def>
15
15
16 <%def name="page_nav()">
16 <%def name="page_nav()">
17 ${self.menu('changelog')}
17 ${self.menu('changelog')}
18 </%def>
18 </%def>
19
19
20 <%def name="main()">
20 <%def name="main()">
21 <div class="box">
21 <div class="box">
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 <div class="table">
26 <div class="table">
27 <div id="body" class="diffblock">
27 <div id="body" class="diffblock">
28 <div class="code-header cv">
28 <div class="code-header cv">
29 <h3 class="code-header-title">${_('Compare View')}</h3>
29 <h3 class="code-header-title">${_('Compare View')}</h3>
30 <div>
30 <div>
31 ${'%s@%s' % (c.org_repo.repo_name, c.org_ref)} -> ${'%s@%s' % (c.other_repo.repo_name, c.other_ref)} <a href="${c.swap_url}">[swap]</a>
31 ${'%s@%s' % (c.org_repo.repo_name, c.org_ref)} -> ${'%s@%s' % (c.other_repo.repo_name, c.other_ref)} <a href="${c.swap_url}">[swap]</a>
32 </div>
32 </div>
33 </div>
33 </div>
34 </div>
34 </div>
35 <div id="changeset_compare_view_content">
35 <div id="changeset_compare_view_content">
36 ##CS
36 ##CS
37 <div style="font-size:1.1em;font-weight: bold;clear:both;padding-top:10px">${ungettext('Showing %s commit','Showing %s commits', len(c.cs_ranges)) % len(c.cs_ranges)}</div>
37 <div style="font-size:1.1em;font-weight: bold;clear:both;padding-top:10px">${ungettext('Showing %s commit','Showing %s commits', len(c.cs_ranges)) % len(c.cs_ranges)}</div>
38 <%include file="compare_cs.html" />
38 <%include file="compare_cs.html" />
39
39
40 ## FILES
40 ## FILES
41 <div style="font-size:1.1em;font-weight: bold;clear:both;padding-top:10px">
41 <div style="font-size:1.1em;font-weight: bold;clear:both;padding-top:10px">
42
42
43 % if c.limited_diff:
43 % if c.limited_diff:
44 ${ungettext('%s file changed', '%s files changed', len(c.files)) % len(c.files)}
44 ${ungettext('%s file changed', '%s files changed', len(c.files)) % len(c.files)}
45 % else:
45 % else:
46 ${ungettext('%s file changed with %s insertions and %s deletions','%s files changed with %s insertions and %s deletions', len(c.files)) % (len(c.files),c.lines_added,c.lines_deleted)}:
46 ${ungettext('%s file changed with %s insertions and %s deletions','%s files changed with %s insertions and %s deletions', len(c.files)) % (len(c.files),c.lines_added,c.lines_deleted)}:
47 %endif
47 %endif
48
48
49 </div>
49 </div>
50 <div class="cs_files">
50 <div class="cs_files">
51 %if not c.files:
51 %if not c.files:
52 <span class="empty_data">${_('No files')}</span>
52 <span class="empty_data">${_('No files')}</span>
53 %endif
53 %endif
54 %for fid, change, f, stat in c.files:
54 %for fid, change, f, stat in c.files:
55 <div class="cs_${change}">
55 <div class="cs_${change}">
56 <div class="node">${h.link_to(h.safe_unicode(f),h.url.current(anchor=fid))}</div>
56 <div class="node">${h.link_to(h.safe_unicode(f),h.url.current(anchor=fid))}</div>
57 <div class="changes">${h.fancy_file_stats(stat)}</div>
57 <div class="changes">${h.fancy_file_stats(stat)}</div>
58 </div>
58 </div>
59 %endfor
59 %endfor
60 </div>
60 </div>
61 % if c.limited_diff:
61 % if c.limited_diff:
62 <h5>${_('Changeset was too big and was cut off...')}</h5>
62 <h5>${_('Changeset was too big and was cut off...')}</h5>
63 % endif
63 % endif
64 </div>
64 </div>
65 </div>
65 </div>
66
66
67 ## diff block
67 ## diff block
68 <%namespace name="diff_block" file="/changeset/diff_block.html"/>
68 <%namespace name="diff_block" file="/changeset/diff_block.html"/>
69 %for fid, change, f, stat in c.files:
69 %for fid, change, f, stat in c.files:
70 ${diff_block.diff_block_simple([c.changes[fid]])}
70 ${diff_block.diff_block_simple([c.changes[fid]])}
71 %endfor
71 %endfor
72 % if c.limited_diff:
72 % if c.limited_diff:
73 <h4>${_('Changeset was too big and was cut off...')}</h4>
73 <h4>${_('Changeset was too big and was cut off...')}</h4>
74 % endif
74 % endif
75 <script type="text/javascript">
75 <script type="text/javascript">
76
76
77 YUE.onDOMReady(function(){
77 YUE.onDOMReady(function(){
78
78
79 YUE.on(YUQ('.diff-menu-activate'),'click',function(e){
79 YUE.on(YUQ('.diff-menu-activate'),'click',function(e){
80 var act = e.currentTarget.nextElementSibling;
80 var act = e.currentTarget.nextElementSibling;
81
81
82 if(YUD.hasClass(act,'active')){
82 if(YUD.hasClass(act,'active')){
83 YUD.removeClass(act,'active');
83 YUD.removeClass(act,'active');
84 YUD.setStyle(act,'display','none');
84 YUD.setStyle(act,'display','none');
85 }else{
85 }else{
86 YUD.addClass(act,'active');
86 YUD.addClass(act,'active');
87 YUD.setStyle(act,'display','');
87 YUD.setStyle(act,'display','');
88 }
88 }
89 });
89 });
90 })
90 })
91 </script>
91 </script>
92 </div>
92 </div>
93 </%def>
93 </%def>
@@ -1,161 +1,161 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 = 5*60*1000; //cache for 5*60s
42 var CACHE_EXPIRE = 5*60*1000; //cache for 5*60s
43 //used to construct links from the search list
43 //used to construct links from the search list
44 var url_base = '${h.url("files_home",repo_name=c.repo_name,revision='__REV__',f_path='__FPATH__')}';
44 var url_base = '${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 node_list_url = '${h.url("files_nodelist_home",repo_name=c.repo_name,revision='__REV__',f_path='__FPATH__')}';
46 var node_list_url = '${h.url("files_nodelist_home",repo_name=c.repo_name,revision='__REV__',f_path='__FPATH__')}';
47 // send the node history requst to this url
47 // send the node history requst to this url
48 var node_history_url = '${h.url("files_history_home",repo_name=c.repo_name,revision='__REV__',f_path='__FPATH__')}';
48 var node_history_url = '${h.url("files_history_home",repo_name=c.repo_name,revision='__REV__',f_path='__FPATH__')}';
49
49
50 var ypjax_links = function(){
50 var ypjax_links = function(){
51 YUE.on(YUQ('.ypjax-link'), 'click',function(e){
51 YUE.on(YUQ('.ypjax-link'), 'click',function(e){
52
52
53 //don't do ypjax on middle click
53 //don't do ypjax on middle click
54 if(e.which == 2 || !History.enabled){
54 if(e.which == 2 || !History.enabled){
55 return true;
55 return true;
56 }
56 }
57
57
58 var el = e.currentTarget;
58 var el = e.currentTarget;
59 var url = el.href;
59 var url = el.href;
60
60
61 var _base_url = '${h.url("files_home",repo_name=c.repo_name,revision='',f_path='')}';
61 var _base_url = '${h.url("files_home",repo_name=c.repo_name,revision='',f_path='')}';
62 _base_url = _base_url.replace('//','/')
62 _base_url = _base_url.replace('//','/')
63
63
64 //extract rev and the f_path from url.
64 //extract rev and the f_path from url.
65 parts = url.split(_base_url)
65 parts = url.split(_base_url)
66 if(parts.length != 2){
66 if(parts.length != 2){
67 return false;
67 return false;
68 }
68 }
69
69
70 var parts2 = parts[1].split('/');
70 var parts2 = parts[1].split('/');
71 var rev = parts2.shift(); // pop the first element which is the revision
71 var rev = parts2.shift(); // pop the first element which is the revision
72 var f_path = parts2.join('/');
72 var f_path = parts2.join('/');
73
73
74 var title = "${_('%s files') % c.repo_name}" + " - " + f_path;
74 var title = "${_('%s files') % c.repo_name}" + " - " + f_path;
75
75
76 var _node_list_url = node_list_url.replace('__REV__',rev).replace('__FPATH__', f_path);
76 var _node_list_url = node_list_url.replace('__REV__',rev).replace('__FPATH__', f_path);
77 var _url_base = url_base.replace('__REV__',rev);
77 var _url_base = url_base.replace('__REV__',rev);
78
78
79 // Change our States and save some data for handling events
79 // Change our States and save some data for handling events
80 var data = {url:url,title:title, url_base:_url_base,
80 var data = {url:url,title:title, url_base:_url_base,
81 node_list_url:_node_list_url, rev:rev, f_path:f_path};
81 node_list_url:_node_list_url, rev:rev, f_path:f_path};
82 History.pushState(data, title, url);
82 History.pushState(data, title, url);
83
83
84 //now we're sure that we can do ypjax things
84 //now we're sure that we can do ypjax things
85 YUE.preventDefault(e);
85 YUE.preventDefault(e);
86 return false;
86 return false;
87 });
87 });
88 }
88 }
89
89
90 var callbacks = function(State){
90 var callbacks = function(State){
91 ypjax_links();
91 ypjax_links();
92 tooltip_activate();
92 tooltip_activate();
93 fileBrowserListeners(State.url, State.data.node_list_url, State.data.url_base);
93 fileBrowserListeners(State.url, State.data.node_list_url, State.data.url_base);
94
94
95 if(YUD.get('hlcode')){
95 if(YUD.get('hlcode')){
96 YUE.on('hlcode', 'mouseup', getSelectionLink);
96 YUE.on('hlcode', 'mouseup', getSelectionLink);
97 }
97 }
98 //console.log(State);
98 //console.log(State);
99 if(YUD.get('load_node_history')){
99 if(YUD.get('load_node_history')){
100 //remove all listeners due to problems of history state
100 //remove all listeners due to problems of history state
101 YUE.removeListener('load_node_history', 'click');
101 YUE.removeListener('load_node_history', 'click');
102 YUE.on('load_node_history', 'click', function(e){
102 YUE.on('load_node_history', 'click', function(e){
103 var _url = node_history_url.replace('__REV__',State.data.rev).replace('__FPATH__', State.data.f_path);
103 var _url = node_history_url.replace('__REV__',State.data.rev).replace('__FPATH__', State.data.f_path);
104 ypjax(_url, 'node_history', function(o){
104 ypjax(_url, 'node_history', function(o){
105 tooltip_activate();
105 tooltip_activate();
106 })
106 })
107 });
107 });
108 }
108 }
109 // Inform Google Analytics of the change
109 // Inform Google Analytics of the change
110 if ( typeof window.pageTracker !== 'undefined' ) {
110 if ( typeof window.pageTracker !== 'undefined' ) {
111 window.pageTracker._trackPageview(State.url);
111 window.pageTracker._trackPageview(State.url);
112 }
112 }
113 }
113 }
114
114
115 YUE.onDOMReady(function(){
115 YUE.onDOMReady(function(){
116 ypjax_links();
116 ypjax_links();
117 var container = 'files_data';
117 var container = 'files_data';
118 //Bind to StateChange Event
118 //Bind to StateChange Event
119 History.Adapter.bind(window,'statechange',function(){
119 History.Adapter.bind(window,'statechange',function(){
120 var State = History.getState();
120 var State = History.getState();
121 cache_key = State.url;
121 cache_key = State.url;
122 //check if we have this request in cache maybe ?
122 //check if we have this request in cache maybe ?
123 var _cache_obj = CACHE[cache_key];
123 var _cache_obj = CACHE[cache_key];
124 var _cur_time = new Date().getTime();
124 var _cur_time = new Date().getTime();
125 // get from cache if it's there and not yet expired !
125 // get from cache if it's there and not yet expired !
126 if(_cache_obj !== undefined && _cache_obj[0] > _cur_time){
126 if(_cache_obj !== undefined && _cache_obj[0] > _cur_time){
127 YUD.get(container).innerHTML=_cache_obj[1];
127 YUD.get(container).innerHTML=_cache_obj[1];
128 YUD.setStyle(container,'opacity','1.0');
128 YUD.setStyle(container,'opacity','1.0');
129
129
130 //callbacks after ypjax call
130 //callbacks after ypjax call
131 callbacks(State);
131 callbacks(State);
132 }
132 }
133 else{
133 else{
134 ypjax(State.url,container,function(o){
134 ypjax(State.url,container,function(o){
135 //callbacks after ypjax call
135 //callbacks after ypjax call
136 callbacks(State);
136 callbacks(State);
137 if (o !== undefined){
137 if (o !== undefined){
138 //store our request in cache
138 //store our request in cache
139 var _expire_on = new Date().getTime()+CACHE_EXPIRE;
139 var _expire_on = new Date().getTime()+CACHE_EXPIRE;
140 CACHE[cache_key] = [_expire_on, o.responseText];
140 CACHE[cache_key] = [_expire_on, o.responseText];
141 }
141 }
142 });
142 });
143 }
143 }
144 });
144 });
145
145
146 // init the search filter
146 // init the search filter
147 var _State = {
147 var _State = {
148 url: "${h.url.current()}",
148 url: "${h.url.current()}",
149 data: {
149 data: {
150 node_list_url: node_list_url.replace('__REV__',"${c.changeset.raw_id}").replace('__FPATH__', "${h.safe_unicode(c.file.path)}"),
150 node_list_url: node_list_url.replace('__REV__',"${c.changeset.raw_id}").replace('__FPATH__', "${h.safe_unicode(c.file.path)}"),
151 url_base: url_base.replace('__REV__',"${c.changeset.raw_id}"),
151 url_base: url_base.replace('__REV__',"${c.changeset.raw_id}"),
152 rev:"${c.changeset.raw_id}",
152 rev:"${c.changeset.raw_id}",
153 f_path: "${h.safe_unicode(c.file.path)}"
153 f_path: "${h.safe_unicode(c.file.path)}"
154 }
154 }
155 }
155 }
156 fileBrowserListeners(_State.url, _State.data.node_list_url, _State.data.url_base);
156 fileBrowserListeners(_State.url, _State.data.node_list_url, _State.data.url_base);
157 });
157 });
158
158
159 </script>
159 </script>
160
160
161 </%def>
161 </%def>
@@ -1,25 +1,25 b''
1 <dl>
1 <dl>
2 <dt class="file_history">${_('History')}</dt>
2 <dt class="file_history">${_('History')}</dt>
3 <dd>
3 <dd>
4 <div>
4 <div>
5 <div style="float:left">
5 <div style="float:left">
6 ${h.form(h.url('files_diff_home',repo_name=c.repo_name,f_path=c.f_path),method='get')}
6 ${h.form(h.url('files_diff_home',repo_name=c.repo_name,f_path=c.f_path),method='get')}
7 ${h.hidden('diff2',c.file_changeset.raw_id)}
7 ${h.hidden('diff2',c.file_changeset.raw_id)}
8 ${h.select('diff1',c.file_changeset.raw_id,c.file_history)}
8 ${h.select('diff1',c.file_changeset.raw_id,c.file_history)}
9 ${h.submit('diff',_('diff to revision'),class_="ui-btn")}
9 ${h.submit('diff',_('diff to revision'),class_="ui-btn")}
10 ${h.submit('show_rev',_('show at revision'),class_="ui-btn")}
10 ${h.submit('show_rev',_('show at revision'),class_="ui-btn")}
11 ${h.hidden('annotate', c.annotate)}
11 ${h.hidden('annotate', c.annotate)}
12 ${h.end_form()}
12 ${h.end_form()}
13 </div>
13 </div>
14 <div class="file_author">
14 <div class="file_author">
15 <div class="item">${h.literal(ungettext(u'%s author',u'%s authors',len(c.authors)) % ('<b>%s</b>' % len(c.authors))) }</div>
15 <div class="item">${h.literal(ungettext(u'%s author',u'%s authors',len(c.authors)) % ('<b>%s</b>' % len(c.authors))) }</div>
16 %for email, user in c.authors:
16 %for email, user in c.authors:
17 <div class="contributor tooltip" style="float:left" title="${h.tooltip(user)}">
17 <div class="contributor tooltip" style="float:left" title="${h.tooltip(user)}">
18 <div class="gravatar" style="margin:1px"><img alt="gravatar" src="${h.gravatar_url(email, 20)}"/> </div>
18 <div class="gravatar" style="margin:1px"><img alt="gravatar" src="${h.gravatar_url(email, 20)}"/> </div>
19 </div>
19 </div>
20 %endfor
20 %endfor
21 </div>
21 </div>
22 </div>
22 </div>
23 <div style="clear:both"></div>
23 <div style="clear:both"></div>
24 </dd>
24 </dd>
25 </dl> No newline at end of file
25 </dl>
@@ -1,113 +1,113 b''
1 <div id="node_history">
1 <div id="node_history">
2 %if c.load_full_history:
2 %if c.load_full_history:
3 <%include file='files_history_box.html'/>
3 <%include file='files_history_box.html'/>
4 %else:
4 %else:
5 <div style="padding-bottom:10px">
5 <div style="padding-bottom:10px">
6 <span id="load_node_history" class="ui-btn">${_('Load file history')}</span>
6 <span id="load_node_history" class="ui-btn">${_('Load file history')}</span>
7 </div>
7 </div>
8 %endif
8 %endif
9 </div>
9 </div>
10
10
11
11
12 <div id="body" class="codeblock">
12 <div id="body" class="codeblock">
13 <div class="code-header">
13 <div class="code-header">
14 <div class="stats">
14 <div class="stats">
15 <div class="left img"><img src="${h.url('/images/icons/file.png')}"/></div>
15 <div class="left img"><img src="${h.url('/images/icons/file.png')}"/></div>
16 <div class="left item"><pre class="tooltip" title="${h.tooltip(h.fmt_date(c.file_changeset.date))}">${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>
16 <div class="left item"><pre class="tooltip" title="${h.tooltip(h.fmt_date(c.file_changeset.date))}">${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>
17 <div class="left item"><pre>${h.format_byte_size(c.file.size,binary=True)}</pre></div>
17 <div class="left item"><pre>${h.format_byte_size(c.file.size,binary=True)}</pre></div>
18 <div class="left item last"><pre>${c.file.mimetype}</pre></div>
18 <div class="left item last"><pre>${c.file.mimetype}</pre></div>
19 <div class="buttons">
19 <div class="buttons">
20 %if c.annotate:
20 %if c.annotate:
21 ${h.link_to(_('show source'), h.url('files_home', repo_name=c.repo_name,revision=c.file_changeset.raw_id,f_path=c.f_path),class_="ui-btn")}
21 ${h.link_to(_('show source'), h.url('files_home', repo_name=c.repo_name,revision=c.file_changeset.raw_id,f_path=c.f_path),class_="ui-btn")}
22 %else:
22 %else:
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")}
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 %endif
24 %endif
25 ${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(_('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")}
26 ${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 ${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")}
27 % if h.HasRepoPermissionAny('repository.write','repository.admin')(c.repo_name):
27 % if h.HasRepoPermissionAny('repository.write','repository.admin')(c.repo_name):
28 % if not c.file.is_binary:
28 % if not c.file.is_binary:
29 ${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 ${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")}
30 % endif
30 % endif
31 % endif
31 % endif
32 </div>
32 </div>
33 </div>
33 </div>
34 <div class="author">
34 <div class="author">
35 <div class="gravatar">
35 <div class="gravatar">
36 <img alt="gravatar" src="${h.gravatar_url(h.email_or_none(c.file_changeset.author),16)}"/>
36 <img alt="gravatar" src="${h.gravatar_url(h.email_or_none(c.file_changeset.author),16)}"/>
37 </div>
37 </div>
38 <div title="${c.file_changeset.author}" class="user">${h.person(c.file_changeset.author)}</div>
38 <div title="${c.file_changeset.author}" class="user">${h.person(c.file_changeset.author)}</div>
39 </div>
39 </div>
40 <div class="commit">${h.urlify_commit(c.file_changeset.message,c.repo_name)}</div>
40 <div class="commit">${h.urlify_commit(c.file_changeset.message,c.repo_name)}</div>
41 </div>
41 </div>
42 <div class="code-body">
42 <div class="code-body">
43 %if c.file.is_binary:
43 %if c.file.is_binary:
44 ${_('Binary file (%s)') % c.file.mimetype}
44 ${_('Binary file (%s)') % c.file.mimetype}
45 %else:
45 %else:
46 % if c.file.size < c.cut_off_limit:
46 % if c.file.size < c.cut_off_limit:
47 %if c.annotate:
47 %if c.annotate:
48 ${h.pygmentize_annotation(c.repo_name,c.file,linenos=True,anchorlinenos=True,lineanchors='L',cssclass="code-highlight")}
48 ${h.pygmentize_annotation(c.repo_name,c.file,linenos=True,anchorlinenos=True,lineanchors='L',cssclass="code-highlight")}
49 %else:
49 %else:
50 ${h.pygmentize(c.file,linenos=True,anchorlinenos=True,lineanchors='L',cssclass="code-highlight")}
50 ${h.pygmentize(c.file,linenos=True,anchorlinenos=True,lineanchors='L',cssclass="code-highlight")}
51 %endif
51 %endif
52 %else:
52 %else:
53 ${_('File is too big to display')} ${h.link_to(_('show as raw'),
53 ${_('File is too big to display')} ${h.link_to(_('show as raw'),
54 h.url('files_raw_home',repo_name=c.repo_name,revision=c.file_changeset.raw_id,f_path=c.f_path))}
54 h.url('files_raw_home',repo_name=c.repo_name,revision=c.file_changeset.raw_id,f_path=c.f_path))}
55 %endif
55 %endif
56 %endif
56 %endif
57 </div>
57 </div>
58 </div>
58 </div>
59
59
60 <script type="text/javascript">
60 <script type="text/javascript">
61 YUE.onDOMReady(function(){
61 YUE.onDOMReady(function(){
62 function highlight_lines(lines){
62 function highlight_lines(lines){
63 for(pos in lines){
63 for(pos in lines){
64 YUD.setStyle('L'+lines[pos],'background-color','#FFFFBE');
64 YUD.setStyle('L'+lines[pos],'background-color','#FFFFBE');
65 }
65 }
66 }
66 }
67 page_highlights = location.href.substring(location.href.indexOf('#')+1).split('L');
67 page_highlights = location.href.substring(location.href.indexOf('#')+1).split('L');
68 if (page_highlights.length == 2){
68 if (page_highlights.length == 2){
69 highlight_ranges = page_highlights[1].split(",");
69 highlight_ranges = page_highlights[1].split(",");
70
70
71 var h_lines = [];
71 var h_lines = [];
72 for (pos in highlight_ranges){
72 for (pos in highlight_ranges){
73 var _range = highlight_ranges[pos].split('-');
73 var _range = highlight_ranges[pos].split('-');
74 if(_range.length == 2){
74 if(_range.length == 2){
75 var start = parseInt(_range[0]);
75 var start = parseInt(_range[0]);
76 var end = parseInt(_range[1]);
76 var end = parseInt(_range[1]);
77 if (start < end){
77 if (start < end){
78 for(var i=start;i<=end;i++){
78 for(var i=start;i<=end;i++){
79 h_lines.push(i);
79 h_lines.push(i);
80 }
80 }
81 }
81 }
82 }
82 }
83 else{
83 else{
84 h_lines.push(parseInt(highlight_ranges[pos]));
84 h_lines.push(parseInt(highlight_ranges[pos]));
85 }
85 }
86 }
86 }
87 highlight_lines(h_lines);
87 highlight_lines(h_lines);
88
88
89 //remember original location
89 //remember original location
90 var old_hash = location.href.substring(location.href.indexOf('#'));
90 var old_hash = location.href.substring(location.href.indexOf('#'));
91
91
92 // this makes a jump to anchor moved by 3 posstions for padding
92 // this makes a jump to anchor moved by 3 posstions for padding
93 window.location.hash = '#L'+Math.max(parseInt(h_lines[0])-3,1);
93 window.location.hash = '#L'+Math.max(parseInt(h_lines[0])-3,1);
94
94
95 //sets old anchor
95 //sets old anchor
96 window.location.hash = old_hash;
96 window.location.hash = old_hash;
97
97
98 }
98 }
99
99
100 // select code link event
100 // select code link event
101 YUE.on('hlcode', 'mouseup', getSelectionLink);
101 YUE.on('hlcode', 'mouseup', getSelectionLink);
102
102
103 //load history of file
103 //load history of file
104 YUE.on('load_node_history', 'click', function(e){
104 YUE.on('load_node_history', 'click', function(e){
105 var _url = node_history_url.replace('__REV__','${c.file_changeset.raw_id}').replace('__FPATH__', '${c.f_path}');
105 var _url = node_history_url.replace('__REV__','${c.file_changeset.raw_id}').replace('__FPATH__', '${c.f_path}');
106 ypjax(_url, 'node_history', function(o){
106 ypjax(_url, 'node_history', function(o){
107 tooltip_activate();
107 tooltip_activate();
108 })
108 })
109 });
109 });
110
110
111 });
111 });
112
112
113 </script>
113 </script>
@@ -1,204 +1,204 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} ${_('New pull request')}
4 ${c.repo_name} ${_('New pull request')}
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 ${_('New pull request')}
12 ${_('New pull request')}
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 ${h.form(url('pullrequest', repo_name=c.repo_name), method='post', id='pull_request_form')}
22 ${h.form(url('pullrequest', repo_name=c.repo_name), method='post', id='pull_request_form')}
23 <div style="float:left;padding:0px 30px 30px 30px">
23 <div style="float:left;padding:0px 30px 30px 30px">
24 <input type="hidden" name="rev_start" value="${request.GET.get('rev_start')}" />
24 <input type="hidden" name="rev_start" value="${request.GET.get('rev_start')}" />
25 <input type="hidden" name="rev_end" value="${request.GET.get('rev_end')}" />
25 <input type="hidden" name="rev_end" value="${request.GET.get('rev_end')}" />
26
26
27 ##ORG
27 ##ORG
28 <div style="float:left">
28 <div style="float:left">
29 <div class="fork_user">
29 <div class="fork_user">
30 <div class="gravatar">
30 <div class="gravatar">
31 <img alt="gravatar" src="${h.gravatar_url(c.rhodecode_db_repo.user.email,24)}"/>
31 <img alt="gravatar" src="${h.gravatar_url(c.rhodecode_db_repo.user.email,24)}"/>
32 </div>
32 </div>
33 <span style="font-size: 20px">
33 <span style="font-size: 20px">
34 ${h.select('org_repo','',c.org_repos,class_='refs')}:${h.select('org_ref','',c.org_refs,class_='refs')}
34 ${h.select('org_repo','',c.org_repos,class_='refs')}:${h.select('org_ref','',c.org_refs,class_='refs')}
35 </span>
35 </span>
36 <div style="padding:5px 3px 3px 42px;">${c.rhodecode_db_repo.description}</div>
36 <div style="padding:5px 3px 3px 42px;">${c.rhodecode_db_repo.description}</div>
37 </div>
37 </div>
38 <div style="clear:both;padding-top: 10px"></div>
38 <div style="clear:both;padding-top: 10px"></div>
39 </div>
39 </div>
40 <div style="float:left;font-size:24px;padding:0px 20px">
40 <div style="float:left;font-size:24px;padding:0px 20px">
41 <img height=32 width=32 src="${h.url('/images/arrow_right_64.png')}"/>
41 <img height=32 width=32 src="${h.url('/images/arrow_right_64.png')}"/>
42 </div>
42 </div>
43
43
44 ##OTHER, most Probably the PARENT OF THIS FORK
44 ##OTHER, most Probably the PARENT OF THIS FORK
45 <div style="float:left">
45 <div style="float:left">
46 <div class="fork_user">
46 <div class="fork_user">
47 <div class="gravatar">
47 <div class="gravatar">
48 <img id="other_repo_gravatar" alt="gravatar" src=""/>
48 <img id="other_repo_gravatar" alt="gravatar" src=""/>
49 </div>
49 </div>
50 <span style="font-size: 20px">
50 <span style="font-size: 20px">
51 ${h.select('other_repo',c.default_pull_request ,c.other_repos,class_='refs')}:${h.select('other_ref',c.default_pull_request_rev,c.default_revs,class_='refs')}
51 ${h.select('other_repo',c.default_pull_request ,c.other_repos,class_='refs')}:${h.select('other_ref',c.default_pull_request_rev,c.default_revs,class_='refs')}
52 </span>
52 </span>
53 <span style="padding:3px">
53 <span style="padding:3px">
54 <a id="refresh" href="#" class="tooltip" title="${h.tooltip(_('refresh overview'))}">
54 <a id="refresh" href="#" class="tooltip" title="${h.tooltip(_('refresh overview'))}">
55 <img style="margin:3px" class="icon" title="${_('Refresh')}" alt="${_('Refresh')}" src="${h.url('/images/icons/arrow_refresh.png')}"/>
55 <img style="margin:3px" class="icon" title="${_('Refresh')}" alt="${_('Refresh')}" src="${h.url('/images/icons/arrow_refresh.png')}"/>
56 </a>
56 </a>
57 </span>
57 </span>
58 <div id="other_repo_desc" style="padding:5px 3px 3px 42px;"></div>
58 <div id="other_repo_desc" style="padding:5px 3px 3px 42px;"></div>
59 </div>
59 </div>
60 <div style="clear:both;padding-top: 10px"></div>
60 <div style="clear:both;padding-top: 10px"></div>
61 </div>
61 </div>
62 <div style="clear:both;padding-top: 10px"></div>
62 <div style="clear:both;padding-top: 10px"></div>
63 ## overview pulled by ajax
63 ## overview pulled by ajax
64 <div style="float:left" id="pull_request_overview"></div>
64 <div style="float:left" id="pull_request_overview"></div>
65 <div style="float:left;clear:both;padding:10px 10px 10px 0px;display:none">
65 <div style="float:left;clear:both;padding:10px 10px 10px 0px;display:none">
66 <a id="pull_request_overview_url" href="#">${_('Detailed compare view')}</a>
66 <a id="pull_request_overview_url" href="#">${_('Detailed compare view')}</a>
67 </div>
67 </div>
68 </div>
68 </div>
69 <div style="float:left; border-left:1px dashed #eee">
69 <div style="float:left; border-left:1px dashed #eee">
70 <h4>${_('Pull request reviewers')}</h4>
70 <h4>${_('Pull request reviewers')}</h4>
71 <div id="reviewers" style="padding:0px 0px 0px 15px">
71 <div id="reviewers" style="padding:0px 0px 0px 15px">
72 ## members goes here !
72 ## members goes here !
73 <div class="group_members_wrap">
73 <div class="group_members_wrap">
74 <ul id="review_members" class="group_members">
74 <ul id="review_members" class="group_members">
75 %for member in c.review_members:
75 %for member in c.review_members:
76 <li id="reviewer_${member.user_id}">
76 <li id="reviewer_${member.user_id}">
77 <div class="reviewers_member">
77 <div class="reviewers_member">
78 <div class="gravatar"><img alt="gravatar" src="${h.gravatar_url(member.email,14)}"/> </div>
78 <div class="gravatar"><img alt="gravatar" src="${h.gravatar_url(member.email,14)}"/> </div>
79 <div style="float:left">${member.full_name} (${_('owner')})</div>
79 <div style="float:left">${member.full_name} (${_('owner')})</div>
80 <input type="hidden" value="${member.user_id}" name="review_members" />
80 <input type="hidden" value="${member.user_id}" name="review_members" />
81 <span class="delete_icon action_button" onclick="removeReviewer(${member.user_id})"></span>
81 <span class="delete_icon action_button" onclick="removeReviewer(${member.user_id})"></span>
82 </div>
82 </div>
83 </li>
83 </li>
84 %endfor
84 %endfor
85 </ul>
85 </ul>
86 </div>
86 </div>
87
87
88 <div class='ac'>
88 <div class='ac'>
89 <div class="reviewer_ac">
89 <div class="reviewer_ac">
90 ${h.text('user', class_='yui-ac-input')}
90 ${h.text('user', class_='yui-ac-input')}
91 <span class="help-block">${_('Add reviewer to this pull request.')}</span>
91 <span class="help-block">${_('Add reviewer to this pull request.')}</span>
92 <div id="reviewers_container"></div>
92 <div id="reviewers_container"></div>
93 </div>
93 </div>
94 </div>
94 </div>
95 </div>
95 </div>
96 </div>
96 </div>
97 <h3>${_('Create new pull request')}</h3>
97 <h3>${_('Create new pull request')}</h3>
98
98
99 <div class="form">
99 <div class="form">
100 <!-- fields -->
100 <!-- fields -->
101
101
102 <div class="fields">
102 <div class="fields">
103
103
104 <div class="field">
104 <div class="field">
105 <div class="label">
105 <div class="label">
106 <label for="pullrequest_title">${_('Title')}:</label>
106 <label for="pullrequest_title">${_('Title')}:</label>
107 </div>
107 </div>
108 <div class="input">
108 <div class="input">
109 ${h.text('pullrequest_title',size=30)}
109 ${h.text('pullrequest_title',size=30)}
110 </div>
110 </div>
111 </div>
111 </div>
112
112
113 <div class="field">
113 <div class="field">
114 <div class="label label-textarea">
114 <div class="label label-textarea">
115 <label for="pullrequest_desc">${_('description')}:</label>
115 <label for="pullrequest_desc">${_('description')}:</label>
116 </div>
116 </div>
117 <div class="textarea text-area editor">
117 <div class="textarea text-area editor">
118 ${h.textarea('pullrequest_desc',size=30)}
118 ${h.textarea('pullrequest_desc',size=30)}
119 </div>
119 </div>
120 </div>
120 </div>
121
121
122 <div class="buttons">
122 <div class="buttons">
123 ${h.submit('save',_('Send pull request'),class_="ui-btn large")}
123 ${h.submit('save',_('Send pull request'),class_="ui-btn large")}
124 ${h.reset('reset',_('Reset'),class_="ui-btn large")}
124 ${h.reset('reset',_('Reset'),class_="ui-btn large")}
125 </div>
125 </div>
126 </div>
126 </div>
127 </div>
127 </div>
128 ${h.end_form()}
128 ${h.end_form()}
129
129
130 </div>
130 </div>
131
131
132 <script type="text/javascript">
132 <script type="text/javascript">
133 var _USERS_AC_DATA = ${c.users_array|n};
133 var _USERS_AC_DATA = ${c.users_array|n};
134 var _GROUPS_AC_DATA = ${c.users_groups_array|n};
134 var _GROUPS_AC_DATA = ${c.users_groups_array|n};
135 PullRequestAutoComplete('user', 'reviewers_container', _USERS_AC_DATA, _GROUPS_AC_DATA);
135 PullRequestAutoComplete('user', 'reviewers_container', _USERS_AC_DATA, _GROUPS_AC_DATA);
136
136
137 var other_repos_info = ${c.other_repos_info|n};
137 var other_repos_info = ${c.other_repos_info|n};
138
138
139 var loadPreview = function(){
139 var loadPreview = function(){
140 YUD.setStyle(YUD.get('pull_request_overview_url').parentElement,'display','none');
140 YUD.setStyle(YUD.get('pull_request_overview_url').parentElement,'display','none');
141 var url = "${h.url('compare_url',
141 var url = "${h.url('compare_url',
142 repo_name='org_repo',
142 repo_name='org_repo',
143 org_ref_type='org_ref_type', org_ref='org_ref',
143 org_ref_type='org_ref_type', org_ref='org_ref',
144 other_ref_type='other_ref_type', other_ref='other_ref',
144 other_ref_type='other_ref_type', other_ref='other_ref',
145 repo='other_repo',
145 repo='other_repo',
146 as_form=True, bundle=False,
146 as_form=True, bundle=False,
147 rev_start=request.GET.get('rev_start',''),
147 rev_start=request.GET.get('rev_start',''),
148 rev_end=request.GET.get('rev_end',''))}";
148 rev_end=request.GET.get('rev_end',''))}";
149
149
150 var select_refs = YUQ('#pull_request_form select.refs')
150 var select_refs = YUQ('#pull_request_form select.refs')
151 var rev_data = {}; // gather the org/other ref and repo here
151 var rev_data = {}; // gather the org/other ref and repo here
152 for(var i=0;i<select_refs.length;i++){
152 for(var i=0;i<select_refs.length;i++){
153 var select_ref = select_refs[i];
153 var select_ref = select_refs[i];
154 var select_ref_data = select_ref.value.split(':');
154 var select_ref_data = select_ref.value.split(':');
155 var key = null;
155 var key = null;
156 var val = null;
156 var val = null;
157
157
158 if(select_ref_data.length>1){
158 if(select_ref_data.length>1){
159 key = select_ref.name+"_type";
159 key = select_ref.name+"_type";
160 val = select_ref_data[0];
160 val = select_ref_data[0];
161 url = url.replace(key,val);
161 url = url.replace(key,val);
162 rev_data[key] = val;
162 rev_data[key] = val;
163
163
164 key = select_ref.name;
164 key = select_ref.name;
165 val = select_ref_data[1];
165 val = select_ref_data[1];
166 url = url.replace(key,val);
166 url = url.replace(key,val);
167 rev_data[key] = val;
167 rev_data[key] = val;
168
168
169 }else{
169 }else{
170 key = select_ref.name;
170 key = select_ref.name;
171 val = select_ref.value;
171 val = select_ref.value;
172 url = url.replace(key,val);
172 url = url.replace(key,val);
173 rev_data[key] = val;
173 rev_data[key] = val;
174 }
174 }
175 }
175 }
176
176
177 YUE.on('other_repo', 'change', function(e){
177 YUE.on('other_repo', 'change', function(e){
178 var repo_name = e.currentTarget.value;
178 var repo_name = e.currentTarget.value;
179 // replace the <select> of changed repo
179 // replace the <select> of changed repo
180 YUD.get('other_ref').innerHTML = other_repos_info[repo_name]['revs'];
180 YUD.get('other_ref').innerHTML = other_repos_info[repo_name]['revs'];
181 });
181 });
182
182
183 ypjax(url,'pull_request_overview', function(data){
183 ypjax(url,'pull_request_overview', function(data){
184 var sel_box = YUQ('#pull_request_form #other_repo')[0];
184 var sel_box = YUQ('#pull_request_form #other_repo')[0];
185 var repo_name = sel_box.options[sel_box.selectedIndex].value;
185 var repo_name = sel_box.options[sel_box.selectedIndex].value;
186 YUD.get('pull_request_overview_url').href = url;
186 YUD.get('pull_request_overview_url').href = url;
187 YUD.setStyle(YUD.get('pull_request_overview_url').parentElement,'display','');
187 YUD.setStyle(YUD.get('pull_request_overview_url').parentElement,'display','');
188 YUD.get('other_repo_gravatar').src = other_repos_info[repo_name]['gravatar'];
188 YUD.get('other_repo_gravatar').src = other_repos_info[repo_name]['gravatar'];
189 YUD.get('other_repo_desc').innerHTML = other_repos_info[repo_name]['description'];
189 YUD.get('other_repo_desc').innerHTML = other_repos_info[repo_name]['description'];
190 YUD.get('other_ref').innerHTML = other_repos_info[repo_name]['revs'];
190 YUD.get('other_ref').innerHTML = other_repos_info[repo_name]['revs'];
191 // select back the revision that was just compared
191 // select back the revision that was just compared
192 setSelectValue(YUD.get('other_ref'), rev_data['other_ref']);
192 setSelectValue(YUD.get('other_ref'), rev_data['other_ref']);
193 })
193 })
194 }
194 }
195 YUE.on('refresh','click',function(e){
195 YUE.on('refresh','click',function(e){
196 loadPreview()
196 loadPreview()
197 })
197 })
198
198
199 //lazy load overview after 0.5s
199 //lazy load overview after 0.5s
200 setTimeout(loadPreview, 500)
200 setTimeout(loadPreview, 500)
201
201
202 </script>
202 </script>
203
203
204 </%def>
204 </%def>
@@ -1,217 +1,217 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 % if len(c.pull_request_pending_reviewers) > 0:
47 % if len(c.pull_request_pending_reviewers) > 0:
48 <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 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>
49 %else:
49 %else:
50 <div>${_('pull request was reviewed by all reviewers')}</div>
50 <div>${_('pull request was reviewed by all reviewers')}</div>
51 %endif
51 %endif
52 </div>
52 </div>
53 </div>
53 </div>
54 </div>
54 </div>
55 </div>
55 </div>
56 <div style="white-space:pre-wrap;padding:3px 3px 5px 20px">${h.literal(c.pull_request.description)}</div>
56 <div style="white-space:pre-wrap;padding:3px 3px 5px 20px">${h.literal(c.pull_request.description)}</div>
57 <div style="padding:4px 4px 10px 20px">
57 <div style="padding:4px 4px 10px 20px">
58 <div>${_('Created on')}: ${h.fmt_date(c.pull_request.created_on)}</div>
58 <div>${_('Created on')}: ${h.fmt_date(c.pull_request.created_on)}</div>
59 </div>
59 </div>
60
60
61 <div style="overflow: auto;">
61 <div style="overflow: auto;">
62 ##DIFF
62 ##DIFF
63 <div class="table" style="float:left;clear:none">
63 <div class="table" style="float:left;clear:none">
64 <div id="body" class="diffblock">
64 <div id="body" class="diffblock">
65 <div style="white-space:pre-wrap;padding:5px">${_('Compare view')}</div>
65 <div style="white-space:pre-wrap;padding:5px">${_('Compare view')}</div>
66 </div>
66 </div>
67 <div id="changeset_compare_view_content">
67 <div id="changeset_compare_view_content">
68 ##CS
68 ##CS
69 <div style="font-size:1.1em;font-weight: bold;clear:both;padding-top:10px">${ungettext('Showing %s commit','Showing %s commits', len(c.cs_ranges)) % len(c.cs_ranges)}</div>
69 <div style="font-size:1.1em;font-weight: bold;clear:both;padding-top:10px">${ungettext('Showing %s commit','Showing %s commits', len(c.cs_ranges)) % len(c.cs_ranges)}</div>
70 <%include file="/compare/compare_cs.html" />
70 <%include file="/compare/compare_cs.html" />
71
71
72 ## FILES
72 ## FILES
73 <div style="font-size:1.1em;font-weight: bold;clear:both;padding-top:10px">
73 <div style="font-size:1.1em;font-weight: bold;clear:both;padding-top:10px">
74
74
75 % if c.limited_diff:
75 % if c.limited_diff:
76 ${ungettext('%s file changed', '%s files changed', len(c.files)) % len(c.files)}
76 ${ungettext('%s file changed', '%s files changed', len(c.files)) % len(c.files)}
77 % else:
77 % else:
78 ${ungettext('%s file changed with %s insertions and %s deletions','%s files changed with %s insertions and %s deletions', len(c.files)) % (len(c.files),c.lines_added,c.lines_deleted)}:
78 ${ungettext('%s file changed with %s insertions and %s deletions','%s files changed with %s insertions and %s deletions', len(c.files)) % (len(c.files),c.lines_added,c.lines_deleted)}:
79 %endif
79 %endif
80
80
81 </div>
81 </div>
82 <div class="cs_files">
82 <div class="cs_files">
83 %if not c.files:
83 %if not c.files:
84 <span class="empty_data">${_('No files')}</span>
84 <span class="empty_data">${_('No files')}</span>
85 %endif
85 %endif
86 %for fid, change, f, stat in c.files:
86 %for fid, change, f, stat in c.files:
87 <div class="cs_${change}">
87 <div class="cs_${change}">
88 <div class="node">${h.link_to(h.safe_unicode(f),h.url.current(anchor=fid))}</div>
88 <div class="node">${h.link_to(h.safe_unicode(f),h.url.current(anchor=fid))}</div>
89 <div class="changes">${h.fancy_file_stats(stat)}</div>
89 <div class="changes">${h.fancy_file_stats(stat)}</div>
90 </div>
90 </div>
91 %endfor
91 %endfor
92 </div>
92 </div>
93 % if c.limited_diff:
93 % if c.limited_diff:
94 <h5>${_('Changeset was too big and was cut off...')}</h5>
94 <h5>${_('Changeset was too big and was cut off...')}</h5>
95 % endif
95 % endif
96 </div>
96 </div>
97 </div>
97 </div>
98 ## REVIEWERS
98 ## REVIEWERS
99 <div style="float:left; border-left:1px dashed #eee">
99 <div style="float:left; border-left:1px dashed #eee">
100 <h4>${_('Pull request reviewers')}</h4>
100 <h4>${_('Pull request reviewers')}</h4>
101 <div id="reviewers" style="padding:0px 0px 5px 10px">
101 <div id="reviewers" style="padding:0px 0px 5px 10px">
102 ## members goes here !
102 ## members goes here !
103 <div class="group_members_wrap" style="min-height:45px">
103 <div class="group_members_wrap" style="min-height:45px">
104 <ul id="review_members" class="group_members">
104 <ul id="review_members" class="group_members">
105 %for member,status in c.pull_request_reviewers:
105 %for member,status in c.pull_request_reviewers:
106 <li id="reviewer_${member.user_id}">
106 <li id="reviewer_${member.user_id}">
107 <div class="reviewers_member">
107 <div class="reviewers_member">
108 <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'))}">
108 <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'))}">
109 <img src="${h.url(str('/images/icons/flag_status_%s.png' % (status[0][1].status if status else 'not_reviewed')))}"/>
109 <img src="${h.url(str('/images/icons/flag_status_%s.png' % (status[0][1].status if status else 'not_reviewed')))}"/>
110 </div>
110 </div>
111 <div class="gravatar"><img alt="gravatar" src="${h.gravatar_url(member.email,14)}"/> </div>
111 <div class="gravatar"><img alt="gravatar" src="${h.gravatar_url(member.email,14)}"/> </div>
112 <div style="float:left">${member.full_name} (${_('owner')})</div>
112 <div style="float:left">${member.full_name} (${_('owner')})</div>
113 <input type="hidden" value="${member.user_id}" name="review_members" />
113 <input type="hidden" value="${member.user_id}" name="review_members" />
114 %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):
114 %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):
115 <span class="delete_icon action_button" onclick="removeReviewer(${member.user_id})"></span>
115 <span class="delete_icon action_button" onclick="removeReviewer(${member.user_id})"></span>
116 %endif
116 %endif
117 </div>
117 </div>
118 </li>
118 </li>
119 %endfor
119 %endfor
120 </ul>
120 </ul>
121 </div>
121 </div>
122 %if not c.pull_request.is_closed():
122 %if not c.pull_request.is_closed():
123 <div class='ac'>
123 <div class='ac'>
124 %if h.HasPermissionAny('hg.admin', 'repository.admin')() or c.pull_request.author.user_id == c.rhodecode_user.user_id:
124 %if h.HasPermissionAny('hg.admin', 'repository.admin')() or c.pull_request.author.user_id == c.rhodecode_user.user_id:
125 <div class="reviewer_ac">
125 <div class="reviewer_ac">
126 ${h.text('user', class_='yui-ac-input')}
126 ${h.text('user', class_='yui-ac-input')}
127 <span class="help-block">${_('Add reviewer to this pull request.')}</span>
127 <span class="help-block">${_('Add reviewer to this pull request.')}</span>
128 <div id="reviewers_container"></div>
128 <div id="reviewers_container"></div>
129 </div>
129 </div>
130 <div style="padding:0px 10px">
130 <div style="padding:0px 10px">
131 <span id="update_pull_request" class="ui-btn xsmall">${_('save')}</span>
131 <span id="update_pull_request" class="ui-btn xsmall">${_('save')}</span>
132 </div>
132 </div>
133 %endif
133 %endif
134 </div>
134 </div>
135 %endif
135 %endif
136 </div>
136 </div>
137 </div>
137 </div>
138 </div>
138 </div>
139 <script>
139 <script>
140 var _USERS_AC_DATA = ${c.users_array|n};
140 var _USERS_AC_DATA = ${c.users_array|n};
141 var _GROUPS_AC_DATA = ${c.users_groups_array|n};
141 var _GROUPS_AC_DATA = ${c.users_groups_array|n};
142 AJAX_COMMENT_URL = "${url('pullrequest_comment',repo_name=c.repo_name,pull_request_id=c.pull_request.pull_request_id)}";
142 AJAX_COMMENT_URL = "${url('pullrequest_comment',repo_name=c.repo_name,pull_request_id=c.pull_request.pull_request_id)}";
143 AJAX_COMMENT_DELETE_URL = "${url('pullrequest_comment_delete',repo_name=c.repo_name,comment_id='__COMMENT_ID__')}";
143 AJAX_COMMENT_DELETE_URL = "${url('pullrequest_comment_delete',repo_name=c.repo_name,comment_id='__COMMENT_ID__')}";
144 AJAX_UPDATE_PULLREQUEST = "${url('pullrequest_update',repo_name=c.repo_name,pull_request_id=c.pull_request.pull_request_id)}"
144 AJAX_UPDATE_PULLREQUEST = "${url('pullrequest_update',repo_name=c.repo_name,pull_request_id=c.pull_request.pull_request_id)}"
145 </script>
145 </script>
146
146
147 ## diff block
147 ## diff block
148 <%namespace name="diff_block" file="/changeset/diff_block.html"/>
148 <%namespace name="diff_block" file="/changeset/diff_block.html"/>
149 %for fid, change, f, stat in c.files:
149 %for fid, change, f, stat in c.files:
150 ${diff_block.diff_block_simple([c.changes[fid]])}
150 ${diff_block.diff_block_simple([c.changes[fid]])}
151 %endfor
151 %endfor
152 % if c.limited_diff:
152 % if c.limited_diff:
153 <h4>${_('Changeset was too big and was cut off...')}</h4>
153 <h4>${_('Changeset was too big and was cut off...')}</h4>
154 % endif
154 % endif
155
155
156
156
157 ## template for inline comment form
157 ## template for inline comment form
158 <%namespace name="comment" file="/changeset/changeset_file_comment.html"/>
158 <%namespace name="comment" file="/changeset/changeset_file_comment.html"/>
159 ${comment.comment_inline_form()}
159 ${comment.comment_inline_form()}
160
160
161 ## render comments and inlines
161 ## render comments and inlines
162 ${comment.generate_comments()}
162 ${comment.generate_comments()}
163
163
164 % if not c.pull_request.is_closed():
164 % if not c.pull_request.is_closed():
165 ## main comment form and it status
165 ## main comment form and it status
166 ${comment.comments(h.url('pullrequest_comment', repo_name=c.repo_name,
166 ${comment.comments(h.url('pullrequest_comment', repo_name=c.repo_name,
167 pull_request_id=c.pull_request.pull_request_id),
167 pull_request_id=c.pull_request.pull_request_id),
168 c.current_changeset_status,
168 c.current_changeset_status,
169 close_btn=True)}
169 close_btn=True)}
170 %endif
170 %endif
171
171
172 <script type="text/javascript">
172 <script type="text/javascript">
173 YUE.onDOMReady(function(){
173 YUE.onDOMReady(function(){
174 PullRequestAutoComplete('user', 'reviewers_container', _USERS_AC_DATA, _GROUPS_AC_DATA);
174 PullRequestAutoComplete('user', 'reviewers_container', _USERS_AC_DATA, _GROUPS_AC_DATA);
175
175
176 YUE.on(YUQ('.show-inline-comments'),'change',function(e){
176 YUE.on(YUQ('.show-inline-comments'),'change',function(e){
177 var show = 'none';
177 var show = 'none';
178 var target = e.currentTarget;
178 var target = e.currentTarget;
179 if(target.checked){
179 if(target.checked){
180 var show = ''
180 var show = ''
181 }
181 }
182 var boxid = YUD.getAttribute(target,'id_for');
182 var boxid = YUD.getAttribute(target,'id_for');
183 var comments = YUQ('#{0} .inline-comments'.format(boxid));
183 var comments = YUQ('#{0} .inline-comments'.format(boxid));
184 for(c in comments){
184 for(c in comments){
185 YUD.setStyle(comments[c],'display',show);
185 YUD.setStyle(comments[c],'display',show);
186 }
186 }
187 var btns = YUQ('#{0} .inline-comments-button'.format(boxid));
187 var btns = YUQ('#{0} .inline-comments-button'.format(boxid));
188 for(c in btns){
188 for(c in btns){
189 YUD.setStyle(btns[c],'display',show);
189 YUD.setStyle(btns[c],'display',show);
190 }
190 }
191 })
191 })
192
192
193 YUE.on(YUQ('.line'),'click',function(e){
193 YUE.on(YUQ('.line'),'click',function(e){
194 var tr = e.currentTarget;
194 var tr = e.currentTarget;
195 injectInlineForm(tr);
195 injectInlineForm(tr);
196 });
196 });
197
197
198 // inject comments into they proper positions
198 // inject comments into they proper positions
199 var file_comments = YUQ('.inline-comment-placeholder');
199 var file_comments = YUQ('.inline-comment-placeholder');
200 renderInlineComments(file_comments);
200 renderInlineComments(file_comments);
201
201
202 YUE.on(YUD.get('update_pull_request'),'click',function(e){
202 YUE.on(YUD.get('update_pull_request'),'click',function(e){
203
203
204 var reviewers_ids = [];
204 var reviewers_ids = [];
205 var ids = YUQ('#review_members input');
205 var ids = YUQ('#review_members input');
206 for(var i=0; i<ids.length;i++){
206 for(var i=0; i<ids.length;i++){
207 var id = ids[i].value
207 var id = ids[i].value
208 reviewers_ids.push(id);
208 reviewers_ids.push(id);
209 }
209 }
210 updateReviewers(reviewers_ids);
210 updateReviewers(reviewers_ids);
211 })
211 })
212 })
212 })
213 </script>
213 </script>
214
214
215 </div>
215 </div>
216
216
217 </%def>
217 </%def>
@@ -1,93 +1,93 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 ${_('%s Tags') % c.repo_name} - ${c.rhodecode_name}
5 ${_('%s Tags') % c.repo_name} - ${c.rhodecode_name}
6 </%def>
6 </%def>
7
7
8
8
9 <%def name="breadcrumbs_links()">
9 <%def name="breadcrumbs_links()">
10 <input class="q_filter_box" id="q_filter_tags" size="15" type="text" name="filter" value="${_('quick filter...')}"/>
10 <input class="q_filter_box" id="q_filter_tags" size="15" type="text" name="filter" value="${_('quick filter...')}"/>
11 ${h.link_to(_(u'Home'),h.url('/'))}
11 ${h.link_to(_(u'Home'),h.url('/'))}
12 &raquo;
12 &raquo;
13 ${h.link_to(c.repo_name,h.url('summary_home',repo_name=c.repo_name))}
13 ${h.link_to(c.repo_name,h.url('summary_home',repo_name=c.repo_name))}
14 &raquo;
14 &raquo;
15 ${_('tags')}
15 ${_('tags')}
16 </%def>
16 </%def>
17
17
18 <%def name="page_nav()">
18 <%def name="page_nav()">
19 ${self.menu('tags')}
19 ${self.menu('tags')}
20 </%def>
20 </%def>
21 <%def name="main()">
21 <%def name="main()">
22 <div class="box">
22 <div class="box">
23 <!-- box / title -->
23 <!-- box / title -->
24 <div class="title">
24 <div class="title">
25 ${self.breadcrumbs()}
25 ${self.breadcrumbs()}
26 </div>
26 </div>
27 <!-- end box / title -->
27 <!-- end box / title -->
28 %if c.repo_tags:
28 %if c.repo_tags:
29 <div class="info_box" id="compare_tags" style="clear: both;padding: 10px 19px;vertical-align: right;text-align: right;"><a href="#" class="ui-btn small">${_('Compare tags')}</a></div>
29 <div class="info_box" id="compare_tags" style="clear: both;padding: 10px 19px;vertical-align: right;text-align: right;"><a href="#" class="ui-btn small">${_('Compare tags')}</a></div>
30 %endif
30 %endif
31 <div class="table">
31 <div class="table">
32 <%include file='tags_data.html'/>
32 <%include file='tags_data.html'/>
33 </div>
33 </div>
34 </div>
34 </div>
35 <script type="text/javascript">
35 <script type="text/javascript">
36 YUE.on('compare_tags','click',function(e){
36 YUE.on('compare_tags','click',function(e){
37 YUE.preventDefault(e);
37 YUE.preventDefault(e);
38 var org = YUQ('input[name=compare_org]:checked')[0];
38 var org = YUQ('input[name=compare_org]:checked')[0];
39 var other = YUQ('input[name=compare_other]:checked')[0];
39 var other = YUQ('input[name=compare_other]:checked')[0];
40
40
41 if(org && other){
41 if(org && other){
42 var compare_url = "${h.url('compare_url',repo_name=c.repo_name,org_ref_type='tag',org_ref='__ORG__',other_ref_type='tag',other_ref='__OTHER__')}";
42 var compare_url = "${h.url('compare_url',repo_name=c.repo_name,org_ref_type='tag',org_ref='__ORG__',other_ref_type='tag',other_ref='__OTHER__')}";
43 var u = compare_url.replace('__ORG__',org.value)
43 var u = compare_url.replace('__ORG__',org.value)
44 .replace('__OTHER__',other.value);
44 .replace('__OTHER__',other.value);
45 window.location=u;
45 window.location=u;
46 }
46 }
47 });
47 });
48
48
49 // main table sorting
49 // main table sorting
50 var myColumnDefs = [
50 var myColumnDefs = [
51 {key:"name",label:"${_('Name')}",sortable:true},
51 {key:"name",label:"${_('Name')}",sortable:true},
52 {key:"date",label:"${_('Date')}",sortable:true,
52 {key:"date",label:"${_('Date')}",sortable:true,
53 sortOptions: { sortFunction: dateSort }},
53 sortOptions: { sortFunction: dateSort }},
54 {key:"author",label:"${_('Author')}",sortable:true},
54 {key:"author",label:"${_('Author')}",sortable:true},
55 {key:"revision",label:"${_('Revision')}",sortable:true,
55 {key:"revision",label:"${_('Revision')}",sortable:true,
56 sortOptions: { sortFunction: revisionSort }},
56 sortOptions: { sortFunction: revisionSort }},
57 {key:"compare",label:"${_('Compare')}",sortable:false,},
57 {key:"compare",label:"${_('Compare')}",sortable:false,},
58 ];
58 ];
59
59
60 var myDataSource = new YAHOO.util.DataSource(YUD.get("tags_data"));
60 var myDataSource = new YAHOO.util.DataSource(YUD.get("tags_data"));
61
61
62 myDataSource.responseType = YAHOO.util.DataSource.TYPE_HTMLTABLE;
62 myDataSource.responseType = YAHOO.util.DataSource.TYPE_HTMLTABLE;
63
63
64 myDataSource.responseSchema = {
64 myDataSource.responseSchema = {
65 fields: [
65 fields: [
66 {key:"name"},
66 {key:"name"},
67 {key:"date"},
67 {key:"date"},
68 {key:"author"},
68 {key:"author"},
69 {key:"revision"},
69 {key:"revision"},
70 {key:"compare"},
70 {key:"compare"},
71 ]
71 ]
72 };
72 };
73
73
74 var myDataTable = new YAHOO.widget.DataTable("table_wrap", myColumnDefs, myDataSource,
74 var myDataTable = new YAHOO.widget.DataTable("table_wrap", myColumnDefs, myDataSource,
75 {
75 {
76 sortedBy:{key:"name",dir:"asc"},
76 sortedBy:{key:"name",dir:"asc"},
77 MSG_SORTASC:"${_('Click to sort ascending')}",
77 MSG_SORTASC:"${_('Click to sort ascending')}",
78 MSG_SORTDESC:"${_('Click to sort descending')}",
78 MSG_SORTDESC:"${_('Click to sort descending')}",
79 MSG_EMPTY:"${_('No records found.')}",
79 MSG_EMPTY:"${_('No records found.')}",
80 MSG_ERROR:"${_('Data error.')}",
80 MSG_ERROR:"${_('Data error.')}",
81 MSG_LOADING:"${_('Loading...')}",
81 MSG_LOADING:"${_('Loading...')}",
82 }
82 }
83 );
83 );
84 myDataTable.subscribe('postRenderEvent',function(oArgs) {
84 myDataTable.subscribe('postRenderEvent',function(oArgs) {
85 tooltip_activate();
85 tooltip_activate();
86 var func = function(node){
86 var func = function(node){
87 return node.parentNode.parentNode.parentNode.parentNode.parentNode;
87 return node.parentNode.parentNode.parentNode.parentNode.parentNode;
88 }
88 }
89 q_filter('q_filter_tags',YUQ('div.table tr td .logtags .tagtag a'),func);
89 q_filter('q_filter_tags',YUQ('div.table tr td .logtags .tagtag a'),func);
90 });
90 });
91
91
92 </script>
92 </script>
93 </%def>
93 </%def>
@@ -1,39 +1,39 b''
1 %if c.repo_tags:
1 %if c.repo_tags:
2 <div id="table_wrap" class="yui-skin-sam">
2 <div id="table_wrap" class="yui-skin-sam">
3 <table id="tags_data">
3 <table id="tags_data">
4 <thead>
4 <thead>
5 <tr>
5 <tr>
6 <th class="left">${_('Name')}</th>
6 <th class="left">${_('Name')}</th>
7 <th class="left">${_('Date')}</th>
7 <th class="left">${_('Date')}</th>
8 <th class="left">${_('Author')}</th>
8 <th class="left">${_('Author')}</th>
9 <th class="left">${_('Revision')}</th>
9 <th class="left">${_('Revision')}</th>
10 <th class="left">${_('Compare')}</th>
10 <th class="left">${_('Compare')}</th>
11 </tr>
11 </tr>
12 </thead>
12 </thead>
13 %for cnt,tag in enumerate(c.repo_tags.items()):
13 %for cnt,tag in enumerate(c.repo_tags.items()):
14 <tr class="parity${cnt%2}">
14 <tr class="parity${cnt%2}">
15 <td>
15 <td>
16 <span class="logtags">
16 <span class="logtags">
17 <span class="tagtag">${h.link_to(tag[0],
17 <span class="tagtag">${h.link_to(tag[0],
18 h.url('files_home',repo_name=c.repo_name,revision=tag[1].raw_id))}
18 h.url('files_home',repo_name=c.repo_name,revision=tag[1].raw_id))}
19 </span>
19 </span>
20 </span>
20 </span>
21 </td>
21 </td>
22 <td><span class="tooltip" title="${h.tooltip(h.age(tag[1].date))}">${h.fmt_date(tag[1].date)}</span></td>
22 <td><span class="tooltip" title="${h.tooltip(h.age(tag[1].date))}">${h.fmt_date(tag[1].date)}</span></td>
23 <td title="${tag[1].author}">${h.person(tag[1].author)}</td>
23 <td title="${tag[1].author}">${h.person(tag[1].author)}</td>
24 <td>
24 <td>
25 <div>
25 <div>
26 <pre><a href="${h.url('files_home',repo_name=c.repo_name,revision=tag[1].raw_id)}">r${tag[1].revision}:${h.short_id(tag[1].raw_id)}</a></pre>
26 <pre><a href="${h.url('files_home',repo_name=c.repo_name,revision=tag[1].raw_id)}">r${tag[1].revision}:${h.short_id(tag[1].raw_id)}</a></pre>
27 </div>
27 </div>
28 </td>
28 </td>
29 <td>
29 <td>
30 <input class="branch-compare" type="radio" name="compare_org" value="${tag[0]}"/>
30 <input class="branch-compare" type="radio" name="compare_org" value="${tag[0]}"/>
31 <input class="branch-compare" type="radio" name="compare_other" value="${tag[0]}"/>
31 <input class="branch-compare" type="radio" name="compare_other" value="${tag[0]}"/>
32 </td>
32 </td>
33 </tr>
33 </tr>
34 %endfor
34 %endfor
35 </table>
35 </table>
36 </div>
36 </div>
37 %else:
37 %else:
38 ${_('There are no tags yet')}
38 ${_('There are no tags yet')}
39 %endif
39 %endif
@@ -1,347 +1,347 b''
1 from rhodecode.tests import *
1 from rhodecode.tests import *
2 from rhodecode.model.repo import RepoModel
2 from rhodecode.model.repo import RepoModel
3 from rhodecode.model.meta import Session
3 from rhodecode.model.meta import Session
4 from rhodecode.model.db import Repository
4 from rhodecode.model.db import Repository
5 from rhodecode.model.scm import ScmModel
5 from rhodecode.model.scm import ScmModel
6 from rhodecode.lib.vcs.backends.base import EmptyChangeset
6 from rhodecode.lib.vcs.backends.base import EmptyChangeset
7
7
8
8
9 def _fork_repo(fork_name, vcs_type, parent=None):
9 def _fork_repo(fork_name, vcs_type, parent=None):
10 if vcs_type =='hg':
10 if vcs_type =='hg':
11 _REPO = HG_REPO
11 _REPO = HG_REPO
12 elif vcs_type == 'git':
12 elif vcs_type == 'git':
13 _REPO = GIT_REPO
13 _REPO = GIT_REPO
14
14
15 if parent:
15 if parent:
16 _REPO = parent
16 _REPO = parent
17
17
18 form_data = dict(
18 form_data = dict(
19 repo_name=fork_name,
19 repo_name=fork_name,
20 repo_name_full=fork_name,
20 repo_name_full=fork_name,
21 repo_group=None,
21 repo_group=None,
22 repo_type=vcs_type,
22 repo_type=vcs_type,
23 description='',
23 description='',
24 private=False,
24 private=False,
25 copy_permissions=False,
25 copy_permissions=False,
26 landing_rev='tip',
26 landing_rev='tip',
27 update_after_clone=False,
27 update_after_clone=False,
28 fork_parent_id=Repository.get_by_repo_name(_REPO),
28 fork_parent_id=Repository.get_by_repo_name(_REPO),
29 )
29 )
30 repo = RepoModel().create_fork(form_data, cur_user=TEST_USER_ADMIN_LOGIN)
30 repo = RepoModel().create_fork(form_data, cur_user=TEST_USER_ADMIN_LOGIN)
31
31
32 Session().commit()
32 Session().commit()
33 return Repository.get_by_repo_name(fork_name)
33 return Repository.get_by_repo_name(fork_name)
34
34
35
35
36 def _commit_change(repo, filename, content, message, vcs_type, parent=None, newfile=False):
36 def _commit_change(repo, filename, content, message, vcs_type, parent=None, newfile=False):
37 repo = Repository.get_by_repo_name(repo)
37 repo = Repository.get_by_repo_name(repo)
38 _cs = parent
38 _cs = parent
39 if not parent:
39 if not parent:
40 _cs = EmptyChangeset(alias=vcs_type)
40 _cs = EmptyChangeset(alias=vcs_type)
41
41
42 if newfile:
42 if newfile:
43 cs = ScmModel().create_node(
43 cs = ScmModel().create_node(
44 repo=repo.scm_instance, repo_name=repo.repo_name,
44 repo=repo.scm_instance, repo_name=repo.repo_name,
45 cs=_cs, user=TEST_USER_ADMIN_LOGIN,
45 cs=_cs, user=TEST_USER_ADMIN_LOGIN,
46 author=TEST_USER_ADMIN_LOGIN,
46 author=TEST_USER_ADMIN_LOGIN,
47 message=message,
47 message=message,
48 content=content,
48 content=content,
49 f_path=filename
49 f_path=filename
50 )
50 )
51 else:
51 else:
52 cs = ScmModel().commit_change(
52 cs = ScmModel().commit_change(
53 repo=repo.scm_instance, repo_name=repo.repo_name,
53 repo=repo.scm_instance, repo_name=repo.repo_name,
54 cs=parent, user=TEST_USER_ADMIN_LOGIN,
54 cs=parent, user=TEST_USER_ADMIN_LOGIN,
55 author=TEST_USER_ADMIN_LOGIN,
55 author=TEST_USER_ADMIN_LOGIN,
56 message=message,
56 message=message,
57 content=content,
57 content=content,
58 f_path=filename
58 f_path=filename
59 )
59 )
60 return cs
60 return cs
61
61
62
62
63 class TestCompareController(TestController):
63 class TestCompareController(TestController):
64
64
65 def test_compare_forks_on_branch_extra_commits_hg(self):
65 def test_compare_forks_on_branch_extra_commits_hg(self):
66 self.log_user()
66 self.log_user()
67
67
68 repo1 = RepoModel().create_repo(repo_name='one', repo_type='hg',
68 repo1 = RepoModel().create_repo(repo_name='one', repo_type='hg',
69 description='diff-test',
69 description='diff-test',
70 owner=TEST_USER_ADMIN_LOGIN)
70 owner=TEST_USER_ADMIN_LOGIN)
71 r1_id = repo1.repo_id
71 r1_id = repo1.repo_id
72 Session().commit()
72 Session().commit()
73 #commit something !
73 #commit something !
74 cs0 = _commit_change(repo1.repo_name, filename='file1', content='line1\n',
74 cs0 = _commit_change(repo1.repo_name, filename='file1', content='line1\n',
75 message='commit1', vcs_type='hg', parent=None, newfile=True)
75 message='commit1', vcs_type='hg', parent=None, newfile=True)
76
76
77 #fork this repo
77 #fork this repo
78 repo2 = _fork_repo('one-fork', 'hg', parent='one')
78 repo2 = _fork_repo('one-fork', 'hg', parent='one')
79 Session().commit()
79 Session().commit()
80 r2_id = repo2.repo_id
80 r2_id = repo2.repo_id
81
81
82 #add two extra commit into fork
82 #add two extra commit into fork
83 cs1 = _commit_change(repo2.repo_name, filename='file1', content='line1\nline2\n',
83 cs1 = _commit_change(repo2.repo_name, filename='file1', content='line1\nline2\n',
84 message='commit2', vcs_type='hg', parent=cs0)
84 message='commit2', vcs_type='hg', parent=cs0)
85
85
86 cs2 = _commit_change(repo2.repo_name, filename='file1', content='line1\nline2\nline3\n',
86 cs2 = _commit_change(repo2.repo_name, filename='file1', content='line1\nline2\nline3\n',
87 message='commit3', vcs_type='hg', parent=cs1)
87 message='commit3', vcs_type='hg', parent=cs1)
88
88
89 rev1 = 'default'
89 rev1 = 'default'
90 rev2 = 'default'
90 rev2 = 'default'
91 response = self.app.get(url(controller='compare', action='index',
91 response = self.app.get(url(controller='compare', action='index',
92 repo_name=repo2.repo_name,
92 repo_name=repo2.repo_name,
93 org_ref_type="branch",
93 org_ref_type="branch",
94 org_ref=rev1,
94 org_ref=rev1,
95 other_ref_type="branch",
95 other_ref_type="branch",
96 other_ref=rev2,
96 other_ref=rev2,
97 repo=repo1.repo_name
97 repo=repo1.repo_name
98 ))
98 ))
99
99
100 try:
100 try:
101 response.mustcontain('%s@%s -> %s@%s' % (repo2.repo_name, rev1, repo1.repo_name, rev2))
101 response.mustcontain('%s@%s -> %s@%s' % (repo2.repo_name, rev1, repo1.repo_name, rev2))
102 response.mustcontain("""Showing 2 commits""")
102 response.mustcontain("""Showing 2 commits""")
103 response.mustcontain("""1 file changed with 2 insertions and 0 deletions""")
103 response.mustcontain("""1 file changed with 2 insertions and 0 deletions""")
104
104
105 response.mustcontain("""<div class="message tooltip" title="commit2" style="white-space:normal">commit2</div>""")
105 response.mustcontain("""<div class="message tooltip" title="commit2" style="white-space:normal">commit2</div>""")
106 response.mustcontain("""<div class="message tooltip" title="commit3" style="white-space:normal">commit3</div>""")
106 response.mustcontain("""<div class="message tooltip" title="commit3" style="white-space:normal">commit3</div>""")
107
107
108 response.mustcontain("""<a href="/%s/changeset/%s">r1:%s</a>""" % (repo2.repo_name, cs1.raw_id, cs1.short_id))
108 response.mustcontain("""<a href="/%s/changeset/%s">r1:%s</a>""" % (repo2.repo_name, cs1.raw_id, cs1.short_id))
109 response.mustcontain("""<a href="/%s/changeset/%s">r2:%s</a>""" % (repo2.repo_name, cs2.raw_id, cs2.short_id))
109 response.mustcontain("""<a href="/%s/changeset/%s">r2:%s</a>""" % (repo2.repo_name, cs2.raw_id, cs2.short_id))
110 ## files
110 ## files
111 response.mustcontain("""<a href="/%s/compare/branch@%s...branch@%s#C--826e8142e6ba">file1</a>""" % (repo2.repo_name, rev1, rev2))
111 response.mustcontain("""<a href="/%s/compare/branch@%s...branch@%s#C--826e8142e6ba">file1</a>""" % (repo2.repo_name, rev1, rev2))
112
112
113 finally:
113 finally:
114 RepoModel().delete(r2_id)
114 RepoModel().delete(r2_id)
115 RepoModel().delete(r1_id)
115 RepoModel().delete(r1_id)
116
116
117
117
118 def test_compare_forks_on_branch_extra_commits_origin_has_incomming_hg(self):
118 def test_compare_forks_on_branch_extra_commits_origin_has_incomming_hg(self):
119 self.log_user()
119 self.log_user()
120
120
121 repo1 = RepoModel().create_repo(repo_name='one', repo_type='hg',
121 repo1 = RepoModel().create_repo(repo_name='one', repo_type='hg',
122 description='diff-test',
122 description='diff-test',
123 owner=TEST_USER_ADMIN_LOGIN)
123 owner=TEST_USER_ADMIN_LOGIN)
124 r1_id = repo1.repo_id
124 r1_id = repo1.repo_id
125 Session().commit()
125 Session().commit()
126 #commit something !
126 #commit something !
127 cs0 = _commit_change(repo1.repo_name, filename='file1', content='line1\n',
127 cs0 = _commit_change(repo1.repo_name, filename='file1', content='line1\n',
128 message='commit1', vcs_type='hg', parent=None, newfile=True)
128 message='commit1', vcs_type='hg', parent=None, newfile=True)
129
129
130 #fork this repo
130 #fork this repo
131 repo2 = _fork_repo('one-fork', 'hg', parent='one')
131 repo2 = _fork_repo('one-fork', 'hg', parent='one')
132 Session().commit()
132 Session().commit()
133
133
134 #now commit something to origin repo
134 #now commit something to origin repo
135 cs1_prim = _commit_change(repo1.repo_name, filename='file2', content='line1file2\n',
135 cs1_prim = _commit_change(repo1.repo_name, filename='file2', content='line1file2\n',
136 message='commit2', vcs_type='hg', parent=cs0, newfile=True)
136 message='commit2', vcs_type='hg', parent=cs0, newfile=True)
137
137
138 r2_id = repo2.repo_id
138 r2_id = repo2.repo_id
139
139
140 #add two extra commit into fork
140 #add two extra commit into fork
141 cs1 = _commit_change(repo2.repo_name, filename='file1', content='line1\nline2\n',
141 cs1 = _commit_change(repo2.repo_name, filename='file1', content='line1\nline2\n',
142 message='commit2', vcs_type='hg', parent=cs0)
142 message='commit2', vcs_type='hg', parent=cs0)
143
143
144 cs2 = _commit_change(repo2.repo_name, filename='file1', content='line1\nline2\nline3\n',
144 cs2 = _commit_change(repo2.repo_name, filename='file1', content='line1\nline2\nline3\n',
145 message='commit3', vcs_type='hg', parent=cs1)
145 message='commit3', vcs_type='hg', parent=cs1)
146
146
147 rev1 = 'default'
147 rev1 = 'default'
148 rev2 = 'default'
148 rev2 = 'default'
149 response = self.app.get(url(controller='compare', action='index',
149 response = self.app.get(url(controller='compare', action='index',
150 repo_name=repo2.repo_name,
150 repo_name=repo2.repo_name,
151 org_ref_type="branch",
151 org_ref_type="branch",
152 org_ref=rev1,
152 org_ref=rev1,
153 other_ref_type="branch",
153 other_ref_type="branch",
154 other_ref=rev2,
154 other_ref=rev2,
155 repo=repo1.repo_name
155 repo=repo1.repo_name
156 ))
156 ))
157
157
158 try:
158 try:
159 response.mustcontain('%s@%s -> %s@%s' % (repo2.repo_name, rev1, repo1.repo_name, rev2))
159 response.mustcontain('%s@%s -> %s@%s' % (repo2.repo_name, rev1, repo1.repo_name, rev2))
160 response.mustcontain("""Showing 2 commits""")
160 response.mustcontain("""Showing 2 commits""")
161 response.mustcontain("""1 file changed with 2 insertions and 0 deletions""")
161 response.mustcontain("""1 file changed with 2 insertions and 0 deletions""")
162
162
163 response.mustcontain("""<div class="message tooltip" title="commit2" style="white-space:normal">commit2</div>""")
163 response.mustcontain("""<div class="message tooltip" title="commit2" style="white-space:normal">commit2</div>""")
164 response.mustcontain("""<div class="message tooltip" title="commit3" style="white-space:normal">commit3</div>""")
164 response.mustcontain("""<div class="message tooltip" title="commit3" style="white-space:normal">commit3</div>""")
165
165
166 response.mustcontain("""<a href="/%s/changeset/%s">r1:%s</a>""" % (repo2.repo_name, cs1.raw_id, cs1.short_id))
166 response.mustcontain("""<a href="/%s/changeset/%s">r1:%s</a>""" % (repo2.repo_name, cs1.raw_id, cs1.short_id))
167 response.mustcontain("""<a href="/%s/changeset/%s">r2:%s</a>""" % (repo2.repo_name, cs2.raw_id, cs2.short_id))
167 response.mustcontain("""<a href="/%s/changeset/%s">r2:%s</a>""" % (repo2.repo_name, cs2.raw_id, cs2.short_id))
168 ## files
168 ## files
169 response.mustcontain("""<a href="/%s/compare/branch@%s...branch@%s#C--826e8142e6ba">file1</a>""" % (repo2.repo_name, rev1, rev2))
169 response.mustcontain("""<a href="/%s/compare/branch@%s...branch@%s#C--826e8142e6ba">file1</a>""" % (repo2.repo_name, rev1, rev2))
170
170
171 finally:
171 finally:
172 RepoModel().delete(r2_id)
172 RepoModel().delete(r2_id)
173 RepoModel().delete(r1_id)
173 RepoModel().delete(r1_id)
174
174
175
175
176 # def test_compare_remote_repos_remote_flag_off(self):
176 # def test_compare_remote_repos_remote_flag_off(self):
177 # self.log_user()
177 # self.log_user()
178 # _fork_repo(HG_FORK, 'hg')
178 # _fork_repo(HG_FORK, 'hg')
179 #
179 #
180 # rev1 = '56349e29c2af'
180 # rev1 = '56349e29c2af'
181 # rev2 = '7d4bc8ec6be5'
181 # rev2 = '7d4bc8ec6be5'
182 #
182 #
183 # response = self.app.get(url(controller='compare', action='index',
183 # response = self.app.get(url(controller='compare', action='index',
184 # repo_name=HG_REPO,
184 # repo_name=HG_REPO,
185 # org_ref_type="rev",
185 # org_ref_type="rev",
186 # org_ref=rev1,
186 # org_ref=rev1,
187 # other_ref_type="rev",
187 # other_ref_type="rev",
188 # other_ref=rev2,
188 # other_ref=rev2,
189 # repo=HG_FORK,
189 # repo=HG_FORK,
190 # bundle=False,
190 # bundle=False,
191 # ))
191 # ))
192 #
192 #
193 # try:
193 # try:
194 # response.mustcontain('%s@%s -> %s@%s' % (HG_REPO, rev1, HG_FORK, rev2))
194 # response.mustcontain('%s@%s -> %s@%s' % (HG_REPO, rev1, HG_FORK, rev2))
195 # ## outgoing changesets between those revisions
195 # ## outgoing changesets between those revisions
196 #
196 #
197 # response.mustcontain("""<a href="/%s/changeset/2dda4e345facb0ccff1a191052dd1606dba6781d">r4:2dda4e345fac</a>""" % (HG_REPO))
197 # response.mustcontain("""<a href="/%s/changeset/2dda4e345facb0ccff1a191052dd1606dba6781d">r4:2dda4e345fac</a>""" % (HG_REPO))
198 # response.mustcontain("""<a href="/%s/changeset/6fff84722075f1607a30f436523403845f84cd9e">r5:6fff84722075</a>""" % (HG_REPO))
198 # response.mustcontain("""<a href="/%s/changeset/6fff84722075f1607a30f436523403845f84cd9e">r5:6fff84722075</a>""" % (HG_REPO))
199 # response.mustcontain("""<a href="/%s/changeset/7d4bc8ec6be56c0f10425afb40b6fc315a4c25e7">r6:%s</a>""" % (HG_REPO, rev2))
199 # response.mustcontain("""<a href="/%s/changeset/7d4bc8ec6be56c0f10425afb40b6fc315a4c25e7">r6:%s</a>""" % (HG_REPO, rev2))
200 #
200 #
201 # ## files
201 # ## files
202 # response.mustcontain("""<a href="/%s/compare/rev@%s...rev@%s#C--9c390eb52cd6">vcs/backends/hg.py</a>""" % (HG_REPO, rev1, rev2))
202 # response.mustcontain("""<a href="/%s/compare/rev@%s...rev@%s#C--9c390eb52cd6">vcs/backends/hg.py</a>""" % (HG_REPO, rev1, rev2))
203 # response.mustcontain("""<a href="/%s/compare/rev@%s...rev@%s#C--41b41c1f2796">vcs/backends/__init__.py</a>""" % (HG_REPO, rev1, rev2))
203 # response.mustcontain("""<a href="/%s/compare/rev@%s...rev@%s#C--41b41c1f2796">vcs/backends/__init__.py</a>""" % (HG_REPO, rev1, rev2))
204 # response.mustcontain("""<a href="/%s/compare/rev@%s...rev@%s#C--2f574d260608">vcs/backends/base.py</a>""" % (HG_REPO, rev1, rev2))
204 # response.mustcontain("""<a href="/%s/compare/rev@%s...rev@%s#C--2f574d260608">vcs/backends/base.py</a>""" % (HG_REPO, rev1, rev2))
205 # finally:
205 # finally:
206 # RepoModel().delete(HG_FORK)
206 # RepoModel().delete(HG_FORK)
207
207
208
208
209
209
210 #
210 #
211 # def test_compare_remote_branches_hg(self):
211 # def test_compare_remote_branches_hg(self):
212 # self.log_user()
212 # self.log_user()
213 #
213 #
214 # _fork_repo(HG_FORK, 'hg')
214 # _fork_repo(HG_FORK, 'hg')
215 #
215 #
216 # rev1 = '56349e29c2af'
216 # rev1 = '56349e29c2af'
217 # rev2 = '7d4bc8ec6be5'
217 # rev2 = '7d4bc8ec6be5'
218 #
218 #
219 # response = self.app.get(url(controller='compare', action='index',
219 # response = self.app.get(url(controller='compare', action='index',
220 # repo_name=HG_REPO,
220 # repo_name=HG_REPO,
221 # org_ref_type="rev",
221 # org_ref_type="rev",
222 # org_ref=rev1,
222 # org_ref=rev1,
223 # other_ref_type="rev",
223 # other_ref_type="rev",
224 # other_ref=rev2,
224 # other_ref=rev2,
225 # repo=HG_FORK,
225 # repo=HG_FORK,
226 # ))
226 # ))
227 #
227 #
228 # try:
228 # try:
229 # response.mustcontain('%s@%s -> %s@%s' % (HG_REPO, rev1, HG_FORK, rev2))
229 # response.mustcontain('%s@%s -> %s@%s' % (HG_REPO, rev1, HG_FORK, rev2))
230 # ## outgoing changesets between those revisions
230 # ## outgoing changesets between those revisions
231 #
231 #
232 # response.mustcontain("""<a href="/%s/changeset/2dda4e345facb0ccff1a191052dd1606dba6781d">r4:2dda4e345fac</a>""" % (HG_REPO))
232 # response.mustcontain("""<a href="/%s/changeset/2dda4e345facb0ccff1a191052dd1606dba6781d">r4:2dda4e345fac</a>""" % (HG_REPO))
233 # response.mustcontain("""<a href="/%s/changeset/6fff84722075f1607a30f436523403845f84cd9e">r5:6fff84722075</a>""" % (HG_REPO))
233 # response.mustcontain("""<a href="/%s/changeset/6fff84722075f1607a30f436523403845f84cd9e">r5:6fff84722075</a>""" % (HG_REPO))
234 # response.mustcontain("""<a href="/%s/changeset/7d4bc8ec6be56c0f10425afb40b6fc315a4c25e7">r6:%s</a>""" % (HG_REPO, rev2))
234 # response.mustcontain("""<a href="/%s/changeset/7d4bc8ec6be56c0f10425afb40b6fc315a4c25e7">r6:%s</a>""" % (HG_REPO, rev2))
235 #
235 #
236 # ## files
236 # ## files
237 # response.mustcontain("""<a href="/%s/compare/rev@%s...rev@%s#C--9c390eb52cd6">vcs/backends/hg.py</a>""" % (HG_REPO, rev1, rev2))
237 # response.mustcontain("""<a href="/%s/compare/rev@%s...rev@%s#C--9c390eb52cd6">vcs/backends/hg.py</a>""" % (HG_REPO, rev1, rev2))
238 # response.mustcontain("""<a href="/%s/compare/rev@%s...rev@%s#C--41b41c1f2796">vcs/backends/__init__.py</a>""" % (HG_REPO, rev1, rev2))
238 # response.mustcontain("""<a href="/%s/compare/rev@%s...rev@%s#C--41b41c1f2796">vcs/backends/__init__.py</a>""" % (HG_REPO, rev1, rev2))
239 # response.mustcontain("""<a href="/%s/compare/rev@%s...rev@%s#C--2f574d260608">vcs/backends/base.py</a>""" % (HG_REPO, rev1, rev2))
239 # response.mustcontain("""<a href="/%s/compare/rev@%s...rev@%s#C--2f574d260608">vcs/backends/base.py</a>""" % (HG_REPO, rev1, rev2))
240 # finally:
240 # finally:
241 # RepoModel().delete(HG_FORK)
241 # RepoModel().delete(HG_FORK)
242 #
242 #
243 # def test_org_repo_new_commits_after_forking_simple_diff(self):
243 # def test_org_repo_new_commits_after_forking_simple_diff(self):
244 # self.log_user()
244 # self.log_user()
245 #
245 #
246 # repo1 = RepoModel().create_repo(repo_name='one', repo_type='hg',
246 # repo1 = RepoModel().create_repo(repo_name='one', repo_type='hg',
247 # description='diff-test',
247 # description='diff-test',
248 # owner=TEST_USER_ADMIN_LOGIN)
248 # owner=TEST_USER_ADMIN_LOGIN)
249 #
249 #
250 # Session().commit()
250 # Session().commit()
251 # r1_id = repo1.repo_id
251 # r1_id = repo1.repo_id
252 # r1_name = repo1.repo_name
252 # r1_name = repo1.repo_name
253 #
253 #
254 # #commit something initially !
254 # #commit something initially !
255 # cs0 = ScmModel().create_node(
255 # cs0 = ScmModel().create_node(
256 # repo=repo1.scm_instance, repo_name=r1_name,
256 # repo=repo1.scm_instance, repo_name=r1_name,
257 # cs=EmptyChangeset(alias='hg'), user=TEST_USER_ADMIN_LOGIN,
257 # cs=EmptyChangeset(alias='hg'), user=TEST_USER_ADMIN_LOGIN,
258 # author=TEST_USER_ADMIN_LOGIN,
258 # author=TEST_USER_ADMIN_LOGIN,
259 # message='commit1',
259 # message='commit1',
260 # content='line1',
260 # content='line1',
261 # f_path='file1'
261 # f_path='file1'
262 # )
262 # )
263 # Session().commit()
263 # Session().commit()
264 # self.assertEqual(repo1.scm_instance.revisions, [cs0.raw_id])
264 # self.assertEqual(repo1.scm_instance.revisions, [cs0.raw_id])
265 # #fork the repo1
265 # #fork the repo1
266 # repo2 = RepoModel().create_repo(repo_name='one-fork', repo_type='hg',
266 # repo2 = RepoModel().create_repo(repo_name='one-fork', repo_type='hg',
267 # description='compare-test',
267 # description='compare-test',
268 # clone_uri=repo1.repo_full_path,
268 # clone_uri=repo1.repo_full_path,
269 # owner=TEST_USER_ADMIN_LOGIN, fork_of='one')
269 # owner=TEST_USER_ADMIN_LOGIN, fork_of='one')
270 # Session().commit()
270 # Session().commit()
271 # self.assertEqual(repo2.scm_instance.revisions, [cs0.raw_id])
271 # self.assertEqual(repo2.scm_instance.revisions, [cs0.raw_id])
272 # r2_id = repo2.repo_id
272 # r2_id = repo2.repo_id
273 # r2_name = repo2.repo_name
273 # r2_name = repo2.repo_name
274 #
274 #
275 # #make 3 new commits in fork
275 # #make 3 new commits in fork
276 # cs1 = ScmModel().create_node(
276 # cs1 = ScmModel().create_node(
277 # repo=repo2.scm_instance, repo_name=r2_name,
277 # repo=repo2.scm_instance, repo_name=r2_name,
278 # cs=repo2.scm_instance[-1], user=TEST_USER_ADMIN_LOGIN,
278 # cs=repo2.scm_instance[-1], user=TEST_USER_ADMIN_LOGIN,
279 # author=TEST_USER_ADMIN_LOGIN,
279 # author=TEST_USER_ADMIN_LOGIN,
280 # message='commit1-fork',
280 # message='commit1-fork',
281 # content='file1-line1-from-fork',
281 # content='file1-line1-from-fork',
282 # f_path='file1-fork'
282 # f_path='file1-fork'
283 # )
283 # )
284 # cs2 = ScmModel().create_node(
284 # cs2 = ScmModel().create_node(
285 # repo=repo2.scm_instance, repo_name=r2_name,
285 # repo=repo2.scm_instance, repo_name=r2_name,
286 # cs=cs1, user=TEST_USER_ADMIN_LOGIN,
286 # cs=cs1, user=TEST_USER_ADMIN_LOGIN,
287 # author=TEST_USER_ADMIN_LOGIN,
287 # author=TEST_USER_ADMIN_LOGIN,
288 # message='commit2-fork',
288 # message='commit2-fork',
289 # content='file2-line1-from-fork',
289 # content='file2-line1-from-fork',
290 # f_path='file2-fork'
290 # f_path='file2-fork'
291 # )
291 # )
292 # cs3 = ScmModel().create_node(
292 # cs3 = ScmModel().create_node(
293 # repo=repo2.scm_instance, repo_name=r2_name,
293 # repo=repo2.scm_instance, repo_name=r2_name,
294 # cs=cs2, user=TEST_USER_ADMIN_LOGIN,
294 # cs=cs2, user=TEST_USER_ADMIN_LOGIN,
295 # author=TEST_USER_ADMIN_LOGIN,
295 # author=TEST_USER_ADMIN_LOGIN,
296 # message='commit3-fork',
296 # message='commit3-fork',
297 # content='file3-line1-from-fork',
297 # content='file3-line1-from-fork',
298 # f_path='file3-fork'
298 # f_path='file3-fork'
299 # )
299 # )
300 #
300 #
301 # #compare !
301 # #compare !
302 # rev1 = 'default'
302 # rev1 = 'default'
303 # rev2 = 'default'
303 # rev2 = 'default'
304 # response = self.app.get(url(controller='compare', action='index',
304 # response = self.app.get(url(controller='compare', action='index',
305 # repo_name=r2_name,
305 # repo_name=r2_name,
306 # org_ref_type="branch",
306 # org_ref_type="branch",
307 # org_ref=rev1,
307 # org_ref=rev1,
308 # other_ref_type="branch",
308 # other_ref_type="branch",
309 # other_ref=rev2,
309 # other_ref=rev2,
310 # repo=r1_name,
310 # repo=r1_name,
311 # bundle=False,
311 # bundle=False,
312 # ))
312 # ))
313 #
313 #
314 # try:
314 # try:
315 # #response.mustcontain('%s@%s -> %s@%s' % (r2_name, rev1, r1_name, rev2))
315 # #response.mustcontain('%s@%s -> %s@%s' % (r2_name, rev1, r1_name, rev2))
316 #
316 #
317 # #add new commit into parent !
317 # #add new commit into parent !
318 # cs0 = ScmModel().create_node(
318 # cs0 = ScmModel().create_node(
319 # repo=repo1.scm_instance, repo_name=r1_name,
319 # repo=repo1.scm_instance, repo_name=r1_name,
320 # cs=EmptyChangeset(alias='hg'), user=TEST_USER_ADMIN_LOGIN,
320 # cs=EmptyChangeset(alias='hg'), user=TEST_USER_ADMIN_LOGIN,
321 # author=TEST_USER_ADMIN_LOGIN,
321 # author=TEST_USER_ADMIN_LOGIN,
322 # message='commit2',
322 # message='commit2',
323 # content='line1',
323 # content='line1',
324 # f_path='file2'
324 # f_path='file2'
325 # )
325 # )
326 # #compare !
326 # #compare !
327 # rev1 = 'default'
327 # rev1 = 'default'
328 # rev2 = 'default'
328 # rev2 = 'default'
329 # response = self.app.get(url(controller='compare', action='index',
329 # response = self.app.get(url(controller='compare', action='index',
330 # repo_name=r2_name,
330 # repo_name=r2_name,
331 # org_ref_type="branch",
331 # org_ref_type="branch",
332 # org_ref=rev1,
332 # org_ref=rev1,
333 # other_ref_type="branch",
333 # other_ref_type="branch",
334 # other_ref=rev2,
334 # other_ref=rev2,
335 # repo=r1_name,
335 # repo=r1_name,
336 # bundle=False
336 # bundle=False
337 # ))
337 # ))
338 #
338 #
339 # response.mustcontain('%s@%s -> %s@%s' % (r2_name, rev1, r1_name, rev2))
339 # response.mustcontain('%s@%s -> %s@%s' % (r2_name, rev1, r1_name, rev2))
340 # response.mustcontain("""file1-line1-from-fork""")
340 # response.mustcontain("""file1-line1-from-fork""")
341 # response.mustcontain("""file2-line1-from-fork""")
341 # response.mustcontain("""file2-line1-from-fork""")
342 # response.mustcontain("""file3-line1-from-fork""")
342 # response.mustcontain("""file3-line1-from-fork""")
343 # self.assertFalse("""<a href="#">file2</a>""" in response.body) # new commit from parent
343 # self.assertFalse("""<a href="#">file2</a>""" in response.body) # new commit from parent
344 # self.assertFalse("""line1-from-new-parent""" in response.body)
344 # self.assertFalse("""line1-from-new-parent""" in response.body)
345 # finally:
345 # finally:
346 # RepoModel().delete(r2_id)
346 # RepoModel().delete(r2_id)
347 # RepoModel().delete(r1_id)
347 # RepoModel().delete(r1_id)
General Comments 0
You need to be logged in to leave comments. Login now