##// END OF EJS Templates
routing: removed more usage of pylons url() objects.
marcink -
r2103:c8a23404 default
parent child Browse files
Show More
@@ -1,58 +1,59 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2017 RhodeCode GmbH
3 # Copyright (C) 2016-2017 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import os
21 import os
22 import logging
22 import logging
23
23
24 from pyramid.view import view_config
24 from pyramid.view import view_config
25 from pyramid.renderers import render_to_response
25 from pyramid.renderers import render_to_response
26 from rhodecode.apps._base import BaseAppView
26 from rhodecode.apps._base import BaseAppView
27
27
28 log = logging.getLogger(__name__)
28 log = logging.getLogger(__name__)
29
29
30
30
31 class DebugStyleView(BaseAppView):
31 class DebugStyleView(BaseAppView):
32 def load_default_context(self):
32 def load_default_context(self):
33 c = self._get_local_tmpl_context()
33 c = self._get_local_tmpl_context()
34 self._register_global_c(c)
34 self._register_global_c(c)
35 return c
35 return c
36
36
37 @view_config(
37 @view_config(
38 route_name='debug_style_home', request_method='GET',
38 route_name='debug_style_home', request_method='GET',
39 renderer=None)
39 renderer=None)
40 def index(self):
40 def index(self):
41 c = self.load_default_context()
41 c = self.load_default_context()
42 c.active = 'index'
42 c.active = 'index'
43
43
44 return render_to_response(
44 return render_to_response(
45 'debug_style/index.html', self._get_template_context(c),
45 'debug_style/index.html', self._get_template_context(c),
46 request=self.request)
46 request=self.request)
47
47
48 @view_config(
48 @view_config(
49 route_name='debug_style_template', request_method='GET',
49 route_name='debug_style_template', request_method='GET',
50 renderer=None)
50 renderer=None)
51 def template(self):
51 def template(self):
52 t_path = self.request.matchdict['t_path']
52 t_path = self.request.matchdict['t_path']
53 c = self.load_default_context()
53 c = self.load_default_context()
54 c.active = os.path.splitext(t_path)[0]
54 c.active = os.path.splitext(t_path)[0]
55 c.came_from = ''
55
56
56 return render_to_response(
57 return render_to_response(
57 'debug_style/' + t_path, self._get_template_context(c),
58 'debug_style/' + t_path, self._get_template_context(c),
58 request=self.request) No newline at end of file
59 request=self.request)
@@ -1,106 +1,106 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2017 RhodeCode GmbH
3 # Copyright (C) 2010-2017 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 """
21 """
22 Single source for redirection links.
22 Single source for redirection links.
23
23
24 Goal of this module is to provide a single source of truth regarding external
24 Goal of this module is to provide a single source of truth regarding external
25 links. The data inside this module is used to configure the routing
25 links. The data inside this module is used to configure the routing
26 system of Enterprise and it is used also as a base to check if this data
26 system of Enterprise and it is used also as a base to check if this data
27 and our server configuration are in sync.
27 and our server configuration are in sync.
28
28
29 .. py:data:: link_config
29 .. py:data:: link_config
30
30
31 Contains the configuration for external links. Each item is supposed to be
31 Contains the configuration for external links. Each item is supposed to be
32 a `dict` like this example::
32 a `dict` like this example::
33
33
34 {"name": "url_name",
34 {"name": "url_name",
35 "target": "https://rhodecode.com/r1/enterprise/keyword/",
35 "target": "https://rhodecode.com/r1/enterprise/keyword/",
36 "external_target": "https://example.com/some-page.html",
36 "external_target": "https://example.com/some-page.html",
37 }
37 }
38
38
39 then you can retrieve the url by simply calling the URL function:
39 then you can retrieve the url by simply calling the URL function:
40
40
41 `h.url('url_name')`
41 `h.route_path('url_name')`
42
42
43 The redirection must be first implemented in our servers before
43 The redirection must be first implemented in our servers before
44 you can see it working.
44 you can see it working.
45 """
45 """
46 # flake8: noqa
46 # flake8: noqa
47 from __future__ import unicode_literals
47 from __future__ import unicode_literals
48
48
49 link_config = [
49 link_config = [
50 {
50 {
51 "name": "enterprise_docs",
51 "name": "enterprise_docs",
52 "target": "https://rhodecode.com/r1/enterprise/docs/",
52 "target": "https://rhodecode.com/r1/enterprise/docs/",
53 "external_target": "https://docs.rhodecode.com/RhodeCode-Enterprise/",
53 "external_target": "https://docs.rhodecode.com/RhodeCode-Enterprise/",
54 },
54 },
55 {
55 {
56 "name": "enterprise_log_file_locations",
56 "name": "enterprise_log_file_locations",
57 "target": "https://rhodecode.com/r1/enterprise/docs/admin-system-overview/",
57 "target": "https://rhodecode.com/r1/enterprise/docs/admin-system-overview/",
58 "external_target": "https://docs.rhodecode.com/RhodeCode-Enterprise/admin/system-overview.html#log-files",
58 "external_target": "https://docs.rhodecode.com/RhodeCode-Enterprise/admin/system-overview.html#log-files",
59 },
59 },
60 {
60 {
61 "name": "enterprise_issue_tracker_settings",
61 "name": "enterprise_issue_tracker_settings",
62 "target": "https://rhodecode.com/r1/enterprise/docs/issue-trackers-overview/",
62 "target": "https://rhodecode.com/r1/enterprise/docs/issue-trackers-overview/",
63 "external_target": "https://docs.rhodecode.com/RhodeCode-Enterprise/issue-trackers/issue-trackers.html",
63 "external_target": "https://docs.rhodecode.com/RhodeCode-Enterprise/issue-trackers/issue-trackers.html",
64 },
64 },
65 {
65 {
66 "name": "enterprise_svn_setup",
66 "name": "enterprise_svn_setup",
67 "target": "https://rhodecode.com/r1/enterprise/docs/svn-setup/",
67 "target": "https://rhodecode.com/r1/enterprise/docs/svn-setup/",
68 "external_target": "https://docs.rhodecode.com/RhodeCode-Enterprise/admin/svn-http.html",
68 "external_target": "https://docs.rhodecode.com/RhodeCode-Enterprise/admin/svn-http.html",
69 },
69 },
70 {
70 {
71 "name": "enterprise_license_convert_from_old",
71 "name": "enterprise_license_convert_from_old",
72 "target": "https://rhodecode.com/r1/enterprise/convert-license/",
72 "target": "https://rhodecode.com/r1/enterprise/convert-license/",
73 "external_target": "https://rhodecode.com/u/license-upgrade",
73 "external_target": "https://rhodecode.com/u/license-upgrade",
74 },
74 },
75 {
75 {
76 "name": "rst_help",
76 "name": "rst_help",
77 "target": "http://docutils.sourceforge.net/docs/user/rst/quickref.html",
77 "target": "http://docutils.sourceforge.net/docs/user/rst/quickref.html",
78 "external_target": "http://docutils.sourceforge.net/docs/user/rst/quickref.html",
78 "external_target": "http://docutils.sourceforge.net/docs/user/rst/quickref.html",
79 },
79 },
80 {
80 {
81 "name": "markdown_help",
81 "name": "markdown_help",
82 "target": "https://daringfireball.net/projects/markdown/syntax",
82 "target": "https://daringfireball.net/projects/markdown/syntax",
83 "external_target": "https://daringfireball.net/projects/markdown/syntax",
83 "external_target": "https://daringfireball.net/projects/markdown/syntax",
84 },
84 },
85 {
85 {
86 "name": "rhodecode_official",
86 "name": "rhodecode_official",
87 "target": "https://rhodecode.com",
87 "target": "https://rhodecode.com",
88 "external_target": "https://rhodecode.com/",
88 "external_target": "https://rhodecode.com/",
89 },
89 },
90 {
90 {
91 "name": "rhodecode_support",
91 "name": "rhodecode_support",
92 "target": "https://rhodecode.com/help/",
92 "target": "https://rhodecode.com/help/",
93 "external_target": "https://rhodecode.com/support",
93 "external_target": "https://rhodecode.com/support",
94 },
94 },
95 {
95 {
96 "name": "rhodecode_translations",
96 "name": "rhodecode_translations",
97 "target": "https://rhodecode.com/translate/enterprise",
97 "target": "https://rhodecode.com/translate/enterprise",
98 "external_target": "https://www.transifex.com/rhodecode/RhodeCode/",
98 "external_target": "https://www.transifex.com/rhodecode/RhodeCode/",
99 },
99 },
100
100
101 ]
101 ]
102
102
103
103
104 def connect_redirection_links(config):
104 def connect_redirection_links(config):
105 for link in link_config:
105 for link in link_config:
106 config.add_route(link['name'], link['target'], static=True)
106 config.add_route(link['name'], link['target'], static=True)
@@ -1,652 +1,652 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2011-2017 RhodeCode GmbH
3 # Copyright (C) 2011-2017 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 """
21 """
22 comments model for RhodeCode
22 comments model for RhodeCode
23 """
23 """
24
24
25 import logging
25 import logging
26 import traceback
26 import traceback
27 import collections
27 import collections
28
28
29 from pylons.i18n.translation import _
29 from pylons.i18n.translation import _
30 from pyramid.threadlocal import get_current_registry, get_current_request
30 from pyramid.threadlocal import get_current_registry, get_current_request
31 from sqlalchemy.sql.expression import null
31 from sqlalchemy.sql.expression import null
32 from sqlalchemy.sql.functions import coalesce
32 from sqlalchemy.sql.functions import coalesce
33
33
34 from rhodecode.lib import helpers as h, diffs, channelstream
34 from rhodecode.lib import helpers as h, diffs, channelstream
35 from rhodecode.lib import audit_logger
35 from rhodecode.lib import audit_logger
36 from rhodecode.lib.utils2 import extract_mentioned_users, safe_str
36 from rhodecode.lib.utils2 import extract_mentioned_users, safe_str
37 from rhodecode.model import BaseModel
37 from rhodecode.model import BaseModel
38 from rhodecode.model.db import (
38 from rhodecode.model.db import (
39 ChangesetComment, User, Notification, PullRequest, AttributeDict)
39 ChangesetComment, User, Notification, PullRequest, AttributeDict)
40 from rhodecode.model.notification import NotificationModel
40 from rhodecode.model.notification import NotificationModel
41 from rhodecode.model.meta import Session
41 from rhodecode.model.meta import Session
42 from rhodecode.model.settings import VcsSettingsModel
42 from rhodecode.model.settings import VcsSettingsModel
43 from rhodecode.model.notification import EmailNotificationModel
43 from rhodecode.model.notification import EmailNotificationModel
44 from rhodecode.model.validation_schema.schemas import comment_schema
44 from rhodecode.model.validation_schema.schemas import comment_schema
45
45
46
46
47 log = logging.getLogger(__name__)
47 log = logging.getLogger(__name__)
48
48
49
49
50 class CommentsModel(BaseModel):
50 class CommentsModel(BaseModel):
51
51
52 cls = ChangesetComment
52 cls = ChangesetComment
53
53
54 DIFF_CONTEXT_BEFORE = 3
54 DIFF_CONTEXT_BEFORE = 3
55 DIFF_CONTEXT_AFTER = 3
55 DIFF_CONTEXT_AFTER = 3
56
56
57 def __get_commit_comment(self, changeset_comment):
57 def __get_commit_comment(self, changeset_comment):
58 return self._get_instance(ChangesetComment, changeset_comment)
58 return self._get_instance(ChangesetComment, changeset_comment)
59
59
60 def __get_pull_request(self, pull_request):
60 def __get_pull_request(self, pull_request):
61 return self._get_instance(PullRequest, pull_request)
61 return self._get_instance(PullRequest, pull_request)
62
62
63 def _extract_mentions(self, s):
63 def _extract_mentions(self, s):
64 user_objects = []
64 user_objects = []
65 for username in extract_mentioned_users(s):
65 for username in extract_mentioned_users(s):
66 user_obj = User.get_by_username(username, case_insensitive=True)
66 user_obj = User.get_by_username(username, case_insensitive=True)
67 if user_obj:
67 if user_obj:
68 user_objects.append(user_obj)
68 user_objects.append(user_obj)
69 return user_objects
69 return user_objects
70
70
71 def _get_renderer(self, global_renderer='rst'):
71 def _get_renderer(self, global_renderer='rst'):
72 request = get_current_request()
72 request = get_current_request()
73
73
74 try:
74 try:
75 global_renderer = request.call_context.visual.default_renderer
75 global_renderer = request.call_context.visual.default_renderer
76 except AttributeError:
76 except AttributeError:
77 log.debug("Renderer not set, falling back "
77 log.debug("Renderer not set, falling back "
78 "to default renderer '%s'", global_renderer)
78 "to default renderer '%s'", global_renderer)
79 except Exception:
79 except Exception:
80 log.error(traceback.format_exc())
80 log.error(traceback.format_exc())
81 return global_renderer
81 return global_renderer
82
82
83 def aggregate_comments(self, comments, versions, show_version, inline=False):
83 def aggregate_comments(self, comments, versions, show_version, inline=False):
84 # group by versions, and count until, and display objects
84 # group by versions, and count until, and display objects
85
85
86 comment_groups = collections.defaultdict(list)
86 comment_groups = collections.defaultdict(list)
87 [comment_groups[
87 [comment_groups[
88 _co.pull_request_version_id].append(_co) for _co in comments]
88 _co.pull_request_version_id].append(_co) for _co in comments]
89
89
90 def yield_comments(pos):
90 def yield_comments(pos):
91 for co in comment_groups[pos]:
91 for co in comment_groups[pos]:
92 yield co
92 yield co
93
93
94 comment_versions = collections.defaultdict(
94 comment_versions = collections.defaultdict(
95 lambda: collections.defaultdict(list))
95 lambda: collections.defaultdict(list))
96 prev_prvid = -1
96 prev_prvid = -1
97 # fake last entry with None, to aggregate on "latest" version which
97 # fake last entry with None, to aggregate on "latest" version which
98 # doesn't have an pull_request_version_id
98 # doesn't have an pull_request_version_id
99 for ver in versions + [AttributeDict({'pull_request_version_id': None})]:
99 for ver in versions + [AttributeDict({'pull_request_version_id': None})]:
100 prvid = ver.pull_request_version_id
100 prvid = ver.pull_request_version_id
101 if prev_prvid == -1:
101 if prev_prvid == -1:
102 prev_prvid = prvid
102 prev_prvid = prvid
103
103
104 for co in yield_comments(prvid):
104 for co in yield_comments(prvid):
105 comment_versions[prvid]['at'].append(co)
105 comment_versions[prvid]['at'].append(co)
106
106
107 # save until
107 # save until
108 current = comment_versions[prvid]['at']
108 current = comment_versions[prvid]['at']
109 prev_until = comment_versions[prev_prvid]['until']
109 prev_until = comment_versions[prev_prvid]['until']
110 cur_until = prev_until + current
110 cur_until = prev_until + current
111 comment_versions[prvid]['until'].extend(cur_until)
111 comment_versions[prvid]['until'].extend(cur_until)
112
112
113 # save outdated
113 # save outdated
114 if inline:
114 if inline:
115 outdated = [x for x in cur_until
115 outdated = [x for x in cur_until
116 if x.outdated_at_version(show_version)]
116 if x.outdated_at_version(show_version)]
117 else:
117 else:
118 outdated = [x for x in cur_until
118 outdated = [x for x in cur_until
119 if x.older_than_version(show_version)]
119 if x.older_than_version(show_version)]
120 display = [x for x in cur_until if x not in outdated]
120 display = [x for x in cur_until if x not in outdated]
121
121
122 comment_versions[prvid]['outdated'] = outdated
122 comment_versions[prvid]['outdated'] = outdated
123 comment_versions[prvid]['display'] = display
123 comment_versions[prvid]['display'] = display
124
124
125 prev_prvid = prvid
125 prev_prvid = prvid
126
126
127 return comment_versions
127 return comment_versions
128
128
129 def get_unresolved_todos(self, pull_request, show_outdated=True):
129 def get_unresolved_todos(self, pull_request, show_outdated=True):
130
130
131 todos = Session().query(ChangesetComment) \
131 todos = Session().query(ChangesetComment) \
132 .filter(ChangesetComment.pull_request == pull_request) \
132 .filter(ChangesetComment.pull_request == pull_request) \
133 .filter(ChangesetComment.resolved_by == None) \
133 .filter(ChangesetComment.resolved_by == None) \
134 .filter(ChangesetComment.comment_type
134 .filter(ChangesetComment.comment_type
135 == ChangesetComment.COMMENT_TYPE_TODO)
135 == ChangesetComment.COMMENT_TYPE_TODO)
136
136
137 if not show_outdated:
137 if not show_outdated:
138 todos = todos.filter(
138 todos = todos.filter(
139 coalesce(ChangesetComment.display_state, '') !=
139 coalesce(ChangesetComment.display_state, '') !=
140 ChangesetComment.COMMENT_OUTDATED)
140 ChangesetComment.COMMENT_OUTDATED)
141
141
142 todos = todos.all()
142 todos = todos.all()
143
143
144 return todos
144 return todos
145
145
146 def get_commit_unresolved_todos(self, commit_id, show_outdated=True):
146 def get_commit_unresolved_todos(self, commit_id, show_outdated=True):
147
147
148 todos = Session().query(ChangesetComment) \
148 todos = Session().query(ChangesetComment) \
149 .filter(ChangesetComment.revision == commit_id) \
149 .filter(ChangesetComment.revision == commit_id) \
150 .filter(ChangesetComment.resolved_by == None) \
150 .filter(ChangesetComment.resolved_by == None) \
151 .filter(ChangesetComment.comment_type
151 .filter(ChangesetComment.comment_type
152 == ChangesetComment.COMMENT_TYPE_TODO)
152 == ChangesetComment.COMMENT_TYPE_TODO)
153
153
154 if not show_outdated:
154 if not show_outdated:
155 todos = todos.filter(
155 todos = todos.filter(
156 coalesce(ChangesetComment.display_state, '') !=
156 coalesce(ChangesetComment.display_state, '') !=
157 ChangesetComment.COMMENT_OUTDATED)
157 ChangesetComment.COMMENT_OUTDATED)
158
158
159 todos = todos.all()
159 todos = todos.all()
160
160
161 return todos
161 return todos
162
162
163 def _log_audit_action(self, action, action_data, user, comment):
163 def _log_audit_action(self, action, action_data, user, comment):
164 audit_logger.store(
164 audit_logger.store(
165 action=action,
165 action=action,
166 action_data=action_data,
166 action_data=action_data,
167 user=user,
167 user=user,
168 repo=comment.repo)
168 repo=comment.repo)
169
169
170 def create(self, text, repo, user, commit_id=None, pull_request=None,
170 def create(self, text, repo, user, commit_id=None, pull_request=None,
171 f_path=None, line_no=None, status_change=None,
171 f_path=None, line_no=None, status_change=None,
172 status_change_type=None, comment_type=None,
172 status_change_type=None, comment_type=None,
173 resolves_comment_id=None, closing_pr=False, send_email=True,
173 resolves_comment_id=None, closing_pr=False, send_email=True,
174 renderer=None):
174 renderer=None):
175 """
175 """
176 Creates new comment for commit or pull request.
176 Creates new comment for commit or pull request.
177 IF status_change is not none this comment is associated with a
177 IF status_change is not none this comment is associated with a
178 status change of commit or commit associated with pull request
178 status change of commit or commit associated with pull request
179
179
180 :param text:
180 :param text:
181 :param repo:
181 :param repo:
182 :param user:
182 :param user:
183 :param commit_id:
183 :param commit_id:
184 :param pull_request:
184 :param pull_request:
185 :param f_path:
185 :param f_path:
186 :param line_no:
186 :param line_no:
187 :param status_change: Label for status change
187 :param status_change: Label for status change
188 :param comment_type: Type of comment
188 :param comment_type: Type of comment
189 :param status_change_type: type of status change
189 :param status_change_type: type of status change
190 :param closing_pr:
190 :param closing_pr:
191 :param send_email:
191 :param send_email:
192 :param renderer: pick renderer for this comment
192 :param renderer: pick renderer for this comment
193 """
193 """
194 if not text:
194 if not text:
195 log.warning('Missing text for comment, skipping...')
195 log.warning('Missing text for comment, skipping...')
196 return
196 return
197
197
198 if not renderer:
198 if not renderer:
199 renderer = self._get_renderer()
199 renderer = self._get_renderer()
200
200
201 repo = self._get_repo(repo)
201 repo = self._get_repo(repo)
202 user = self._get_user(user)
202 user = self._get_user(user)
203
203
204 schema = comment_schema.CommentSchema()
204 schema = comment_schema.CommentSchema()
205 validated_kwargs = schema.deserialize(dict(
205 validated_kwargs = schema.deserialize(dict(
206 comment_body=text,
206 comment_body=text,
207 comment_type=comment_type,
207 comment_type=comment_type,
208 comment_file=f_path,
208 comment_file=f_path,
209 comment_line=line_no,
209 comment_line=line_no,
210 renderer_type=renderer,
210 renderer_type=renderer,
211 status_change=status_change_type,
211 status_change=status_change_type,
212 resolves_comment_id=resolves_comment_id,
212 resolves_comment_id=resolves_comment_id,
213 repo=repo.repo_id,
213 repo=repo.repo_id,
214 user=user.user_id,
214 user=user.user_id,
215 ))
215 ))
216
216
217 comment = ChangesetComment()
217 comment = ChangesetComment()
218 comment.renderer = validated_kwargs['renderer_type']
218 comment.renderer = validated_kwargs['renderer_type']
219 comment.text = validated_kwargs['comment_body']
219 comment.text = validated_kwargs['comment_body']
220 comment.f_path = validated_kwargs['comment_file']
220 comment.f_path = validated_kwargs['comment_file']
221 comment.line_no = validated_kwargs['comment_line']
221 comment.line_no = validated_kwargs['comment_line']
222 comment.comment_type = validated_kwargs['comment_type']
222 comment.comment_type = validated_kwargs['comment_type']
223
223
224 comment.repo = repo
224 comment.repo = repo
225 comment.author = user
225 comment.author = user
226 comment.resolved_comment = self.__get_commit_comment(
226 comment.resolved_comment = self.__get_commit_comment(
227 validated_kwargs['resolves_comment_id'])
227 validated_kwargs['resolves_comment_id'])
228
228
229 pull_request_id = pull_request
229 pull_request_id = pull_request
230
230
231 commit_obj = None
231 commit_obj = None
232 pull_request_obj = None
232 pull_request_obj = None
233
233
234 if commit_id:
234 if commit_id:
235 notification_type = EmailNotificationModel.TYPE_COMMIT_COMMENT
235 notification_type = EmailNotificationModel.TYPE_COMMIT_COMMENT
236 # do a lookup, so we don't pass something bad here
236 # do a lookup, so we don't pass something bad here
237 commit_obj = repo.scm_instance().get_commit(commit_id=commit_id)
237 commit_obj = repo.scm_instance().get_commit(commit_id=commit_id)
238 comment.revision = commit_obj.raw_id
238 comment.revision = commit_obj.raw_id
239
239
240 elif pull_request_id:
240 elif pull_request_id:
241 notification_type = EmailNotificationModel.TYPE_PULL_REQUEST_COMMENT
241 notification_type = EmailNotificationModel.TYPE_PULL_REQUEST_COMMENT
242 pull_request_obj = self.__get_pull_request(pull_request_id)
242 pull_request_obj = self.__get_pull_request(pull_request_id)
243 comment.pull_request = pull_request_obj
243 comment.pull_request = pull_request_obj
244 else:
244 else:
245 raise Exception('Please specify commit or pull_request_id')
245 raise Exception('Please specify commit or pull_request_id')
246
246
247 Session().add(comment)
247 Session().add(comment)
248 Session().flush()
248 Session().flush()
249 kwargs = {
249 kwargs = {
250 'user': user,
250 'user': user,
251 'renderer_type': renderer,
251 'renderer_type': renderer,
252 'repo_name': repo.repo_name,
252 'repo_name': repo.repo_name,
253 'status_change': status_change,
253 'status_change': status_change,
254 'status_change_type': status_change_type,
254 'status_change_type': status_change_type,
255 'comment_body': text,
255 'comment_body': text,
256 'comment_file': f_path,
256 'comment_file': f_path,
257 'comment_line': line_no,
257 'comment_line': line_no,
258 'comment_type': comment_type or 'note'
258 'comment_type': comment_type or 'note'
259 }
259 }
260
260
261 if commit_obj:
261 if commit_obj:
262 recipients = ChangesetComment.get_users(
262 recipients = ChangesetComment.get_users(
263 revision=commit_obj.raw_id)
263 revision=commit_obj.raw_id)
264 # add commit author if it's in RhodeCode system
264 # add commit author if it's in RhodeCode system
265 cs_author = User.get_from_cs_author(commit_obj.author)
265 cs_author = User.get_from_cs_author(commit_obj.author)
266 if not cs_author:
266 if not cs_author:
267 # use repo owner if we cannot extract the author correctly
267 # use repo owner if we cannot extract the author correctly
268 cs_author = repo.user
268 cs_author = repo.user
269 recipients += [cs_author]
269 recipients += [cs_author]
270
270
271 commit_comment_url = self.get_url(comment)
271 commit_comment_url = self.get_url(comment)
272
272
273 target_repo_url = h.link_to(
273 target_repo_url = h.link_to(
274 repo.repo_name,
274 repo.repo_name,
275 h.route_url('repo_summary', repo_name=repo.repo_name))
275 h.route_url('repo_summary', repo_name=repo.repo_name))
276
276
277 # commit specifics
277 # commit specifics
278 kwargs.update({
278 kwargs.update({
279 'commit': commit_obj,
279 'commit': commit_obj,
280 'commit_message': commit_obj.message,
280 'commit_message': commit_obj.message,
281 'commit_target_repo': target_repo_url,
281 'commit_target_repo': target_repo_url,
282 'commit_comment_url': commit_comment_url,
282 'commit_comment_url': commit_comment_url,
283 })
283 })
284
284
285 elif pull_request_obj:
285 elif pull_request_obj:
286 # get the current participants of this pull request
286 # get the current participants of this pull request
287 recipients = ChangesetComment.get_users(
287 recipients = ChangesetComment.get_users(
288 pull_request_id=pull_request_obj.pull_request_id)
288 pull_request_id=pull_request_obj.pull_request_id)
289 # add pull request author
289 # add pull request author
290 recipients += [pull_request_obj.author]
290 recipients += [pull_request_obj.author]
291
291
292 # add the reviewers to notification
292 # add the reviewers to notification
293 recipients += [x.user for x in pull_request_obj.reviewers]
293 recipients += [x.user for x in pull_request_obj.reviewers]
294
294
295 pr_target_repo = pull_request_obj.target_repo
295 pr_target_repo = pull_request_obj.target_repo
296 pr_source_repo = pull_request_obj.source_repo
296 pr_source_repo = pull_request_obj.source_repo
297
297
298 pr_comment_url = h.route_url(
298 pr_comment_url = h.route_url(
299 'pullrequest_show',
299 'pullrequest_show',
300 repo_name=pr_target_repo.repo_name,
300 repo_name=pr_target_repo.repo_name,
301 pull_request_id=pull_request_obj.pull_request_id,
301 pull_request_id=pull_request_obj.pull_request_id,
302 anchor='comment-%s' % comment.comment_id)
302 _anchor='comment-%s' % comment.comment_id)
303
303
304 # set some variables for email notification
304 # set some variables for email notification
305 pr_target_repo_url = h.route_url(
305 pr_target_repo_url = h.route_url(
306 'repo_summary', repo_name=pr_target_repo.repo_name)
306 'repo_summary', repo_name=pr_target_repo.repo_name)
307
307
308 pr_source_repo_url = h.route_url(
308 pr_source_repo_url = h.route_url(
309 'repo_summary', repo_name=pr_source_repo.repo_name)
309 'repo_summary', repo_name=pr_source_repo.repo_name)
310
310
311 # pull request specifics
311 # pull request specifics
312 kwargs.update({
312 kwargs.update({
313 'pull_request': pull_request_obj,
313 'pull_request': pull_request_obj,
314 'pr_id': pull_request_obj.pull_request_id,
314 'pr_id': pull_request_obj.pull_request_id,
315 'pr_target_repo': pr_target_repo,
315 'pr_target_repo': pr_target_repo,
316 'pr_target_repo_url': pr_target_repo_url,
316 'pr_target_repo_url': pr_target_repo_url,
317 'pr_source_repo': pr_source_repo,
317 'pr_source_repo': pr_source_repo,
318 'pr_source_repo_url': pr_source_repo_url,
318 'pr_source_repo_url': pr_source_repo_url,
319 'pr_comment_url': pr_comment_url,
319 'pr_comment_url': pr_comment_url,
320 'pr_closing': closing_pr,
320 'pr_closing': closing_pr,
321 })
321 })
322 if send_email:
322 if send_email:
323 # pre-generate the subject for notification itself
323 # pre-generate the subject for notification itself
324 (subject,
324 (subject,
325 _h, _e, # we don't care about those
325 _h, _e, # we don't care about those
326 body_plaintext) = EmailNotificationModel().render_email(
326 body_plaintext) = EmailNotificationModel().render_email(
327 notification_type, **kwargs)
327 notification_type, **kwargs)
328
328
329 mention_recipients = set(
329 mention_recipients = set(
330 self._extract_mentions(text)).difference(recipients)
330 self._extract_mentions(text)).difference(recipients)
331
331
332 # create notification objects, and emails
332 # create notification objects, and emails
333 NotificationModel().create(
333 NotificationModel().create(
334 created_by=user,
334 created_by=user,
335 notification_subject=subject,
335 notification_subject=subject,
336 notification_body=body_plaintext,
336 notification_body=body_plaintext,
337 notification_type=notification_type,
337 notification_type=notification_type,
338 recipients=recipients,
338 recipients=recipients,
339 mention_recipients=mention_recipients,
339 mention_recipients=mention_recipients,
340 email_kwargs=kwargs,
340 email_kwargs=kwargs,
341 )
341 )
342
342
343 Session().flush()
343 Session().flush()
344 if comment.pull_request:
344 if comment.pull_request:
345 action = 'repo.pull_request.comment.create'
345 action = 'repo.pull_request.comment.create'
346 else:
346 else:
347 action = 'repo.commit.comment.create'
347 action = 'repo.commit.comment.create'
348
348
349 comment_data = comment.get_api_data()
349 comment_data = comment.get_api_data()
350 self._log_audit_action(
350 self._log_audit_action(
351 action, {'data': comment_data}, user, comment)
351 action, {'data': comment_data}, user, comment)
352
352
353 msg_url = ''
353 msg_url = ''
354 channel = None
354 channel = None
355 if commit_obj:
355 if commit_obj:
356 msg_url = commit_comment_url
356 msg_url = commit_comment_url
357 repo_name = repo.repo_name
357 repo_name = repo.repo_name
358 channel = u'/repo${}$/commit/{}'.format(
358 channel = u'/repo${}$/commit/{}'.format(
359 repo_name,
359 repo_name,
360 commit_obj.raw_id
360 commit_obj.raw_id
361 )
361 )
362 elif pull_request_obj:
362 elif pull_request_obj:
363 msg_url = pr_comment_url
363 msg_url = pr_comment_url
364 repo_name = pr_target_repo.repo_name
364 repo_name = pr_target_repo.repo_name
365 channel = u'/repo${}$/pr/{}'.format(
365 channel = u'/repo${}$/pr/{}'.format(
366 repo_name,
366 repo_name,
367 pull_request_id
367 pull_request_id
368 )
368 )
369
369
370 message = '<strong>{}</strong> {} - ' \
370 message = '<strong>{}</strong> {} - ' \
371 '<a onclick="window.location=\'{}\';' \
371 '<a onclick="window.location=\'{}\';' \
372 'window.location.reload()">' \
372 'window.location.reload()">' \
373 '<strong>{}</strong></a>'
373 '<strong>{}</strong></a>'
374 message = message.format(
374 message = message.format(
375 user.username, _('made a comment'), msg_url,
375 user.username, _('made a comment'), msg_url,
376 _('Show it now'))
376 _('Show it now'))
377
377
378 channelstream.post_message(
378 channelstream.post_message(
379 channel, message, user.username,
379 channel, message, user.username,
380 registry=get_current_registry())
380 registry=get_current_registry())
381
381
382 return comment
382 return comment
383
383
384 def delete(self, comment, user):
384 def delete(self, comment, user):
385 """
385 """
386 Deletes given comment
386 Deletes given comment
387 """
387 """
388 comment = self.__get_commit_comment(comment)
388 comment = self.__get_commit_comment(comment)
389 old_data = comment.get_api_data()
389 old_data = comment.get_api_data()
390 Session().delete(comment)
390 Session().delete(comment)
391
391
392 if comment.pull_request:
392 if comment.pull_request:
393 action = 'repo.pull_request.comment.delete'
393 action = 'repo.pull_request.comment.delete'
394 else:
394 else:
395 action = 'repo.commit.comment.delete'
395 action = 'repo.commit.comment.delete'
396
396
397 self._log_audit_action(
397 self._log_audit_action(
398 action, {'old_data': old_data}, user, comment)
398 action, {'old_data': old_data}, user, comment)
399
399
400 return comment
400 return comment
401
401
402 def get_all_comments(self, repo_id, revision=None, pull_request=None):
402 def get_all_comments(self, repo_id, revision=None, pull_request=None):
403 q = ChangesetComment.query()\
403 q = ChangesetComment.query()\
404 .filter(ChangesetComment.repo_id == repo_id)
404 .filter(ChangesetComment.repo_id == repo_id)
405 if revision:
405 if revision:
406 q = q.filter(ChangesetComment.revision == revision)
406 q = q.filter(ChangesetComment.revision == revision)
407 elif pull_request:
407 elif pull_request:
408 pull_request = self.__get_pull_request(pull_request)
408 pull_request = self.__get_pull_request(pull_request)
409 q = q.filter(ChangesetComment.pull_request == pull_request)
409 q = q.filter(ChangesetComment.pull_request == pull_request)
410 else:
410 else:
411 raise Exception('Please specify commit or pull_request')
411 raise Exception('Please specify commit or pull_request')
412 q = q.order_by(ChangesetComment.created_on)
412 q = q.order_by(ChangesetComment.created_on)
413 return q.all()
413 return q.all()
414
414
415 def get_url(self, comment, request=None, permalink=False):
415 def get_url(self, comment, request=None, permalink=False):
416 if not request:
416 if not request:
417 request = get_current_request()
417 request = get_current_request()
418
418
419 comment = self.__get_commit_comment(comment)
419 comment = self.__get_commit_comment(comment)
420 if comment.pull_request:
420 if comment.pull_request:
421 pull_request = comment.pull_request
421 pull_request = comment.pull_request
422 if permalink:
422 if permalink:
423 return request.route_url(
423 return request.route_url(
424 'pull_requests_global',
424 'pull_requests_global',
425 pull_request_id=pull_request.pull_request_id,
425 pull_request_id=pull_request.pull_request_id,
426 _anchor='comment-%s' % comment.comment_id)
426 _anchor='comment-%s' % comment.comment_id)
427 else:
427 else:
428 return request.route_url('pullrequest_show',
428 return request.route_url('pullrequest_show',
429 repo_name=safe_str(pull_request.target_repo.repo_name),
429 repo_name=safe_str(pull_request.target_repo.repo_name),
430 pull_request_id=pull_request.pull_request_id,
430 pull_request_id=pull_request.pull_request_id,
431 _anchor='comment-%s' % comment.comment_id)
431 _anchor='comment-%s' % comment.comment_id)
432
432
433 else:
433 else:
434 repo = comment.repo
434 repo = comment.repo
435 commit_id = comment.revision
435 commit_id = comment.revision
436
436
437 if permalink:
437 if permalink:
438 return request.route_url(
438 return request.route_url(
439 'repo_commit', repo_name=safe_str(repo.repo_id),
439 'repo_commit', repo_name=safe_str(repo.repo_id),
440 commit_id=commit_id,
440 commit_id=commit_id,
441 _anchor='comment-%s' % comment.comment_id)
441 _anchor='comment-%s' % comment.comment_id)
442
442
443 else:
443 else:
444 return request.route_url(
444 return request.route_url(
445 'repo_commit', repo_name=safe_str(repo.repo_name),
445 'repo_commit', repo_name=safe_str(repo.repo_name),
446 commit_id=commit_id,
446 commit_id=commit_id,
447 _anchor='comment-%s' % comment.comment_id)
447 _anchor='comment-%s' % comment.comment_id)
448
448
449 def get_comments(self, repo_id, revision=None, pull_request=None):
449 def get_comments(self, repo_id, revision=None, pull_request=None):
450 """
450 """
451 Gets main comments based on revision or pull_request_id
451 Gets main comments based on revision or pull_request_id
452
452
453 :param repo_id:
453 :param repo_id:
454 :param revision:
454 :param revision:
455 :param pull_request:
455 :param pull_request:
456 """
456 """
457
457
458 q = ChangesetComment.query()\
458 q = ChangesetComment.query()\
459 .filter(ChangesetComment.repo_id == repo_id)\
459 .filter(ChangesetComment.repo_id == repo_id)\
460 .filter(ChangesetComment.line_no == None)\
460 .filter(ChangesetComment.line_no == None)\
461 .filter(ChangesetComment.f_path == None)
461 .filter(ChangesetComment.f_path == None)
462 if revision:
462 if revision:
463 q = q.filter(ChangesetComment.revision == revision)
463 q = q.filter(ChangesetComment.revision == revision)
464 elif pull_request:
464 elif pull_request:
465 pull_request = self.__get_pull_request(pull_request)
465 pull_request = self.__get_pull_request(pull_request)
466 q = q.filter(ChangesetComment.pull_request == pull_request)
466 q = q.filter(ChangesetComment.pull_request == pull_request)
467 else:
467 else:
468 raise Exception('Please specify commit or pull_request')
468 raise Exception('Please specify commit or pull_request')
469 q = q.order_by(ChangesetComment.created_on)
469 q = q.order_by(ChangesetComment.created_on)
470 return q.all()
470 return q.all()
471
471
472 def get_inline_comments(self, repo_id, revision=None, pull_request=None):
472 def get_inline_comments(self, repo_id, revision=None, pull_request=None):
473 q = self._get_inline_comments_query(repo_id, revision, pull_request)
473 q = self._get_inline_comments_query(repo_id, revision, pull_request)
474 return self._group_comments_by_path_and_line_number(q)
474 return self._group_comments_by_path_and_line_number(q)
475
475
476 def get_inline_comments_count(self, inline_comments, skip_outdated=True,
476 def get_inline_comments_count(self, inline_comments, skip_outdated=True,
477 version=None):
477 version=None):
478 inline_cnt = 0
478 inline_cnt = 0
479 for fname, per_line_comments in inline_comments.iteritems():
479 for fname, per_line_comments in inline_comments.iteritems():
480 for lno, comments in per_line_comments.iteritems():
480 for lno, comments in per_line_comments.iteritems():
481 for comm in comments:
481 for comm in comments:
482 if not comm.outdated_at_version(version) and skip_outdated:
482 if not comm.outdated_at_version(version) and skip_outdated:
483 inline_cnt += 1
483 inline_cnt += 1
484
484
485 return inline_cnt
485 return inline_cnt
486
486
487 def get_outdated_comments(self, repo_id, pull_request):
487 def get_outdated_comments(self, repo_id, pull_request):
488 # TODO: johbo: Remove `repo_id`, it is not needed to find the comments
488 # TODO: johbo: Remove `repo_id`, it is not needed to find the comments
489 # of a pull request.
489 # of a pull request.
490 q = self._all_inline_comments_of_pull_request(pull_request)
490 q = self._all_inline_comments_of_pull_request(pull_request)
491 q = q.filter(
491 q = q.filter(
492 ChangesetComment.display_state ==
492 ChangesetComment.display_state ==
493 ChangesetComment.COMMENT_OUTDATED
493 ChangesetComment.COMMENT_OUTDATED
494 ).order_by(ChangesetComment.comment_id.asc())
494 ).order_by(ChangesetComment.comment_id.asc())
495
495
496 return self._group_comments_by_path_and_line_number(q)
496 return self._group_comments_by_path_and_line_number(q)
497
497
498 def _get_inline_comments_query(self, repo_id, revision, pull_request):
498 def _get_inline_comments_query(self, repo_id, revision, pull_request):
499 # TODO: johbo: Split this into two methods: One for PR and one for
499 # TODO: johbo: Split this into two methods: One for PR and one for
500 # commit.
500 # commit.
501 if revision:
501 if revision:
502 q = Session().query(ChangesetComment).filter(
502 q = Session().query(ChangesetComment).filter(
503 ChangesetComment.repo_id == repo_id,
503 ChangesetComment.repo_id == repo_id,
504 ChangesetComment.line_no != null(),
504 ChangesetComment.line_no != null(),
505 ChangesetComment.f_path != null(),
505 ChangesetComment.f_path != null(),
506 ChangesetComment.revision == revision)
506 ChangesetComment.revision == revision)
507
507
508 elif pull_request:
508 elif pull_request:
509 pull_request = self.__get_pull_request(pull_request)
509 pull_request = self.__get_pull_request(pull_request)
510 if not CommentsModel.use_outdated_comments(pull_request):
510 if not CommentsModel.use_outdated_comments(pull_request):
511 q = self._visible_inline_comments_of_pull_request(pull_request)
511 q = self._visible_inline_comments_of_pull_request(pull_request)
512 else:
512 else:
513 q = self._all_inline_comments_of_pull_request(pull_request)
513 q = self._all_inline_comments_of_pull_request(pull_request)
514
514
515 else:
515 else:
516 raise Exception('Please specify commit or pull_request_id')
516 raise Exception('Please specify commit or pull_request_id')
517 q = q.order_by(ChangesetComment.comment_id.asc())
517 q = q.order_by(ChangesetComment.comment_id.asc())
518 return q
518 return q
519
519
520 def _group_comments_by_path_and_line_number(self, q):
520 def _group_comments_by_path_and_line_number(self, q):
521 comments = q.all()
521 comments = q.all()
522 paths = collections.defaultdict(lambda: collections.defaultdict(list))
522 paths = collections.defaultdict(lambda: collections.defaultdict(list))
523 for co in comments:
523 for co in comments:
524 paths[co.f_path][co.line_no].append(co)
524 paths[co.f_path][co.line_no].append(co)
525 return paths
525 return paths
526
526
527 @classmethod
527 @classmethod
528 def needed_extra_diff_context(cls):
528 def needed_extra_diff_context(cls):
529 return max(cls.DIFF_CONTEXT_BEFORE, cls.DIFF_CONTEXT_AFTER)
529 return max(cls.DIFF_CONTEXT_BEFORE, cls.DIFF_CONTEXT_AFTER)
530
530
531 def outdate_comments(self, pull_request, old_diff_data, new_diff_data):
531 def outdate_comments(self, pull_request, old_diff_data, new_diff_data):
532 if not CommentsModel.use_outdated_comments(pull_request):
532 if not CommentsModel.use_outdated_comments(pull_request):
533 return
533 return
534
534
535 comments = self._visible_inline_comments_of_pull_request(pull_request)
535 comments = self._visible_inline_comments_of_pull_request(pull_request)
536 comments_to_outdate = comments.all()
536 comments_to_outdate = comments.all()
537
537
538 for comment in comments_to_outdate:
538 for comment in comments_to_outdate:
539 self._outdate_one_comment(comment, old_diff_data, new_diff_data)
539 self._outdate_one_comment(comment, old_diff_data, new_diff_data)
540
540
541 def _outdate_one_comment(self, comment, old_diff_proc, new_diff_proc):
541 def _outdate_one_comment(self, comment, old_diff_proc, new_diff_proc):
542 diff_line = _parse_comment_line_number(comment.line_no)
542 diff_line = _parse_comment_line_number(comment.line_no)
543
543
544 try:
544 try:
545 old_context = old_diff_proc.get_context_of_line(
545 old_context = old_diff_proc.get_context_of_line(
546 path=comment.f_path, diff_line=diff_line)
546 path=comment.f_path, diff_line=diff_line)
547 new_context = new_diff_proc.get_context_of_line(
547 new_context = new_diff_proc.get_context_of_line(
548 path=comment.f_path, diff_line=diff_line)
548 path=comment.f_path, diff_line=diff_line)
549 except (diffs.LineNotInDiffException,
549 except (diffs.LineNotInDiffException,
550 diffs.FileNotInDiffException):
550 diffs.FileNotInDiffException):
551 comment.display_state = ChangesetComment.COMMENT_OUTDATED
551 comment.display_state = ChangesetComment.COMMENT_OUTDATED
552 return
552 return
553
553
554 if old_context == new_context:
554 if old_context == new_context:
555 return
555 return
556
556
557 if self._should_relocate_diff_line(diff_line):
557 if self._should_relocate_diff_line(diff_line):
558 new_diff_lines = new_diff_proc.find_context(
558 new_diff_lines = new_diff_proc.find_context(
559 path=comment.f_path, context=old_context,
559 path=comment.f_path, context=old_context,
560 offset=self.DIFF_CONTEXT_BEFORE)
560 offset=self.DIFF_CONTEXT_BEFORE)
561 if not new_diff_lines:
561 if not new_diff_lines:
562 comment.display_state = ChangesetComment.COMMENT_OUTDATED
562 comment.display_state = ChangesetComment.COMMENT_OUTDATED
563 else:
563 else:
564 new_diff_line = self._choose_closest_diff_line(
564 new_diff_line = self._choose_closest_diff_line(
565 diff_line, new_diff_lines)
565 diff_line, new_diff_lines)
566 comment.line_no = _diff_to_comment_line_number(new_diff_line)
566 comment.line_no = _diff_to_comment_line_number(new_diff_line)
567 else:
567 else:
568 comment.display_state = ChangesetComment.COMMENT_OUTDATED
568 comment.display_state = ChangesetComment.COMMENT_OUTDATED
569
569
570 def _should_relocate_diff_line(self, diff_line):
570 def _should_relocate_diff_line(self, diff_line):
571 """
571 """
572 Checks if relocation shall be tried for the given `diff_line`.
572 Checks if relocation shall be tried for the given `diff_line`.
573
573
574 If a comment points into the first lines, then we can have a situation
574 If a comment points into the first lines, then we can have a situation
575 that after an update another line has been added on top. In this case
575 that after an update another line has been added on top. In this case
576 we would find the context still and move the comment around. This
576 we would find the context still and move the comment around. This
577 would be wrong.
577 would be wrong.
578 """
578 """
579 should_relocate = (
579 should_relocate = (
580 (diff_line.new and diff_line.new > self.DIFF_CONTEXT_BEFORE) or
580 (diff_line.new and diff_line.new > self.DIFF_CONTEXT_BEFORE) or
581 (diff_line.old and diff_line.old > self.DIFF_CONTEXT_BEFORE))
581 (diff_line.old and diff_line.old > self.DIFF_CONTEXT_BEFORE))
582 return should_relocate
582 return should_relocate
583
583
584 def _choose_closest_diff_line(self, diff_line, new_diff_lines):
584 def _choose_closest_diff_line(self, diff_line, new_diff_lines):
585 candidate = new_diff_lines[0]
585 candidate = new_diff_lines[0]
586 best_delta = _diff_line_delta(diff_line, candidate)
586 best_delta = _diff_line_delta(diff_line, candidate)
587 for new_diff_line in new_diff_lines[1:]:
587 for new_diff_line in new_diff_lines[1:]:
588 delta = _diff_line_delta(diff_line, new_diff_line)
588 delta = _diff_line_delta(diff_line, new_diff_line)
589 if delta < best_delta:
589 if delta < best_delta:
590 candidate = new_diff_line
590 candidate = new_diff_line
591 best_delta = delta
591 best_delta = delta
592 return candidate
592 return candidate
593
593
594 def _visible_inline_comments_of_pull_request(self, pull_request):
594 def _visible_inline_comments_of_pull_request(self, pull_request):
595 comments = self._all_inline_comments_of_pull_request(pull_request)
595 comments = self._all_inline_comments_of_pull_request(pull_request)
596 comments = comments.filter(
596 comments = comments.filter(
597 coalesce(ChangesetComment.display_state, '') !=
597 coalesce(ChangesetComment.display_state, '') !=
598 ChangesetComment.COMMENT_OUTDATED)
598 ChangesetComment.COMMENT_OUTDATED)
599 return comments
599 return comments
600
600
601 def _all_inline_comments_of_pull_request(self, pull_request):
601 def _all_inline_comments_of_pull_request(self, pull_request):
602 comments = Session().query(ChangesetComment)\
602 comments = Session().query(ChangesetComment)\
603 .filter(ChangesetComment.line_no != None)\
603 .filter(ChangesetComment.line_no != None)\
604 .filter(ChangesetComment.f_path != None)\
604 .filter(ChangesetComment.f_path != None)\
605 .filter(ChangesetComment.pull_request == pull_request)
605 .filter(ChangesetComment.pull_request == pull_request)
606 return comments
606 return comments
607
607
608 def _all_general_comments_of_pull_request(self, pull_request):
608 def _all_general_comments_of_pull_request(self, pull_request):
609 comments = Session().query(ChangesetComment)\
609 comments = Session().query(ChangesetComment)\
610 .filter(ChangesetComment.line_no == None)\
610 .filter(ChangesetComment.line_no == None)\
611 .filter(ChangesetComment.f_path == None)\
611 .filter(ChangesetComment.f_path == None)\
612 .filter(ChangesetComment.pull_request == pull_request)
612 .filter(ChangesetComment.pull_request == pull_request)
613 return comments
613 return comments
614
614
615 @staticmethod
615 @staticmethod
616 def use_outdated_comments(pull_request):
616 def use_outdated_comments(pull_request):
617 settings_model = VcsSettingsModel(repo=pull_request.target_repo)
617 settings_model = VcsSettingsModel(repo=pull_request.target_repo)
618 settings = settings_model.get_general_settings()
618 settings = settings_model.get_general_settings()
619 return settings.get('rhodecode_use_outdated_comments', False)
619 return settings.get('rhodecode_use_outdated_comments', False)
620
620
621
621
622 def _parse_comment_line_number(line_no):
622 def _parse_comment_line_number(line_no):
623 """
623 """
624 Parses line numbers of the form "(o|n)\d+" and returns them in a tuple.
624 Parses line numbers of the form "(o|n)\d+" and returns them in a tuple.
625 """
625 """
626 old_line = None
626 old_line = None
627 new_line = None
627 new_line = None
628 if line_no.startswith('o'):
628 if line_no.startswith('o'):
629 old_line = int(line_no[1:])
629 old_line = int(line_no[1:])
630 elif line_no.startswith('n'):
630 elif line_no.startswith('n'):
631 new_line = int(line_no[1:])
631 new_line = int(line_no[1:])
632 else:
632 else:
633 raise ValueError("Comment lines have to start with either 'o' or 'n'.")
633 raise ValueError("Comment lines have to start with either 'o' or 'n'.")
634 return diffs.DiffLineNumber(old_line, new_line)
634 return diffs.DiffLineNumber(old_line, new_line)
635
635
636
636
637 def _diff_to_comment_line_number(diff_line):
637 def _diff_to_comment_line_number(diff_line):
638 if diff_line.new is not None:
638 if diff_line.new is not None:
639 return u'n{}'.format(diff_line.new)
639 return u'n{}'.format(diff_line.new)
640 elif diff_line.old is not None:
640 elif diff_line.old is not None:
641 return u'o{}'.format(diff_line.old)
641 return u'o{}'.format(diff_line.old)
642 return u''
642 return u''
643
643
644
644
645 def _diff_line_delta(a, b):
645 def _diff_line_delta(a, b):
646 if None not in (a.new, b.new):
646 if None not in (a.new, b.new):
647 return abs(a.new - b.new)
647 return abs(a.new - b.new)
648 elif None not in (a.old, b.old):
648 elif None not in (a.old, b.old):
649 return abs(a.old - b.old)
649 return abs(a.old - b.old)
650 else:
650 else:
651 raise ValueError(
651 raise ValueError(
652 "Cannot compute delta between {} and {}".format(a, b))
652 "Cannot compute delta between {} and {}".format(a, b))
@@ -1,271 +1,273 b''
1
1
2 /******************************************************************************
2 /******************************************************************************
3 * *
3 * *
4 * DO NOT CHANGE THIS FILE MANUALLY *
4 * DO NOT CHANGE THIS FILE MANUALLY *
5 * *
5 * *
6 * *
6 * *
7 * This file is automatically generated when the app starts up with *
7 * This file is automatically generated when the app starts up with *
8 * generate_js_files = true *
8 * generate_js_files = true *
9 * *
9 * *
10 * To add a route here pass jsroute=True to the route definition in the app *
10 * To add a route here pass jsroute=True to the route definition in the app *
11 * *
11 * *
12 ******************************************************************************/
12 ******************************************************************************/
13 function registerRCRoutes() {
13 function registerRCRoutes() {
14 // routes registration
14 // routes registration
15 pyroutes.register('edit_user', '/_admin/users/%(user_id)s/edit', ['user_id']);
15 pyroutes.register('edit_user', '/_admin/users/%(user_id)s/edit', ['user_id']);
16 pyroutes.register('favicon', '/favicon.ico', []);
16 pyroutes.register('favicon', '/favicon.ico', []);
17 pyroutes.register('robots', '/robots.txt', []);
17 pyroutes.register('robots', '/robots.txt', []);
18 pyroutes.register('auth_home', '/_admin/auth*traverse', []);
18 pyroutes.register('auth_home', '/_admin/auth*traverse', []);
19 pyroutes.register('global_integrations_new', '/_admin/integrations/new', []);
19 pyroutes.register('global_integrations_new', '/_admin/integrations/new', []);
20 pyroutes.register('global_integrations_home', '/_admin/integrations', []);
20 pyroutes.register('global_integrations_home', '/_admin/integrations', []);
21 pyroutes.register('global_integrations_list', '/_admin/integrations/%(integration)s', ['integration']);
21 pyroutes.register('global_integrations_list', '/_admin/integrations/%(integration)s', ['integration']);
22 pyroutes.register('global_integrations_create', '/_admin/integrations/%(integration)s/new', ['integration']);
22 pyroutes.register('global_integrations_create', '/_admin/integrations/%(integration)s/new', ['integration']);
23 pyroutes.register('global_integrations_edit', '/_admin/integrations/%(integration)s/%(integration_id)s', ['integration', 'integration_id']);
23 pyroutes.register('global_integrations_edit', '/_admin/integrations/%(integration)s/%(integration_id)s', ['integration', 'integration_id']);
24 pyroutes.register('repo_group_integrations_home', '/%(repo_group_name)s/settings/integrations', ['repo_group_name']);
24 pyroutes.register('repo_group_integrations_home', '/%(repo_group_name)s/settings/integrations', ['repo_group_name']);
25 pyroutes.register('repo_group_integrations_new', '/%(repo_group_name)s/settings/integrations/new', ['repo_group_name']);
25 pyroutes.register('repo_group_integrations_new', '/%(repo_group_name)s/settings/integrations/new', ['repo_group_name']);
26 pyroutes.register('repo_group_integrations_list', '/%(repo_group_name)s/settings/integrations/%(integration)s', ['repo_group_name', 'integration']);
26 pyroutes.register('repo_group_integrations_list', '/%(repo_group_name)s/settings/integrations/%(integration)s', ['repo_group_name', 'integration']);
27 pyroutes.register('repo_group_integrations_create', '/%(repo_group_name)s/settings/integrations/%(integration)s/new', ['repo_group_name', 'integration']);
27 pyroutes.register('repo_group_integrations_create', '/%(repo_group_name)s/settings/integrations/%(integration)s/new', ['repo_group_name', 'integration']);
28 pyroutes.register('repo_group_integrations_edit', '/%(repo_group_name)s/settings/integrations/%(integration)s/%(integration_id)s', ['repo_group_name', 'integration', 'integration_id']);
28 pyroutes.register('repo_group_integrations_edit', '/%(repo_group_name)s/settings/integrations/%(integration)s/%(integration_id)s', ['repo_group_name', 'integration', 'integration_id']);
29 pyroutes.register('repo_integrations_home', '/%(repo_name)s/settings/integrations', ['repo_name']);
29 pyroutes.register('repo_integrations_home', '/%(repo_name)s/settings/integrations', ['repo_name']);
30 pyroutes.register('repo_integrations_new', '/%(repo_name)s/settings/integrations/new', ['repo_name']);
30 pyroutes.register('repo_integrations_new', '/%(repo_name)s/settings/integrations/new', ['repo_name']);
31 pyroutes.register('repo_integrations_list', '/%(repo_name)s/settings/integrations/%(integration)s', ['repo_name', 'integration']);
31 pyroutes.register('repo_integrations_list', '/%(repo_name)s/settings/integrations/%(integration)s', ['repo_name', 'integration']);
32 pyroutes.register('repo_integrations_create', '/%(repo_name)s/settings/integrations/%(integration)s/new', ['repo_name', 'integration']);
32 pyroutes.register('repo_integrations_create', '/%(repo_name)s/settings/integrations/%(integration)s/new', ['repo_name', 'integration']);
33 pyroutes.register('repo_integrations_edit', '/%(repo_name)s/settings/integrations/%(integration)s/%(integration_id)s', ['repo_name', 'integration', 'integration_id']);
33 pyroutes.register('repo_integrations_edit', '/%(repo_name)s/settings/integrations/%(integration)s/%(integration_id)s', ['repo_name', 'integration', 'integration_id']);
34 pyroutes.register('ops_ping', '/_admin/ops/ping', []);
34 pyroutes.register('ops_ping', '/_admin/ops/ping', []);
35 pyroutes.register('ops_error_test', '/_admin/ops/error', []);
35 pyroutes.register('ops_error_test', '/_admin/ops/error', []);
36 pyroutes.register('ops_redirect_test', '/_admin/ops/redirect', []);
36 pyroutes.register('ops_redirect_test', '/_admin/ops/redirect', []);
37 pyroutes.register('ops_ping_legacy', '/_admin/ping', []);
38 pyroutes.register('ops_error_test_legacy', '/_admin/error_test', []);
37 pyroutes.register('admin_home', '/_admin', []);
39 pyroutes.register('admin_home', '/_admin', []);
38 pyroutes.register('admin_audit_logs', '/_admin/audit_logs', []);
40 pyroutes.register('admin_audit_logs', '/_admin/audit_logs', []);
39 pyroutes.register('pull_requests_global_0', '/_admin/pull_requests/%(pull_request_id)s', ['pull_request_id']);
41 pyroutes.register('pull_requests_global_0', '/_admin/pull_requests/%(pull_request_id)s', ['pull_request_id']);
40 pyroutes.register('pull_requests_global_1', '/_admin/pull-requests/%(pull_request_id)s', ['pull_request_id']);
42 pyroutes.register('pull_requests_global_1', '/_admin/pull-requests/%(pull_request_id)s', ['pull_request_id']);
41 pyroutes.register('pull_requests_global', '/_admin/pull-request/%(pull_request_id)s', ['pull_request_id']);
43 pyroutes.register('pull_requests_global', '/_admin/pull-request/%(pull_request_id)s', ['pull_request_id']);
42 pyroutes.register('admin_settings_open_source', '/_admin/settings/open_source', []);
44 pyroutes.register('admin_settings_open_source', '/_admin/settings/open_source', []);
43 pyroutes.register('admin_settings_vcs_svn_generate_cfg', '/_admin/settings/vcs/svn_generate_cfg', []);
45 pyroutes.register('admin_settings_vcs_svn_generate_cfg', '/_admin/settings/vcs/svn_generate_cfg', []);
44 pyroutes.register('admin_settings_system', '/_admin/settings/system', []);
46 pyroutes.register('admin_settings_system', '/_admin/settings/system', []);
45 pyroutes.register('admin_settings_system_update', '/_admin/settings/system/updates', []);
47 pyroutes.register('admin_settings_system_update', '/_admin/settings/system/updates', []);
46 pyroutes.register('admin_settings_sessions', '/_admin/settings/sessions', []);
48 pyroutes.register('admin_settings_sessions', '/_admin/settings/sessions', []);
47 pyroutes.register('admin_settings_sessions_cleanup', '/_admin/settings/sessions/cleanup', []);
49 pyroutes.register('admin_settings_sessions_cleanup', '/_admin/settings/sessions/cleanup', []);
48 pyroutes.register('admin_settings_process_management', '/_admin/settings/process_management', []);
50 pyroutes.register('admin_settings_process_management', '/_admin/settings/process_management', []);
49 pyroutes.register('admin_settings_process_management_signal', '/_admin/settings/process_management/signal', []);
51 pyroutes.register('admin_settings_process_management_signal', '/_admin/settings/process_management/signal', []);
50 pyroutes.register('admin_defaults_repositories', '/_admin/defaults/repositories', []);
52 pyroutes.register('admin_defaults_repositories', '/_admin/defaults/repositories', []);
51 pyroutes.register('admin_defaults_repositories_update', '/_admin/defaults/repositories/update', []);
53 pyroutes.register('admin_defaults_repositories_update', '/_admin/defaults/repositories/update', []);
52 pyroutes.register('admin_permissions_application', '/_admin/permissions/application', []);
54 pyroutes.register('admin_permissions_application', '/_admin/permissions/application', []);
53 pyroutes.register('admin_permissions_application_update', '/_admin/permissions/application/update', []);
55 pyroutes.register('admin_permissions_application_update', '/_admin/permissions/application/update', []);
54 pyroutes.register('admin_permissions_global', '/_admin/permissions/global', []);
56 pyroutes.register('admin_permissions_global', '/_admin/permissions/global', []);
55 pyroutes.register('admin_permissions_global_update', '/_admin/permissions/global/update', []);
57 pyroutes.register('admin_permissions_global_update', '/_admin/permissions/global/update', []);
56 pyroutes.register('admin_permissions_object', '/_admin/permissions/object', []);
58 pyroutes.register('admin_permissions_object', '/_admin/permissions/object', []);
57 pyroutes.register('admin_permissions_object_update', '/_admin/permissions/object/update', []);
59 pyroutes.register('admin_permissions_object_update', '/_admin/permissions/object/update', []);
58 pyroutes.register('admin_permissions_ips', '/_admin/permissions/ips', []);
60 pyroutes.register('admin_permissions_ips', '/_admin/permissions/ips', []);
59 pyroutes.register('admin_permissions_overview', '/_admin/permissions/overview', []);
61 pyroutes.register('admin_permissions_overview', '/_admin/permissions/overview', []);
60 pyroutes.register('admin_permissions_auth_token_access', '/_admin/permissions/auth_token_access', []);
62 pyroutes.register('admin_permissions_auth_token_access', '/_admin/permissions/auth_token_access', []);
61 pyroutes.register('admin_permissions_ssh_keys', '/_admin/permissions/ssh_keys', []);
63 pyroutes.register('admin_permissions_ssh_keys', '/_admin/permissions/ssh_keys', []);
62 pyroutes.register('admin_permissions_ssh_keys_data', '/_admin/permissions/ssh_keys/data', []);
64 pyroutes.register('admin_permissions_ssh_keys_data', '/_admin/permissions/ssh_keys/data', []);
63 pyroutes.register('admin_permissions_ssh_keys_update', '/_admin/permissions/ssh_keys/update', []);
65 pyroutes.register('admin_permissions_ssh_keys_update', '/_admin/permissions/ssh_keys/update', []);
64 pyroutes.register('users', '/_admin/users', []);
66 pyroutes.register('users', '/_admin/users', []);
65 pyroutes.register('users_data', '/_admin/users_data', []);
67 pyroutes.register('users_data', '/_admin/users_data', []);
66 pyroutes.register('edit_user_auth_tokens', '/_admin/users/%(user_id)s/edit/auth_tokens', ['user_id']);
68 pyroutes.register('edit_user_auth_tokens', '/_admin/users/%(user_id)s/edit/auth_tokens', ['user_id']);
67 pyroutes.register('edit_user_auth_tokens_add', '/_admin/users/%(user_id)s/edit/auth_tokens/new', ['user_id']);
69 pyroutes.register('edit_user_auth_tokens_add', '/_admin/users/%(user_id)s/edit/auth_tokens/new', ['user_id']);
68 pyroutes.register('edit_user_auth_tokens_delete', '/_admin/users/%(user_id)s/edit/auth_tokens/delete', ['user_id']);
70 pyroutes.register('edit_user_auth_tokens_delete', '/_admin/users/%(user_id)s/edit/auth_tokens/delete', ['user_id']);
69 pyroutes.register('edit_user_ssh_keys', '/_admin/users/%(user_id)s/edit/ssh_keys', ['user_id']);
71 pyroutes.register('edit_user_ssh_keys', '/_admin/users/%(user_id)s/edit/ssh_keys', ['user_id']);
70 pyroutes.register('edit_user_ssh_keys_generate_keypair', '/_admin/users/%(user_id)s/edit/ssh_keys/generate', ['user_id']);
72 pyroutes.register('edit_user_ssh_keys_generate_keypair', '/_admin/users/%(user_id)s/edit/ssh_keys/generate', ['user_id']);
71 pyroutes.register('edit_user_ssh_keys_add', '/_admin/users/%(user_id)s/edit/ssh_keys/new', ['user_id']);
73 pyroutes.register('edit_user_ssh_keys_add', '/_admin/users/%(user_id)s/edit/ssh_keys/new', ['user_id']);
72 pyroutes.register('edit_user_ssh_keys_delete', '/_admin/users/%(user_id)s/edit/ssh_keys/delete', ['user_id']);
74 pyroutes.register('edit_user_ssh_keys_delete', '/_admin/users/%(user_id)s/edit/ssh_keys/delete', ['user_id']);
73 pyroutes.register('edit_user_emails', '/_admin/users/%(user_id)s/edit/emails', ['user_id']);
75 pyroutes.register('edit_user_emails', '/_admin/users/%(user_id)s/edit/emails', ['user_id']);
74 pyroutes.register('edit_user_emails_add', '/_admin/users/%(user_id)s/edit/emails/new', ['user_id']);
76 pyroutes.register('edit_user_emails_add', '/_admin/users/%(user_id)s/edit/emails/new', ['user_id']);
75 pyroutes.register('edit_user_emails_delete', '/_admin/users/%(user_id)s/edit/emails/delete', ['user_id']);
77 pyroutes.register('edit_user_emails_delete', '/_admin/users/%(user_id)s/edit/emails/delete', ['user_id']);
76 pyroutes.register('edit_user_ips', '/_admin/users/%(user_id)s/edit/ips', ['user_id']);
78 pyroutes.register('edit_user_ips', '/_admin/users/%(user_id)s/edit/ips', ['user_id']);
77 pyroutes.register('edit_user_ips_add', '/_admin/users/%(user_id)s/edit/ips/new', ['user_id']);
79 pyroutes.register('edit_user_ips_add', '/_admin/users/%(user_id)s/edit/ips/new', ['user_id']);
78 pyroutes.register('edit_user_ips_delete', '/_admin/users/%(user_id)s/edit/ips/delete', ['user_id']);
80 pyroutes.register('edit_user_ips_delete', '/_admin/users/%(user_id)s/edit/ips/delete', ['user_id']);
79 pyroutes.register('edit_user_perms_summary', '/_admin/users/%(user_id)s/edit/permissions_summary', ['user_id']);
81 pyroutes.register('edit_user_perms_summary', '/_admin/users/%(user_id)s/edit/permissions_summary', ['user_id']);
80 pyroutes.register('edit_user_perms_summary_json', '/_admin/users/%(user_id)s/edit/permissions_summary/json', ['user_id']);
82 pyroutes.register('edit_user_perms_summary_json', '/_admin/users/%(user_id)s/edit/permissions_summary/json', ['user_id']);
81 pyroutes.register('edit_user_groups_management', '/_admin/users/%(user_id)s/edit/groups_management', ['user_id']);
83 pyroutes.register('edit_user_groups_management', '/_admin/users/%(user_id)s/edit/groups_management', ['user_id']);
82 pyroutes.register('edit_user_groups_management_updates', '/_admin/users/%(user_id)s/edit/edit_user_groups_management/updates', ['user_id']);
84 pyroutes.register('edit_user_groups_management_updates', '/_admin/users/%(user_id)s/edit/edit_user_groups_management/updates', ['user_id']);
83 pyroutes.register('edit_user_audit_logs', '/_admin/users/%(user_id)s/edit/audit', ['user_id']);
85 pyroutes.register('edit_user_audit_logs', '/_admin/users/%(user_id)s/edit/audit', ['user_id']);
84 pyroutes.register('user_groups', '/_admin/user_groups', []);
86 pyroutes.register('user_groups', '/_admin/user_groups', []);
85 pyroutes.register('user_groups_data', '/_admin/user_groups_data', []);
87 pyroutes.register('user_groups_data', '/_admin/user_groups_data', []);
86 pyroutes.register('user_groups_new', '/_admin/user_groups/new', []);
88 pyroutes.register('user_groups_new', '/_admin/user_groups/new', []);
87 pyroutes.register('user_groups_create', '/_admin/user_groups/create', []);
89 pyroutes.register('user_groups_create', '/_admin/user_groups/create', []);
88 pyroutes.register('repos', '/_admin/repos', []);
90 pyroutes.register('repos', '/_admin/repos', []);
89 pyroutes.register('repo_new', '/_admin/repos/new', []);
91 pyroutes.register('repo_new', '/_admin/repos/new', []);
90 pyroutes.register('repo_create', '/_admin/repos/create', []);
92 pyroutes.register('repo_create', '/_admin/repos/create', []);
91 pyroutes.register('channelstream_connect', '/_admin/channelstream/connect', []);
93 pyroutes.register('channelstream_connect', '/_admin/channelstream/connect', []);
92 pyroutes.register('channelstream_subscribe', '/_admin/channelstream/subscribe', []);
94 pyroutes.register('channelstream_subscribe', '/_admin/channelstream/subscribe', []);
93 pyroutes.register('channelstream_proxy', '/_channelstream', []);
95 pyroutes.register('channelstream_proxy', '/_channelstream', []);
94 pyroutes.register('login', '/_admin/login', []);
96 pyroutes.register('login', '/_admin/login', []);
95 pyroutes.register('logout', '/_admin/logout', []);
97 pyroutes.register('logout', '/_admin/logout', []);
96 pyroutes.register('register', '/_admin/register', []);
98 pyroutes.register('register', '/_admin/register', []);
97 pyroutes.register('reset_password', '/_admin/password_reset', []);
99 pyroutes.register('reset_password', '/_admin/password_reset', []);
98 pyroutes.register('reset_password_confirmation', '/_admin/password_reset_confirmation', []);
100 pyroutes.register('reset_password_confirmation', '/_admin/password_reset_confirmation', []);
99 pyroutes.register('home', '/', []);
101 pyroutes.register('home', '/', []);
100 pyroutes.register('user_autocomplete_data', '/_users', []);
102 pyroutes.register('user_autocomplete_data', '/_users', []);
101 pyroutes.register('user_group_autocomplete_data', '/_user_groups', []);
103 pyroutes.register('user_group_autocomplete_data', '/_user_groups', []);
102 pyroutes.register('repo_list_data', '/_repos', []);
104 pyroutes.register('repo_list_data', '/_repos', []);
103 pyroutes.register('goto_switcher_data', '/_goto_data', []);
105 pyroutes.register('goto_switcher_data', '/_goto_data', []);
104 pyroutes.register('journal', '/_admin/journal', []);
106 pyroutes.register('journal', '/_admin/journal', []);
105 pyroutes.register('journal_rss', '/_admin/journal/rss', []);
107 pyroutes.register('journal_rss', '/_admin/journal/rss', []);
106 pyroutes.register('journal_atom', '/_admin/journal/atom', []);
108 pyroutes.register('journal_atom', '/_admin/journal/atom', []);
107 pyroutes.register('journal_public', '/_admin/public_journal', []);
109 pyroutes.register('journal_public', '/_admin/public_journal', []);
108 pyroutes.register('journal_public_atom', '/_admin/public_journal/atom', []);
110 pyroutes.register('journal_public_atom', '/_admin/public_journal/atom', []);
109 pyroutes.register('journal_public_atom_old', '/_admin/public_journal_atom', []);
111 pyroutes.register('journal_public_atom_old', '/_admin/public_journal_atom', []);
110 pyroutes.register('journal_public_rss', '/_admin/public_journal/rss', []);
112 pyroutes.register('journal_public_rss', '/_admin/public_journal/rss', []);
111 pyroutes.register('journal_public_rss_old', '/_admin/public_journal_rss', []);
113 pyroutes.register('journal_public_rss_old', '/_admin/public_journal_rss', []);
112 pyroutes.register('toggle_following', '/_admin/toggle_following', []);
114 pyroutes.register('toggle_following', '/_admin/toggle_following', []);
113 pyroutes.register('repo_creating', '/%(repo_name)s/repo_creating', ['repo_name']);
115 pyroutes.register('repo_creating', '/%(repo_name)s/repo_creating', ['repo_name']);
114 pyroutes.register('repo_creating_check', '/%(repo_name)s/repo_creating_check', ['repo_name']);
116 pyroutes.register('repo_creating_check', '/%(repo_name)s/repo_creating_check', ['repo_name']);
115 pyroutes.register('repo_summary_explicit', '/%(repo_name)s/summary', ['repo_name']);
117 pyroutes.register('repo_summary_explicit', '/%(repo_name)s/summary', ['repo_name']);
116 pyroutes.register('repo_summary_commits', '/%(repo_name)s/summary-commits', ['repo_name']);
118 pyroutes.register('repo_summary_commits', '/%(repo_name)s/summary-commits', ['repo_name']);
117 pyroutes.register('repo_commit', '/%(repo_name)s/changeset/%(commit_id)s', ['repo_name', 'commit_id']);
119 pyroutes.register('repo_commit', '/%(repo_name)s/changeset/%(commit_id)s', ['repo_name', 'commit_id']);
118 pyroutes.register('repo_commit_children', '/%(repo_name)s/changeset_children/%(commit_id)s', ['repo_name', 'commit_id']);
120 pyroutes.register('repo_commit_children', '/%(repo_name)s/changeset_children/%(commit_id)s', ['repo_name', 'commit_id']);
119 pyroutes.register('repo_commit_parents', '/%(repo_name)s/changeset_parents/%(commit_id)s', ['repo_name', 'commit_id']);
121 pyroutes.register('repo_commit_parents', '/%(repo_name)s/changeset_parents/%(commit_id)s', ['repo_name', 'commit_id']);
120 pyroutes.register('repo_commit_raw', '/%(repo_name)s/changeset-diff/%(commit_id)s', ['repo_name', 'commit_id']);
122 pyroutes.register('repo_commit_raw', '/%(repo_name)s/changeset-diff/%(commit_id)s', ['repo_name', 'commit_id']);
121 pyroutes.register('repo_commit_patch', '/%(repo_name)s/changeset-patch/%(commit_id)s', ['repo_name', 'commit_id']);
123 pyroutes.register('repo_commit_patch', '/%(repo_name)s/changeset-patch/%(commit_id)s', ['repo_name', 'commit_id']);
122 pyroutes.register('repo_commit_download', '/%(repo_name)s/changeset-download/%(commit_id)s', ['repo_name', 'commit_id']);
124 pyroutes.register('repo_commit_download', '/%(repo_name)s/changeset-download/%(commit_id)s', ['repo_name', 'commit_id']);
123 pyroutes.register('repo_commit_data', '/%(repo_name)s/changeset-data/%(commit_id)s', ['repo_name', 'commit_id']);
125 pyroutes.register('repo_commit_data', '/%(repo_name)s/changeset-data/%(commit_id)s', ['repo_name', 'commit_id']);
124 pyroutes.register('repo_commit_comment_create', '/%(repo_name)s/changeset/%(commit_id)s/comment/create', ['repo_name', 'commit_id']);
126 pyroutes.register('repo_commit_comment_create', '/%(repo_name)s/changeset/%(commit_id)s/comment/create', ['repo_name', 'commit_id']);
125 pyroutes.register('repo_commit_comment_preview', '/%(repo_name)s/changeset/%(commit_id)s/comment/preview', ['repo_name', 'commit_id']);
127 pyroutes.register('repo_commit_comment_preview', '/%(repo_name)s/changeset/%(commit_id)s/comment/preview', ['repo_name', 'commit_id']);
126 pyroutes.register('repo_commit_comment_delete', '/%(repo_name)s/changeset/%(commit_id)s/comment/%(comment_id)s/delete', ['repo_name', 'commit_id', 'comment_id']);
128 pyroutes.register('repo_commit_comment_delete', '/%(repo_name)s/changeset/%(commit_id)s/comment/%(comment_id)s/delete', ['repo_name', 'commit_id', 'comment_id']);
127 pyroutes.register('repo_commit_raw_deprecated', '/%(repo_name)s/raw-changeset/%(commit_id)s', ['repo_name', 'commit_id']);
129 pyroutes.register('repo_commit_raw_deprecated', '/%(repo_name)s/raw-changeset/%(commit_id)s', ['repo_name', 'commit_id']);
128 pyroutes.register('repo_archivefile', '/%(repo_name)s/archive/%(fname)s', ['repo_name', 'fname']);
130 pyroutes.register('repo_archivefile', '/%(repo_name)s/archive/%(fname)s', ['repo_name', 'fname']);
129 pyroutes.register('repo_files_diff', '/%(repo_name)s/diff/%(f_path)s', ['repo_name', 'f_path']);
131 pyroutes.register('repo_files_diff', '/%(repo_name)s/diff/%(f_path)s', ['repo_name', 'f_path']);
130 pyroutes.register('repo_files_diff_2way_redirect', '/%(repo_name)s/diff-2way/%(f_path)s', ['repo_name', 'f_path']);
132 pyroutes.register('repo_files_diff_2way_redirect', '/%(repo_name)s/diff-2way/%(f_path)s', ['repo_name', 'f_path']);
131 pyroutes.register('repo_files', '/%(repo_name)s/files/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
133 pyroutes.register('repo_files', '/%(repo_name)s/files/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
132 pyroutes.register('repo_files:default_path', '/%(repo_name)s/files/%(commit_id)s/', ['repo_name', 'commit_id']);
134 pyroutes.register('repo_files:default_path', '/%(repo_name)s/files/%(commit_id)s/', ['repo_name', 'commit_id']);
133 pyroutes.register('repo_files:default_commit', '/%(repo_name)s/files', ['repo_name']);
135 pyroutes.register('repo_files:default_commit', '/%(repo_name)s/files', ['repo_name']);
134 pyroutes.register('repo_files:rendered', '/%(repo_name)s/render/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
136 pyroutes.register('repo_files:rendered', '/%(repo_name)s/render/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
135 pyroutes.register('repo_files:annotated', '/%(repo_name)s/annotate/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
137 pyroutes.register('repo_files:annotated', '/%(repo_name)s/annotate/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
136 pyroutes.register('repo_files:annotated_previous', '/%(repo_name)s/annotate-previous/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
138 pyroutes.register('repo_files:annotated_previous', '/%(repo_name)s/annotate-previous/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
137 pyroutes.register('repo_nodetree_full', '/%(repo_name)s/nodetree_full/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
139 pyroutes.register('repo_nodetree_full', '/%(repo_name)s/nodetree_full/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
138 pyroutes.register('repo_nodetree_full:default_path', '/%(repo_name)s/nodetree_full/%(commit_id)s/', ['repo_name', 'commit_id']);
140 pyroutes.register('repo_nodetree_full:default_path', '/%(repo_name)s/nodetree_full/%(commit_id)s/', ['repo_name', 'commit_id']);
139 pyroutes.register('repo_files_nodelist', '/%(repo_name)s/nodelist/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
141 pyroutes.register('repo_files_nodelist', '/%(repo_name)s/nodelist/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
140 pyroutes.register('repo_file_raw', '/%(repo_name)s/raw/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
142 pyroutes.register('repo_file_raw', '/%(repo_name)s/raw/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
141 pyroutes.register('repo_file_download', '/%(repo_name)s/download/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
143 pyroutes.register('repo_file_download', '/%(repo_name)s/download/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
142 pyroutes.register('repo_file_download:legacy', '/%(repo_name)s/rawfile/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
144 pyroutes.register('repo_file_download:legacy', '/%(repo_name)s/rawfile/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
143 pyroutes.register('repo_file_history', '/%(repo_name)s/history/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
145 pyroutes.register('repo_file_history', '/%(repo_name)s/history/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
144 pyroutes.register('repo_file_authors', '/%(repo_name)s/authors/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
146 pyroutes.register('repo_file_authors', '/%(repo_name)s/authors/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
145 pyroutes.register('repo_files_remove_file', '/%(repo_name)s/remove_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
147 pyroutes.register('repo_files_remove_file', '/%(repo_name)s/remove_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
146 pyroutes.register('repo_files_delete_file', '/%(repo_name)s/delete_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
148 pyroutes.register('repo_files_delete_file', '/%(repo_name)s/delete_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
147 pyroutes.register('repo_files_edit_file', '/%(repo_name)s/edit_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
149 pyroutes.register('repo_files_edit_file', '/%(repo_name)s/edit_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
148 pyroutes.register('repo_files_update_file', '/%(repo_name)s/update_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
150 pyroutes.register('repo_files_update_file', '/%(repo_name)s/update_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
149 pyroutes.register('repo_files_add_file', '/%(repo_name)s/add_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
151 pyroutes.register('repo_files_add_file', '/%(repo_name)s/add_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
150 pyroutes.register('repo_files_create_file', '/%(repo_name)s/create_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
152 pyroutes.register('repo_files_create_file', '/%(repo_name)s/create_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
151 pyroutes.register('repo_refs_data', '/%(repo_name)s/refs-data', ['repo_name']);
153 pyroutes.register('repo_refs_data', '/%(repo_name)s/refs-data', ['repo_name']);
152 pyroutes.register('repo_refs_changelog_data', '/%(repo_name)s/refs-data-changelog', ['repo_name']);
154 pyroutes.register('repo_refs_changelog_data', '/%(repo_name)s/refs-data-changelog', ['repo_name']);
153 pyroutes.register('repo_stats', '/%(repo_name)s/repo_stats/%(commit_id)s', ['repo_name', 'commit_id']);
155 pyroutes.register('repo_stats', '/%(repo_name)s/repo_stats/%(commit_id)s', ['repo_name', 'commit_id']);
154 pyroutes.register('repo_changelog', '/%(repo_name)s/changelog', ['repo_name']);
156 pyroutes.register('repo_changelog', '/%(repo_name)s/changelog', ['repo_name']);
155 pyroutes.register('repo_changelog_file', '/%(repo_name)s/changelog/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
157 pyroutes.register('repo_changelog_file', '/%(repo_name)s/changelog/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
156 pyroutes.register('repo_changelog_elements', '/%(repo_name)s/changelog_elements', ['repo_name']);
158 pyroutes.register('repo_changelog_elements', '/%(repo_name)s/changelog_elements', ['repo_name']);
157 pyroutes.register('repo_compare_select', '/%(repo_name)s/compare', ['repo_name']);
159 pyroutes.register('repo_compare_select', '/%(repo_name)s/compare', ['repo_name']);
158 pyroutes.register('repo_compare', '/%(repo_name)s/compare/%(source_ref_type)s@%(source_ref)s...%(target_ref_type)s@%(target_ref)s', ['repo_name', 'source_ref_type', 'source_ref', 'target_ref_type', 'target_ref']);
160 pyroutes.register('repo_compare', '/%(repo_name)s/compare/%(source_ref_type)s@%(source_ref)s...%(target_ref_type)s@%(target_ref)s', ['repo_name', 'source_ref_type', 'source_ref', 'target_ref_type', 'target_ref']);
159 pyroutes.register('tags_home', '/%(repo_name)s/tags', ['repo_name']);
161 pyroutes.register('tags_home', '/%(repo_name)s/tags', ['repo_name']);
160 pyroutes.register('branches_home', '/%(repo_name)s/branches', ['repo_name']);
162 pyroutes.register('branches_home', '/%(repo_name)s/branches', ['repo_name']);
161 pyroutes.register('bookmarks_home', '/%(repo_name)s/bookmarks', ['repo_name']);
163 pyroutes.register('bookmarks_home', '/%(repo_name)s/bookmarks', ['repo_name']);
162 pyroutes.register('repo_fork_new', '/%(repo_name)s/fork', ['repo_name']);
164 pyroutes.register('repo_fork_new', '/%(repo_name)s/fork', ['repo_name']);
163 pyroutes.register('repo_fork_create', '/%(repo_name)s/fork/create', ['repo_name']);
165 pyroutes.register('repo_fork_create', '/%(repo_name)s/fork/create', ['repo_name']);
164 pyroutes.register('repo_forks_show_all', '/%(repo_name)s/forks', ['repo_name']);
166 pyroutes.register('repo_forks_show_all', '/%(repo_name)s/forks', ['repo_name']);
165 pyroutes.register('repo_forks_data', '/%(repo_name)s/forks/data', ['repo_name']);
167 pyroutes.register('repo_forks_data', '/%(repo_name)s/forks/data', ['repo_name']);
166 pyroutes.register('pullrequest_show', '/%(repo_name)s/pull-request/%(pull_request_id)s', ['repo_name', 'pull_request_id']);
168 pyroutes.register('pullrequest_show', '/%(repo_name)s/pull-request/%(pull_request_id)s', ['repo_name', 'pull_request_id']);
167 pyroutes.register('pullrequest_show_all', '/%(repo_name)s/pull-request', ['repo_name']);
169 pyroutes.register('pullrequest_show_all', '/%(repo_name)s/pull-request', ['repo_name']);
168 pyroutes.register('pullrequest_show_all_data', '/%(repo_name)s/pull-request-data', ['repo_name']);
170 pyroutes.register('pullrequest_show_all_data', '/%(repo_name)s/pull-request-data', ['repo_name']);
169 pyroutes.register('pullrequest_repo_refs', '/%(repo_name)s/pull-request/refs/%(target_repo_name)s', ['repo_name', 'target_repo_name']);
171 pyroutes.register('pullrequest_repo_refs', '/%(repo_name)s/pull-request/refs/%(target_repo_name)s', ['repo_name', 'target_repo_name']);
170 pyroutes.register('pullrequest_repo_destinations', '/%(repo_name)s/pull-request/repo-destinations', ['repo_name']);
172 pyroutes.register('pullrequest_repo_destinations', '/%(repo_name)s/pull-request/repo-destinations', ['repo_name']);
171 pyroutes.register('pullrequest_new', '/%(repo_name)s/pull-request/new', ['repo_name']);
173 pyroutes.register('pullrequest_new', '/%(repo_name)s/pull-request/new', ['repo_name']);
172 pyroutes.register('pullrequest_create', '/%(repo_name)s/pull-request/create', ['repo_name']);
174 pyroutes.register('pullrequest_create', '/%(repo_name)s/pull-request/create', ['repo_name']);
173 pyroutes.register('pullrequest_update', '/%(repo_name)s/pull-request/%(pull_request_id)s/update', ['repo_name', 'pull_request_id']);
175 pyroutes.register('pullrequest_update', '/%(repo_name)s/pull-request/%(pull_request_id)s/update', ['repo_name', 'pull_request_id']);
174 pyroutes.register('pullrequest_merge', '/%(repo_name)s/pull-request/%(pull_request_id)s/merge', ['repo_name', 'pull_request_id']);
176 pyroutes.register('pullrequest_merge', '/%(repo_name)s/pull-request/%(pull_request_id)s/merge', ['repo_name', 'pull_request_id']);
175 pyroutes.register('pullrequest_delete', '/%(repo_name)s/pull-request/%(pull_request_id)s/delete', ['repo_name', 'pull_request_id']);
177 pyroutes.register('pullrequest_delete', '/%(repo_name)s/pull-request/%(pull_request_id)s/delete', ['repo_name', 'pull_request_id']);
176 pyroutes.register('pullrequest_comment_create', '/%(repo_name)s/pull-request/%(pull_request_id)s/comment', ['repo_name', 'pull_request_id']);
178 pyroutes.register('pullrequest_comment_create', '/%(repo_name)s/pull-request/%(pull_request_id)s/comment', ['repo_name', 'pull_request_id']);
177 pyroutes.register('pullrequest_comment_delete', '/%(repo_name)s/pull-request/%(pull_request_id)s/comment/%(comment_id)s/delete', ['repo_name', 'pull_request_id', 'comment_id']);
179 pyroutes.register('pullrequest_comment_delete', '/%(repo_name)s/pull-request/%(pull_request_id)s/comment/%(comment_id)s/delete', ['repo_name', 'pull_request_id', 'comment_id']);
178 pyroutes.register('edit_repo', '/%(repo_name)s/settings', ['repo_name']);
180 pyroutes.register('edit_repo', '/%(repo_name)s/settings', ['repo_name']);
179 pyroutes.register('edit_repo_advanced', '/%(repo_name)s/settings/advanced', ['repo_name']);
181 pyroutes.register('edit_repo_advanced', '/%(repo_name)s/settings/advanced', ['repo_name']);
180 pyroutes.register('edit_repo_advanced_delete', '/%(repo_name)s/settings/advanced/delete', ['repo_name']);
182 pyroutes.register('edit_repo_advanced_delete', '/%(repo_name)s/settings/advanced/delete', ['repo_name']);
181 pyroutes.register('edit_repo_advanced_locking', '/%(repo_name)s/settings/advanced/locking', ['repo_name']);
183 pyroutes.register('edit_repo_advanced_locking', '/%(repo_name)s/settings/advanced/locking', ['repo_name']);
182 pyroutes.register('edit_repo_advanced_journal', '/%(repo_name)s/settings/advanced/journal', ['repo_name']);
184 pyroutes.register('edit_repo_advanced_journal', '/%(repo_name)s/settings/advanced/journal', ['repo_name']);
183 pyroutes.register('edit_repo_advanced_fork', '/%(repo_name)s/settings/advanced/fork', ['repo_name']);
185 pyroutes.register('edit_repo_advanced_fork', '/%(repo_name)s/settings/advanced/fork', ['repo_name']);
184 pyroutes.register('edit_repo_caches', '/%(repo_name)s/settings/caches', ['repo_name']);
186 pyroutes.register('edit_repo_caches', '/%(repo_name)s/settings/caches', ['repo_name']);
185 pyroutes.register('edit_repo_perms', '/%(repo_name)s/settings/permissions', ['repo_name']);
187 pyroutes.register('edit_repo_perms', '/%(repo_name)s/settings/permissions', ['repo_name']);
186 pyroutes.register('edit_repo_maintenance', '/%(repo_name)s/settings/maintenance', ['repo_name']);
188 pyroutes.register('edit_repo_maintenance', '/%(repo_name)s/settings/maintenance', ['repo_name']);
187 pyroutes.register('edit_repo_maintenance_execute', '/%(repo_name)s/settings/maintenance/execute', ['repo_name']);
189 pyroutes.register('edit_repo_maintenance_execute', '/%(repo_name)s/settings/maintenance/execute', ['repo_name']);
188 pyroutes.register('edit_repo_fields', '/%(repo_name)s/settings/fields', ['repo_name']);
190 pyroutes.register('edit_repo_fields', '/%(repo_name)s/settings/fields', ['repo_name']);
189 pyroutes.register('edit_repo_fields_create', '/%(repo_name)s/settings/fields/create', ['repo_name']);
191 pyroutes.register('edit_repo_fields_create', '/%(repo_name)s/settings/fields/create', ['repo_name']);
190 pyroutes.register('edit_repo_fields_delete', '/%(repo_name)s/settings/fields/%(field_id)s/delete', ['repo_name', 'field_id']);
192 pyroutes.register('edit_repo_fields_delete', '/%(repo_name)s/settings/fields/%(field_id)s/delete', ['repo_name', 'field_id']);
191 pyroutes.register('repo_edit_toggle_locking', '/%(repo_name)s/settings/toggle_locking', ['repo_name']);
193 pyroutes.register('repo_edit_toggle_locking', '/%(repo_name)s/settings/toggle_locking', ['repo_name']);
192 pyroutes.register('edit_repo_remote', '/%(repo_name)s/settings/remote', ['repo_name']);
194 pyroutes.register('edit_repo_remote', '/%(repo_name)s/settings/remote', ['repo_name']);
193 pyroutes.register('edit_repo_remote_pull', '/%(repo_name)s/settings/remote/pull', ['repo_name']);
195 pyroutes.register('edit_repo_remote_pull', '/%(repo_name)s/settings/remote/pull', ['repo_name']);
194 pyroutes.register('edit_repo_statistics', '/%(repo_name)s/settings/statistics', ['repo_name']);
196 pyroutes.register('edit_repo_statistics', '/%(repo_name)s/settings/statistics', ['repo_name']);
195 pyroutes.register('edit_repo_statistics_reset', '/%(repo_name)s/settings/statistics/update', ['repo_name']);
197 pyroutes.register('edit_repo_statistics_reset', '/%(repo_name)s/settings/statistics/update', ['repo_name']);
196 pyroutes.register('edit_repo_issuetracker', '/%(repo_name)s/settings/issue_trackers', ['repo_name']);
198 pyroutes.register('edit_repo_issuetracker', '/%(repo_name)s/settings/issue_trackers', ['repo_name']);
197 pyroutes.register('edit_repo_issuetracker_test', '/%(repo_name)s/settings/issue_trackers/test', ['repo_name']);
199 pyroutes.register('edit_repo_issuetracker_test', '/%(repo_name)s/settings/issue_trackers/test', ['repo_name']);
198 pyroutes.register('edit_repo_issuetracker_delete', '/%(repo_name)s/settings/issue_trackers/delete', ['repo_name']);
200 pyroutes.register('edit_repo_issuetracker_delete', '/%(repo_name)s/settings/issue_trackers/delete', ['repo_name']);
199 pyroutes.register('edit_repo_issuetracker_update', '/%(repo_name)s/settings/issue_trackers/update', ['repo_name']);
201 pyroutes.register('edit_repo_issuetracker_update', '/%(repo_name)s/settings/issue_trackers/update', ['repo_name']);
200 pyroutes.register('edit_repo_vcs', '/%(repo_name)s/settings/vcs', ['repo_name']);
202 pyroutes.register('edit_repo_vcs', '/%(repo_name)s/settings/vcs', ['repo_name']);
201 pyroutes.register('edit_repo_vcs_update', '/%(repo_name)s/settings/vcs/update', ['repo_name']);
203 pyroutes.register('edit_repo_vcs_update', '/%(repo_name)s/settings/vcs/update', ['repo_name']);
202 pyroutes.register('edit_repo_vcs_svn_pattern_delete', '/%(repo_name)s/settings/vcs/svn_pattern/delete', ['repo_name']);
204 pyroutes.register('edit_repo_vcs_svn_pattern_delete', '/%(repo_name)s/settings/vcs/svn_pattern/delete', ['repo_name']);
203 pyroutes.register('repo_reviewers', '/%(repo_name)s/settings/review/rules', ['repo_name']);
205 pyroutes.register('repo_reviewers', '/%(repo_name)s/settings/review/rules', ['repo_name']);
204 pyroutes.register('repo_default_reviewers_data', '/%(repo_name)s/settings/review/default-reviewers', ['repo_name']);
206 pyroutes.register('repo_default_reviewers_data', '/%(repo_name)s/settings/review/default-reviewers', ['repo_name']);
205 pyroutes.register('edit_repo_strip', '/%(repo_name)s/settings/strip', ['repo_name']);
207 pyroutes.register('edit_repo_strip', '/%(repo_name)s/settings/strip', ['repo_name']);
206 pyroutes.register('strip_check', '/%(repo_name)s/settings/strip_check', ['repo_name']);
208 pyroutes.register('strip_check', '/%(repo_name)s/settings/strip_check', ['repo_name']);
207 pyroutes.register('strip_execute', '/%(repo_name)s/settings/strip_execute', ['repo_name']);
209 pyroutes.register('strip_execute', '/%(repo_name)s/settings/strip_execute', ['repo_name']);
208 pyroutes.register('rss_feed_home', '/%(repo_name)s/feed/rss', ['repo_name']);
210 pyroutes.register('rss_feed_home', '/%(repo_name)s/feed/rss', ['repo_name']);
209 pyroutes.register('atom_feed_home', '/%(repo_name)s/feed/atom', ['repo_name']);
211 pyroutes.register('atom_feed_home', '/%(repo_name)s/feed/atom', ['repo_name']);
210 pyroutes.register('repo_summary', '/%(repo_name)s', ['repo_name']);
212 pyroutes.register('repo_summary', '/%(repo_name)s', ['repo_name']);
211 pyroutes.register('repo_summary_slash', '/%(repo_name)s/', ['repo_name']);
213 pyroutes.register('repo_summary_slash', '/%(repo_name)s/', ['repo_name']);
212 pyroutes.register('repo_group_home', '/%(repo_group_name)s', ['repo_group_name']);
214 pyroutes.register('repo_group_home', '/%(repo_group_name)s', ['repo_group_name']);
213 pyroutes.register('repo_group_home_slash', '/%(repo_group_name)s/', ['repo_group_name']);
215 pyroutes.register('repo_group_home_slash', '/%(repo_group_name)s/', ['repo_group_name']);
214 pyroutes.register('user_group_members_data', '/_admin/user_groups/%(user_group_id)s/members', ['user_group_id']);
216 pyroutes.register('user_group_members_data', '/_admin/user_groups/%(user_group_id)s/members', ['user_group_id']);
215 pyroutes.register('edit_user_group_perms_summary', '/_admin/user_groups/%(user_group_id)s/edit/permissions_summary', ['user_group_id']);
217 pyroutes.register('edit_user_group_perms_summary', '/_admin/user_groups/%(user_group_id)s/edit/permissions_summary', ['user_group_id']);
216 pyroutes.register('edit_user_group_perms_summary_json', '/_admin/user_groups/%(user_group_id)s/edit/permissions_summary/json', ['user_group_id']);
218 pyroutes.register('edit_user_group_perms_summary_json', '/_admin/user_groups/%(user_group_id)s/edit/permissions_summary/json', ['user_group_id']);
217 pyroutes.register('edit_user_group', '/_admin/user_groups/%(user_group_id)s/edit', ['user_group_id']);
219 pyroutes.register('edit_user_group', '/_admin/user_groups/%(user_group_id)s/edit', ['user_group_id']);
218 pyroutes.register('user_groups_update', '/_admin/user_groups/%(user_group_id)s/update', ['user_group_id']);
220 pyroutes.register('user_groups_update', '/_admin/user_groups/%(user_group_id)s/update', ['user_group_id']);
219 pyroutes.register('edit_user_group_global_perms', '/_admin/user_groups/%(user_group_id)s/edit/global_permissions', ['user_group_id']);
221 pyroutes.register('edit_user_group_global_perms', '/_admin/user_groups/%(user_group_id)s/edit/global_permissions', ['user_group_id']);
220 pyroutes.register('edit_user_group_global_perms_update', '/_admin/user_groups/%(user_group_id)s/edit/global_permissions/update', ['user_group_id']);
222 pyroutes.register('edit_user_group_global_perms_update', '/_admin/user_groups/%(user_group_id)s/edit/global_permissions/update', ['user_group_id']);
221 pyroutes.register('edit_user_group_perms', '/_admin/user_groups/%(user_group_id)s/edit/permissions', ['user_group_id']);
223 pyroutes.register('edit_user_group_perms', '/_admin/user_groups/%(user_group_id)s/edit/permissions', ['user_group_id']);
222 pyroutes.register('edit_user_group_perms_update', '/_admin/user_groups/%(user_group_id)s/edit/permissions/update', ['user_group_id']);
224 pyroutes.register('edit_user_group_perms_update', '/_admin/user_groups/%(user_group_id)s/edit/permissions/update', ['user_group_id']);
223 pyroutes.register('edit_user_group_advanced', '/_admin/user_groups/%(user_group_id)s/edit/advanced', ['user_group_id']);
225 pyroutes.register('edit_user_group_advanced', '/_admin/user_groups/%(user_group_id)s/edit/advanced', ['user_group_id']);
224 pyroutes.register('edit_user_group_advanced_sync', '/_admin/user_groups/%(user_group_id)s/edit/advanced/sync', ['user_group_id']);
226 pyroutes.register('edit_user_group_advanced_sync', '/_admin/user_groups/%(user_group_id)s/edit/advanced/sync', ['user_group_id']);
225 pyroutes.register('user_groups_delete', '/_admin/user_groups/%(user_group_id)s/delete', ['user_group_id']);
227 pyroutes.register('user_groups_delete', '/_admin/user_groups/%(user_group_id)s/delete', ['user_group_id']);
226 pyroutes.register('search', '/_admin/search', []);
228 pyroutes.register('search', '/_admin/search', []);
227 pyroutes.register('search_repo', '/%(repo_name)s/search', ['repo_name']);
229 pyroutes.register('search_repo', '/%(repo_name)s/search', ['repo_name']);
228 pyroutes.register('user_profile', '/_profiles/%(username)s', ['username']);
230 pyroutes.register('user_profile', '/_profiles/%(username)s', ['username']);
229 pyroutes.register('my_account_profile', '/_admin/my_account/profile', []);
231 pyroutes.register('my_account_profile', '/_admin/my_account/profile', []);
230 pyroutes.register('my_account_edit', '/_admin/my_account/edit', []);
232 pyroutes.register('my_account_edit', '/_admin/my_account/edit', []);
231 pyroutes.register('my_account_update', '/_admin/my_account/update', []);
233 pyroutes.register('my_account_update', '/_admin/my_account/update', []);
232 pyroutes.register('my_account_password', '/_admin/my_account/password', []);
234 pyroutes.register('my_account_password', '/_admin/my_account/password', []);
233 pyroutes.register('my_account_password_update', '/_admin/my_account/password/update', []);
235 pyroutes.register('my_account_password_update', '/_admin/my_account/password/update', []);
234 pyroutes.register('my_account_auth_tokens', '/_admin/my_account/auth_tokens', []);
236 pyroutes.register('my_account_auth_tokens', '/_admin/my_account/auth_tokens', []);
235 pyroutes.register('my_account_auth_tokens_add', '/_admin/my_account/auth_tokens/new', []);
237 pyroutes.register('my_account_auth_tokens_add', '/_admin/my_account/auth_tokens/new', []);
236 pyroutes.register('my_account_auth_tokens_delete', '/_admin/my_account/auth_tokens/delete', []);
238 pyroutes.register('my_account_auth_tokens_delete', '/_admin/my_account/auth_tokens/delete', []);
237 pyroutes.register('my_account_ssh_keys', '/_admin/my_account/ssh_keys', []);
239 pyroutes.register('my_account_ssh_keys', '/_admin/my_account/ssh_keys', []);
238 pyroutes.register('my_account_ssh_keys_generate', '/_admin/my_account/ssh_keys/generate', []);
240 pyroutes.register('my_account_ssh_keys_generate', '/_admin/my_account/ssh_keys/generate', []);
239 pyroutes.register('my_account_ssh_keys_add', '/_admin/my_account/ssh_keys/new', []);
241 pyroutes.register('my_account_ssh_keys_add', '/_admin/my_account/ssh_keys/new', []);
240 pyroutes.register('my_account_ssh_keys_delete', '/_admin/my_account/ssh_keys/delete', []);
242 pyroutes.register('my_account_ssh_keys_delete', '/_admin/my_account/ssh_keys/delete', []);
241 pyroutes.register('my_account_emails', '/_admin/my_account/emails', []);
243 pyroutes.register('my_account_emails', '/_admin/my_account/emails', []);
242 pyroutes.register('my_account_emails_add', '/_admin/my_account/emails/new', []);
244 pyroutes.register('my_account_emails_add', '/_admin/my_account/emails/new', []);
243 pyroutes.register('my_account_emails_delete', '/_admin/my_account/emails/delete', []);
245 pyroutes.register('my_account_emails_delete', '/_admin/my_account/emails/delete', []);
244 pyroutes.register('my_account_repos', '/_admin/my_account/repos', []);
246 pyroutes.register('my_account_repos', '/_admin/my_account/repos', []);
245 pyroutes.register('my_account_watched', '/_admin/my_account/watched', []);
247 pyroutes.register('my_account_watched', '/_admin/my_account/watched', []);
246 pyroutes.register('my_account_perms', '/_admin/my_account/perms', []);
248 pyroutes.register('my_account_perms', '/_admin/my_account/perms', []);
247 pyroutes.register('my_account_notifications', '/_admin/my_account/notifications', []);
249 pyroutes.register('my_account_notifications', '/_admin/my_account/notifications', []);
248 pyroutes.register('my_account_notifications_toggle_visibility', '/_admin/my_account/toggle_visibility', []);
250 pyroutes.register('my_account_notifications_toggle_visibility', '/_admin/my_account/toggle_visibility', []);
249 pyroutes.register('my_account_pullrequests', '/_admin/my_account/pull_requests', []);
251 pyroutes.register('my_account_pullrequests', '/_admin/my_account/pull_requests', []);
250 pyroutes.register('my_account_pullrequests_data', '/_admin/my_account/pull_requests/data', []);
252 pyroutes.register('my_account_pullrequests_data', '/_admin/my_account/pull_requests/data', []);
251 pyroutes.register('notifications_show_all', '/_admin/notifications', []);
253 pyroutes.register('notifications_show_all', '/_admin/notifications', []);
252 pyroutes.register('notifications_mark_all_read', '/_admin/notifications/mark_all_read', []);
254 pyroutes.register('notifications_mark_all_read', '/_admin/notifications/mark_all_read', []);
253 pyroutes.register('notifications_show', '/_admin/notifications/%(notification_id)s', ['notification_id']);
255 pyroutes.register('notifications_show', '/_admin/notifications/%(notification_id)s', ['notification_id']);
254 pyroutes.register('notifications_update', '/_admin/notifications/%(notification_id)s/update', ['notification_id']);
256 pyroutes.register('notifications_update', '/_admin/notifications/%(notification_id)s/update', ['notification_id']);
255 pyroutes.register('notifications_delete', '/_admin/notifications/%(notification_id)s/delete', ['notification_id']);
257 pyroutes.register('notifications_delete', '/_admin/notifications/%(notification_id)s/delete', ['notification_id']);
256 pyroutes.register('my_account_notifications_test_channelstream', '/_admin/my_account/test_channelstream', []);
258 pyroutes.register('my_account_notifications_test_channelstream', '/_admin/my_account/test_channelstream', []);
257 pyroutes.register('gists_show', '/_admin/gists', []);
259 pyroutes.register('gists_show', '/_admin/gists', []);
258 pyroutes.register('gists_new', '/_admin/gists/new', []);
260 pyroutes.register('gists_new', '/_admin/gists/new', []);
259 pyroutes.register('gists_create', '/_admin/gists/create', []);
261 pyroutes.register('gists_create', '/_admin/gists/create', []);
260 pyroutes.register('gist_show', '/_admin/gists/%(gist_id)s', ['gist_id']);
262 pyroutes.register('gist_show', '/_admin/gists/%(gist_id)s', ['gist_id']);
261 pyroutes.register('gist_delete', '/_admin/gists/%(gist_id)s/delete', ['gist_id']);
263 pyroutes.register('gist_delete', '/_admin/gists/%(gist_id)s/delete', ['gist_id']);
262 pyroutes.register('gist_edit', '/_admin/gists/%(gist_id)s/edit', ['gist_id']);
264 pyroutes.register('gist_edit', '/_admin/gists/%(gist_id)s/edit', ['gist_id']);
263 pyroutes.register('gist_edit_check_revision', '/_admin/gists/%(gist_id)s/edit/check_revision', ['gist_id']);
265 pyroutes.register('gist_edit_check_revision', '/_admin/gists/%(gist_id)s/edit/check_revision', ['gist_id']);
264 pyroutes.register('gist_update', '/_admin/gists/%(gist_id)s/update', ['gist_id']);
266 pyroutes.register('gist_update', '/_admin/gists/%(gist_id)s/update', ['gist_id']);
265 pyroutes.register('gist_show_rev', '/_admin/gists/%(gist_id)s/%(revision)s', ['gist_id', 'revision']);
267 pyroutes.register('gist_show_rev', '/_admin/gists/%(gist_id)s/%(revision)s', ['gist_id', 'revision']);
266 pyroutes.register('gist_show_formatted', '/_admin/gists/%(gist_id)s/%(revision)s/%(format)s', ['gist_id', 'revision', 'format']);
268 pyroutes.register('gist_show_formatted', '/_admin/gists/%(gist_id)s/%(revision)s/%(format)s', ['gist_id', 'revision', 'format']);
267 pyroutes.register('gist_show_formatted_path', '/_admin/gists/%(gist_id)s/%(revision)s/%(format)s/%(f_path)s', ['gist_id', 'revision', 'format', 'f_path']);
269 pyroutes.register('gist_show_formatted_path', '/_admin/gists/%(gist_id)s/%(revision)s/%(format)s/%(f_path)s', ['gist_id', 'revision', 'format', 'f_path']);
268 pyroutes.register('debug_style_home', '/_admin/debug_style', []);
270 pyroutes.register('debug_style_home', '/_admin/debug_style', []);
269 pyroutes.register('debug_style_template', '/_admin/debug_style/t/%(t_path)s', ['t_path']);
271 pyroutes.register('debug_style_template', '/_admin/debug_style/t/%(t_path)s', ['t_path']);
270 pyroutes.register('apiv2', '/_admin/api', []);
272 pyroutes.register('apiv2', '/_admin/api', []);
271 }
273 }
@@ -1,16 +1,17 b''
1 <script>
1 <script>
2 var CHANNELSTREAM_URLS = ${config['url_gen'](request)|n};
2 var CHANNELSTREAM_URLS = ${config['url_gen'](request)|n};
3 %if request.registry.rhodecode_plugins['channelstream']['enabled'] and c.rhodecode_user.username != h.DEFAULT_USER:
3 %if request.registry.rhodecode_plugins['channelstream']['enabled'] and c.rhodecode_user.username != h.DEFAULT_USER:
4 var CHANNELSTREAM_SETTINGS = {
4 var CHANNELSTREAM_SETTINGS = {
5 'enabled': true,
5 'enabled': true,
6 'ws_location': '${request.registry.settings.get('channelstream.ws_url')}',
6 'ws_location': '${request.registry.settings.get('channelstream.ws_url')}',
7 'webapp_location': '${h.url('/', qualified=True)[:-1]}'
7 'webapp_location': '${h.route_url('home').rstrip('/')}'
8 };
8 };
9 %else:
9 %else:
10 var CHANNELSTREAM_SETTINGS = {
10 var CHANNELSTREAM_SETTINGS = {
11 'enabled':false,
11 'enabled':false,
12 'ws_location': '',
12 'ws_location': '',
13 'webapp_location': ''};
13 'webapp_location': '${h.route_url('home').rstrip('/')}'
14 };
14 %endif
15 %endif
15
16
16 </script>
17 </script>
@@ -1,671 +1,672 b''
1 <%namespace name="commentblock" file="/changeset/changeset_file_comment.mako"/>
1 <%namespace name="commentblock" file="/changeset/changeset_file_comment.mako"/>
2
2
3 <%def name="diff_line_anchor(filename, line, type)"><%
3 <%def name="diff_line_anchor(filename, line, type)"><%
4 return '%s_%s_%i' % (h.safeid(filename), type, line)
4 return '%s_%s_%i' % (h.safeid(filename), type, line)
5 %></%def>
5 %></%def>
6
6
7 <%def name="action_class(action)">
7 <%def name="action_class(action)">
8 <%
8 <%
9 return {
9 return {
10 '-': 'cb-deletion',
10 '-': 'cb-deletion',
11 '+': 'cb-addition',
11 '+': 'cb-addition',
12 ' ': 'cb-context',
12 ' ': 'cb-context',
13 }.get(action, 'cb-empty')
13 }.get(action, 'cb-empty')
14 %>
14 %>
15 </%def>
15 </%def>
16
16
17 <%def name="op_class(op_id)">
17 <%def name="op_class(op_id)">
18 <%
18 <%
19 return {
19 return {
20 DEL_FILENODE: 'deletion', # file deleted
20 DEL_FILENODE: 'deletion', # file deleted
21 BIN_FILENODE: 'warning' # binary diff hidden
21 BIN_FILENODE: 'warning' # binary diff hidden
22 }.get(op_id, 'addition')
22 }.get(op_id, 'addition')
23 %>
23 %>
24 </%def>
24 </%def>
25
25
26 <%def name="link_for(**kw)">
26 <%def name="link_for(**kw)">
27 <%
27 <%
28 new_args = request.GET.mixed()
28 new_args = request.GET.mixed()
29 new_args.update(kw)
29 new_args.update(kw)
30 return h.url('', **new_args)
30 return request.current_route_path(_query=new_args)
31 %>
31 %>
32 </%def>
32 </%def>
33
33
34 <%def name="render_diffset(diffset, commit=None,
34 <%def name="render_diffset(diffset, commit=None,
35
35
36 # collapse all file diff entries when there are more than this amount of files in the diff
36 # collapse all file diff entries when there are more than this amount of files in the diff
37 collapse_when_files_over=20,
37 collapse_when_files_over=20,
38
38
39 # collapse lines in the diff when more than this amount of lines changed in the file diff
39 # collapse lines in the diff when more than this amount of lines changed in the file diff
40 lines_changed_limit=500,
40 lines_changed_limit=500,
41
41
42 # add a ruler at to the output
42 # add a ruler at to the output
43 ruler_at_chars=0,
43 ruler_at_chars=0,
44
44
45 # show inline comments
45 # show inline comments
46 use_comments=False,
46 use_comments=False,
47
47
48 # disable new comments
48 # disable new comments
49 disable_new_comments=False,
49 disable_new_comments=False,
50
50
51 # special file-comments that were deleted in previous versions
51 # special file-comments that were deleted in previous versions
52 # it's used for showing outdated comments for deleted files in a PR
52 # it's used for showing outdated comments for deleted files in a PR
53 deleted_files_comments=None
53 deleted_files_comments=None
54
54
55 )">
55 )">
56
56
57 %if use_comments:
57 %if use_comments:
58 <div id="cb-comments-inline-container-template" class="js-template">
58 <div id="cb-comments-inline-container-template" class="js-template">
59 ${inline_comments_container([])}
59 ${inline_comments_container([])}
60 </div>
60 </div>
61 <div class="js-template" id="cb-comment-inline-form-template">
61 <div class="js-template" id="cb-comment-inline-form-template">
62 <div class="comment-inline-form ac">
62 <div class="comment-inline-form ac">
63
63
64 %if c.rhodecode_user.username != h.DEFAULT_USER:
64 %if c.rhodecode_user.username != h.DEFAULT_USER:
65 ## render template for inline comments
65 ## render template for inline comments
66 ${commentblock.comment_form(form_type='inline')}
66 ${commentblock.comment_form(form_type='inline')}
67 %else:
67 %else:
68 ${h.form('', class_='inline-form comment-form-login', method='get')}
68 ${h.form('', class_='inline-form comment-form-login', method='get')}
69 <div class="pull-left">
69 <div class="pull-left">
70 <div class="comment-help pull-right">
70 <div class="comment-help pull-right">
71 ${_('You need to be logged in to leave comments.')} <a href="${h.route_path('login', _query={'came_from': h.url.current()})}">${_('Login now')}</a>
71 ${_('You need to be logged in to leave comments.')} <a href="${h.route_path('login', _query={'came_from': h.url.current()})}">${_('Login now')}</a>
72 </div>
72 </div>
73 </div>
73 </div>
74 <div class="comment-button pull-right">
74 <div class="comment-button pull-right">
75 <button type="button" class="cb-comment-cancel" onclick="return Rhodecode.comments.cancelComment(this);">
75 <button type="button" class="cb-comment-cancel" onclick="return Rhodecode.comments.cancelComment(this);">
76 ${_('Cancel')}
76 ${_('Cancel')}
77 </button>
77 </button>
78 </div>
78 </div>
79 <div class="clearfix"></div>
79 <div class="clearfix"></div>
80 ${h.end_form()}
80 ${h.end_form()}
81 %endif
81 %endif
82 </div>
82 </div>
83 </div>
83 </div>
84
84
85 %endif
85 %endif
86 <%
86 <%
87 collapse_all = len(diffset.files) > collapse_when_files_over
87 collapse_all = len(diffset.files) > collapse_when_files_over
88 %>
88 %>
89
89
90 %if c.diffmode == 'sideside':
90 %if c.diffmode == 'sideside':
91 <style>
91 <style>
92 .wrapper {
92 .wrapper {
93 max-width: 1600px !important;
93 max-width: 1600px !important;
94 }
94 }
95 </style>
95 </style>
96 %endif
96 %endif
97
97
98 %if ruler_at_chars:
98 %if ruler_at_chars:
99 <style>
99 <style>
100 .diff table.cb .cb-content:after {
100 .diff table.cb .cb-content:after {
101 content: "";
101 content: "";
102 border-left: 1px solid blue;
102 border-left: 1px solid blue;
103 position: absolute;
103 position: absolute;
104 top: 0;
104 top: 0;
105 height: 18px;
105 height: 18px;
106 opacity: .2;
106 opacity: .2;
107 z-index: 10;
107 z-index: 10;
108 //## +5 to account for diff action (+/-)
108 //## +5 to account for diff action (+/-)
109 left: ${ruler_at_chars + 5}ch;
109 left: ${ruler_at_chars + 5}ch;
110 </style>
110 </style>
111 %endif
111 %endif
112
112
113 <div class="diffset ${disable_new_comments and 'diffset-comments-disabled'}">
113 <div class="diffset ${disable_new_comments and 'diffset-comments-disabled'}">
114 <div class="diffset-heading ${diffset.limited_diff and 'diffset-heading-warning' or ''}">
114 <div class="diffset-heading ${diffset.limited_diff and 'diffset-heading-warning' or ''}">
115 %if commit:
115 %if commit:
116 <div class="pull-right">
116 <div class="pull-right">
117 <a class="btn tooltip" title="${h.tooltip(_('Browse Files at revision {}').format(commit.raw_id))}" href="${h.route_path('repo_files',repo_name=diffset.repo_name, commit_id=commit.raw_id, f_path='')}">
117 <a class="btn tooltip" title="${h.tooltip(_('Browse Files at revision {}').format(commit.raw_id))}" href="${h.route_path('repo_files',repo_name=diffset.repo_name, commit_id=commit.raw_id, f_path='')}">
118 ${_('Browse Files')}
118 ${_('Browse Files')}
119 </a>
119 </a>
120 </div>
120 </div>
121 %endif
121 %endif
122 <h2 class="clearinner">
122 <h2 class="clearinner">
123 %if commit:
123 %if commit:
124 <a class="tooltip revision" title="${h.tooltip(commit.message)}" href="${h.route_path('repo_commit',repo_name=c.repo_name,commit_id=commit.raw_id)}">${'r%s:%s' % (commit.revision,h.short_id(commit.raw_id))}</a> -
124 <a class="tooltip revision" title="${h.tooltip(commit.message)}" href="${h.route_path('repo_commit',repo_name=c.repo_name,commit_id=commit.raw_id)}">${'r%s:%s' % (commit.revision,h.short_id(commit.raw_id))}</a> -
125 ${h.age_component(commit.date)} -
125 ${h.age_component(commit.date)} -
126 %endif
126 %endif
127
127 %if diffset.limited_diff:
128 %if diffset.limited_diff:
128 ${_('The requested commit is too big and content was truncated.')}
129 ${_('The requested commit is too big and content was truncated.')}
129
130
130 ${_ungettext('%(num)s file changed.', '%(num)s files changed.', diffset.changed_files) % {'num': diffset.changed_files}}
131 ${_ungettext('%(num)s file changed.', '%(num)s files changed.', diffset.changed_files) % {'num': diffset.changed_files}}
131 <a href="${link_for(fulldiff=1)}" onclick="return confirm('${_("Showing a big diff might take some time and resources, continue?")}')">${_('Show full diff')}</a>
132 <a href="${link_for(fulldiff=1)}" onclick="return confirm('${_("Showing a big diff might take some time and resources, continue?")}')">${_('Show full diff')}</a>
132 %else:
133 %else:
133 ${_ungettext('%(num)s file changed: %(linesadd)s inserted, ''%(linesdel)s deleted',
134 ${_ungettext('%(num)s file changed: %(linesadd)s inserted, ''%(linesdel)s deleted',
134 '%(num)s files changed: %(linesadd)s inserted, %(linesdel)s deleted', diffset.changed_files) % {'num': diffset.changed_files, 'linesadd': diffset.lines_added, 'linesdel': diffset.lines_deleted}}
135 '%(num)s files changed: %(linesadd)s inserted, %(linesdel)s deleted', diffset.changed_files) % {'num': diffset.changed_files, 'linesadd': diffset.lines_added, 'linesdel': diffset.lines_deleted}}
135 %endif
136 %endif
136
137
137 </h2>
138 </h2>
138 </div>
139 </div>
139
140
140 %if not diffset.files:
141 %if not diffset.files:
141 <p class="empty_data">${_('No files')}</p>
142 <p class="empty_data">${_('No files')}</p>
142 %endif
143 %endif
143
144
144 <div class="filediffs">
145 <div class="filediffs">
145 ## initial value could be marked as False later on
146 ## initial value could be marked as False later on
146 <% over_lines_changed_limit = False %>
147 <% over_lines_changed_limit = False %>
147 %for i, filediff in enumerate(diffset.files):
148 %for i, filediff in enumerate(diffset.files):
148
149
149 <%
150 <%
150 lines_changed = filediff.patch['stats']['added'] + filediff.patch['stats']['deleted']
151 lines_changed = filediff.patch['stats']['added'] + filediff.patch['stats']['deleted']
151 over_lines_changed_limit = lines_changed > lines_changed_limit
152 over_lines_changed_limit = lines_changed > lines_changed_limit
152 %>
153 %>
153 <input ${collapse_all and 'checked' or ''} class="filediff-collapse-state" id="filediff-collapse-${id(filediff)}" type="checkbox">
154 <input ${collapse_all and 'checked' or ''} class="filediff-collapse-state" id="filediff-collapse-${id(filediff)}" type="checkbox">
154 <div
155 <div
155 class="filediff"
156 class="filediff"
156 data-f-path="${filediff.patch['filename']}"
157 data-f-path="${filediff.patch['filename']}"
157 id="a_${h.FID('', filediff.patch['filename'])}">
158 id="a_${h.FID('', filediff.patch['filename'])}">
158 <label for="filediff-collapse-${id(filediff)}" class="filediff-heading">
159 <label for="filediff-collapse-${id(filediff)}" class="filediff-heading">
159 <div class="filediff-collapse-indicator"></div>
160 <div class="filediff-collapse-indicator"></div>
160 ${diff_ops(filediff)}
161 ${diff_ops(filediff)}
161 </label>
162 </label>
162 ${diff_menu(filediff, use_comments=use_comments)}
163 ${diff_menu(filediff, use_comments=use_comments)}
163 <table class="cb cb-diff-${c.diffmode} code-highlight ${over_lines_changed_limit and 'cb-collapsed' or ''}">
164 <table class="cb cb-diff-${c.diffmode} code-highlight ${over_lines_changed_limit and 'cb-collapsed' or ''}">
164 %if not filediff.hunks:
165 %if not filediff.hunks:
165 %for op_id, op_text in filediff.patch['stats']['ops'].items():
166 %for op_id, op_text in filediff.patch['stats']['ops'].items():
166 <tr>
167 <tr>
167 <td class="cb-text cb-${op_class(op_id)}" ${c.diffmode == 'unified' and 'colspan=4' or 'colspan=6'}>
168 <td class="cb-text cb-${op_class(op_id)}" ${c.diffmode == 'unified' and 'colspan=4' or 'colspan=6'}>
168 %if op_id == DEL_FILENODE:
169 %if op_id == DEL_FILENODE:
169 ${_('File was deleted')}
170 ${_('File was deleted')}
170 %elif op_id == BIN_FILENODE:
171 %elif op_id == BIN_FILENODE:
171 ${_('Binary file hidden')}
172 ${_('Binary file hidden')}
172 %else:
173 %else:
173 ${op_text}
174 ${op_text}
174 %endif
175 %endif
175 </td>
176 </td>
176 </tr>
177 </tr>
177 %endfor
178 %endfor
178 %endif
179 %endif
179 %if filediff.limited_diff:
180 %if filediff.limited_diff:
180 <tr class="cb-warning cb-collapser">
181 <tr class="cb-warning cb-collapser">
181 <td class="cb-text" ${c.diffmode == 'unified' and 'colspan=4' or 'colspan=6'}>
182 <td class="cb-text" ${c.diffmode == 'unified' and 'colspan=4' or 'colspan=6'}>
182 ${_('The requested commit is too big and content was truncated.')} <a href="${link_for(fulldiff=1)}" onclick="return confirm('${_("Showing a big diff might take some time and resources, continue?")}')">${_('Show full diff')}</a>
183 ${_('The requested commit is too big and content was truncated.')} <a href="${link_for(fulldiff=1)}" onclick="return confirm('${_("Showing a big diff might take some time and resources, continue?")}')">${_('Show full diff')}</a>
183 </td>
184 </td>
184 </tr>
185 </tr>
185 %else:
186 %else:
186 %if over_lines_changed_limit:
187 %if over_lines_changed_limit:
187 <tr class="cb-warning cb-collapser">
188 <tr class="cb-warning cb-collapser">
188 <td class="cb-text" ${c.diffmode == 'unified' and 'colspan=4' or 'colspan=6'}>
189 <td class="cb-text" ${c.diffmode == 'unified' and 'colspan=4' or 'colspan=6'}>
189 ${_('This diff has been collapsed as it changes many lines, (%i lines changed)' % lines_changed)}
190 ${_('This diff has been collapsed as it changes many lines, (%i lines changed)' % lines_changed)}
190 <a href="#" class="cb-expand"
191 <a href="#" class="cb-expand"
191 onclick="$(this).closest('table').removeClass('cb-collapsed'); return false;">${_('Show them')}
192 onclick="$(this).closest('table').removeClass('cb-collapsed'); return false;">${_('Show them')}
192 </a>
193 </a>
193 <a href="#" class="cb-collapse"
194 <a href="#" class="cb-collapse"
194 onclick="$(this).closest('table').addClass('cb-collapsed'); return false;">${_('Hide them')}
195 onclick="$(this).closest('table').addClass('cb-collapsed'); return false;">${_('Hide them')}
195 </a>
196 </a>
196 </td>
197 </td>
197 </tr>
198 </tr>
198 %endif
199 %endif
199 %endif
200 %endif
200
201
201 %for hunk in filediff.hunks:
202 %for hunk in filediff.hunks:
202 <tr class="cb-hunk">
203 <tr class="cb-hunk">
203 <td ${c.diffmode == 'unified' and 'colspan=3' or ''}>
204 <td ${c.diffmode == 'unified' and 'colspan=3' or ''}>
204 ## TODO: dan: add ajax loading of more context here
205 ## TODO: dan: add ajax loading of more context here
205 ## <a href="#">
206 ## <a href="#">
206 <i class="icon-more"></i>
207 <i class="icon-more"></i>
207 ## </a>
208 ## </a>
208 </td>
209 </td>
209 <td ${c.diffmode == 'sideside' and 'colspan=5' or ''}>
210 <td ${c.diffmode == 'sideside' and 'colspan=5' or ''}>
210 @@
211 @@
211 -${hunk.source_start},${hunk.source_length}
212 -${hunk.source_start},${hunk.source_length}
212 +${hunk.target_start},${hunk.target_length}
213 +${hunk.target_start},${hunk.target_length}
213 ${hunk.section_header}
214 ${hunk.section_header}
214 </td>
215 </td>
215 </tr>
216 </tr>
216 %if c.diffmode == 'unified':
217 %if c.diffmode == 'unified':
217 ${render_hunk_lines_unified(hunk, use_comments=use_comments)}
218 ${render_hunk_lines_unified(hunk, use_comments=use_comments)}
218 %elif c.diffmode == 'sideside':
219 %elif c.diffmode == 'sideside':
219 ${render_hunk_lines_sideside(hunk, use_comments=use_comments)}
220 ${render_hunk_lines_sideside(hunk, use_comments=use_comments)}
220 %else:
221 %else:
221 <tr class="cb-line">
222 <tr class="cb-line">
222 <td>unknown diff mode</td>
223 <td>unknown diff mode</td>
223 </tr>
224 </tr>
224 %endif
225 %endif
225 %endfor
226 %endfor
226
227
227 ## outdated comments that do not fit into currently displayed lines
228 ## outdated comments that do not fit into currently displayed lines
228 % for lineno, comments in filediff.left_comments.items():
229 % for lineno, comments in filediff.left_comments.items():
229
230
230 %if c.diffmode == 'unified':
231 %if c.diffmode == 'unified':
231 <tr class="cb-line">
232 <tr class="cb-line">
232 <td class="cb-data cb-context"></td>
233 <td class="cb-data cb-context"></td>
233 <td class="cb-lineno cb-context"></td>
234 <td class="cb-lineno cb-context"></td>
234 <td class="cb-lineno cb-context"></td>
235 <td class="cb-lineno cb-context"></td>
235 <td class="cb-content cb-context">
236 <td class="cb-content cb-context">
236 ${inline_comments_container(comments)}
237 ${inline_comments_container(comments)}
237 </td>
238 </td>
238 </tr>
239 </tr>
239 %elif c.diffmode == 'sideside':
240 %elif c.diffmode == 'sideside':
240 <tr class="cb-line">
241 <tr class="cb-line">
241 <td class="cb-data cb-context"></td>
242 <td class="cb-data cb-context"></td>
242 <td class="cb-lineno cb-context"></td>
243 <td class="cb-lineno cb-context"></td>
243 <td class="cb-content cb-context"></td>
244 <td class="cb-content cb-context"></td>
244
245
245 <td class="cb-data cb-context"></td>
246 <td class="cb-data cb-context"></td>
246 <td class="cb-lineno cb-context"></td>
247 <td class="cb-lineno cb-context"></td>
247 <td class="cb-content cb-context">
248 <td class="cb-content cb-context">
248 ${inline_comments_container(comments)}
249 ${inline_comments_container(comments)}
249 </td>
250 </td>
250 </tr>
251 </tr>
251 %endif
252 %endif
252
253
253 % endfor
254 % endfor
254
255
255 </table>
256 </table>
256 </div>
257 </div>
257 %endfor
258 %endfor
258
259
259 ## outdated comments that are made for a file that has been deleted
260 ## outdated comments that are made for a file that has been deleted
260 % for filename, comments_dict in (deleted_files_comments or {}).items():
261 % for filename, comments_dict in (deleted_files_comments or {}).items():
261
262
262 <div class="filediffs filediff-outdated" style="display: none">
263 <div class="filediffs filediff-outdated" style="display: none">
263 <input ${collapse_all and 'checked' or ''} class="filediff-collapse-state" id="filediff-collapse-${id(filename)}" type="checkbox">
264 <input ${collapse_all and 'checked' or ''} class="filediff-collapse-state" id="filediff-collapse-${id(filename)}" type="checkbox">
264 <div class="filediff" data-f-path="${filename}" id="a_${h.FID('', filename)}">
265 <div class="filediff" data-f-path="${filename}" id="a_${h.FID('', filename)}">
265 <label for="filediff-collapse-${id(filename)}" class="filediff-heading">
266 <label for="filediff-collapse-${id(filename)}" class="filediff-heading">
266 <div class="filediff-collapse-indicator"></div>
267 <div class="filediff-collapse-indicator"></div>
267 <span class="pill">
268 <span class="pill">
268 ## file was deleted
269 ## file was deleted
269 <strong>${filename}</strong>
270 <strong>${filename}</strong>
270 </span>
271 </span>
271 <span class="pill-group" style="float: left">
272 <span class="pill-group" style="float: left">
272 ## file op, doesn't need translation
273 ## file op, doesn't need translation
273 <span class="pill" op="removed">removed in this version</span>
274 <span class="pill" op="removed">removed in this version</span>
274 </span>
275 </span>
275 <a class="pill filediff-anchor" href="#a_${h.FID('', filename)}">ΒΆ</a>
276 <a class="pill filediff-anchor" href="#a_${h.FID('', filename)}">ΒΆ</a>
276 <span class="pill-group" style="float: right">
277 <span class="pill-group" style="float: right">
277 <span class="pill" op="deleted">-${comments_dict['stats']}</span>
278 <span class="pill" op="deleted">-${comments_dict['stats']}</span>
278 </span>
279 </span>
279 </label>
280 </label>
280
281
281 <table class="cb cb-diff-${c.diffmode} code-highlight ${over_lines_changed_limit and 'cb-collapsed' or ''}">
282 <table class="cb cb-diff-${c.diffmode} code-highlight ${over_lines_changed_limit and 'cb-collapsed' or ''}">
282 <tr>
283 <tr>
283 % if c.diffmode == 'unified':
284 % if c.diffmode == 'unified':
284 <td></td>
285 <td></td>
285 %endif
286 %endif
286
287
287 <td></td>
288 <td></td>
288 <td class="cb-text cb-${op_class(BIN_FILENODE)}" ${c.diffmode == 'unified' and 'colspan=4' or 'colspan=5'}>
289 <td class="cb-text cb-${op_class(BIN_FILENODE)}" ${c.diffmode == 'unified' and 'colspan=4' or 'colspan=5'}>
289 ${_('File was deleted in this version, and outdated comments were made on it')}
290 ${_('File was deleted in this version, and outdated comments were made on it')}
290 </td>
291 </td>
291 </tr>
292 </tr>
292 %if c.diffmode == 'unified':
293 %if c.diffmode == 'unified':
293 <tr class="cb-line">
294 <tr class="cb-line">
294 <td class="cb-data cb-context"></td>
295 <td class="cb-data cb-context"></td>
295 <td class="cb-lineno cb-context"></td>
296 <td class="cb-lineno cb-context"></td>
296 <td class="cb-lineno cb-context"></td>
297 <td class="cb-lineno cb-context"></td>
297 <td class="cb-content cb-context">
298 <td class="cb-content cb-context">
298 ${inline_comments_container(comments_dict['comments'])}
299 ${inline_comments_container(comments_dict['comments'])}
299 </td>
300 </td>
300 </tr>
301 </tr>
301 %elif c.diffmode == 'sideside':
302 %elif c.diffmode == 'sideside':
302 <tr class="cb-line">
303 <tr class="cb-line">
303 <td class="cb-data cb-context"></td>
304 <td class="cb-data cb-context"></td>
304 <td class="cb-lineno cb-context"></td>
305 <td class="cb-lineno cb-context"></td>
305 <td class="cb-content cb-context"></td>
306 <td class="cb-content cb-context"></td>
306
307
307 <td class="cb-data cb-context"></td>
308 <td class="cb-data cb-context"></td>
308 <td class="cb-lineno cb-context"></td>
309 <td class="cb-lineno cb-context"></td>
309 <td class="cb-content cb-context">
310 <td class="cb-content cb-context">
310 ${inline_comments_container(comments_dict['comments'])}
311 ${inline_comments_container(comments_dict['comments'])}
311 </td>
312 </td>
312 </tr>
313 </tr>
313 %endif
314 %endif
314 </table>
315 </table>
315 </div>
316 </div>
316 </div>
317 </div>
317 % endfor
318 % endfor
318
319
319 </div>
320 </div>
320 </div>
321 </div>
321 </%def>
322 </%def>
322
323
323 <%def name="diff_ops(filediff)">
324 <%def name="diff_ops(filediff)">
324 <%
325 <%
325 from rhodecode.lib.diffs import NEW_FILENODE, DEL_FILENODE, \
326 from rhodecode.lib.diffs import NEW_FILENODE, DEL_FILENODE, \
326 MOD_FILENODE, RENAMED_FILENODE, CHMOD_FILENODE, BIN_FILENODE, COPIED_FILENODE
327 MOD_FILENODE, RENAMED_FILENODE, CHMOD_FILENODE, BIN_FILENODE, COPIED_FILENODE
327 %>
328 %>
328 <span class="pill">
329 <span class="pill">
329 %if filediff.source_file_path and filediff.target_file_path:
330 %if filediff.source_file_path and filediff.target_file_path:
330 %if filediff.source_file_path != filediff.target_file_path:
331 %if filediff.source_file_path != filediff.target_file_path:
331 ## file was renamed, or copied
332 ## file was renamed, or copied
332 %if RENAMED_FILENODE in filediff.patch['stats']['ops']:
333 %if RENAMED_FILENODE in filediff.patch['stats']['ops']:
333 <strong>${filediff.target_file_path}</strong> β¬… <del>${filediff.source_file_path}</del>
334 <strong>${filediff.target_file_path}</strong> β¬… <del>${filediff.source_file_path}</del>
334 %elif COPIED_FILENODE in filediff.patch['stats']['ops']:
335 %elif COPIED_FILENODE in filediff.patch['stats']['ops']:
335 <strong>${filediff.target_file_path}</strong> β¬… ${filediff.source_file_path}
336 <strong>${filediff.target_file_path}</strong> β¬… ${filediff.source_file_path}
336 %endif
337 %endif
337 %else:
338 %else:
338 ## file was modified
339 ## file was modified
339 <strong>${filediff.source_file_path}</strong>
340 <strong>${filediff.source_file_path}</strong>
340 %endif
341 %endif
341 %else:
342 %else:
342 %if filediff.source_file_path:
343 %if filediff.source_file_path:
343 ## file was deleted
344 ## file was deleted
344 <strong>${filediff.source_file_path}</strong>
345 <strong>${filediff.source_file_path}</strong>
345 %else:
346 %else:
346 ## file was added
347 ## file was added
347 <strong>${filediff.target_file_path}</strong>
348 <strong>${filediff.target_file_path}</strong>
348 %endif
349 %endif
349 %endif
350 %endif
350 </span>
351 </span>
351 <span class="pill-group" style="float: left">
352 <span class="pill-group" style="float: left">
352 %if filediff.limited_diff:
353 %if filediff.limited_diff:
353 <span class="pill tooltip" op="limited" title="The stats for this diff are not complete">limited diff</span>
354 <span class="pill tooltip" op="limited" title="The stats for this diff are not complete">limited diff</span>
354 %endif
355 %endif
355
356
356 %if RENAMED_FILENODE in filediff.patch['stats']['ops']:
357 %if RENAMED_FILENODE in filediff.patch['stats']['ops']:
357 <span class="pill" op="renamed">renamed</span>
358 <span class="pill" op="renamed">renamed</span>
358 %endif
359 %endif
359
360
360 %if COPIED_FILENODE in filediff.patch['stats']['ops']:
361 %if COPIED_FILENODE in filediff.patch['stats']['ops']:
361 <span class="pill" op="copied">copied</span>
362 <span class="pill" op="copied">copied</span>
362 %endif
363 %endif
363
364
364 %if NEW_FILENODE in filediff.patch['stats']['ops']:
365 %if NEW_FILENODE in filediff.patch['stats']['ops']:
365 <span class="pill" op="created">created</span>
366 <span class="pill" op="created">created</span>
366 %if filediff['target_mode'].startswith('120'):
367 %if filediff['target_mode'].startswith('120'):
367 <span class="pill" op="symlink">symlink</span>
368 <span class="pill" op="symlink">symlink</span>
368 %else:
369 %else:
369 <span class="pill" op="mode">${nice_mode(filediff['target_mode'])}</span>
370 <span class="pill" op="mode">${nice_mode(filediff['target_mode'])}</span>
370 %endif
371 %endif
371 %endif
372 %endif
372
373
373 %if DEL_FILENODE in filediff.patch['stats']['ops']:
374 %if DEL_FILENODE in filediff.patch['stats']['ops']:
374 <span class="pill" op="removed">removed</span>
375 <span class="pill" op="removed">removed</span>
375 %endif
376 %endif
376
377
377 %if CHMOD_FILENODE in filediff.patch['stats']['ops']:
378 %if CHMOD_FILENODE in filediff.patch['stats']['ops']:
378 <span class="pill" op="mode">
379 <span class="pill" op="mode">
379 ${nice_mode(filediff['source_mode'])} ➑ ${nice_mode(filediff['target_mode'])}
380 ${nice_mode(filediff['source_mode'])} ➑ ${nice_mode(filediff['target_mode'])}
380 </span>
381 </span>
381 %endif
382 %endif
382 </span>
383 </span>
383
384
384 <a class="pill filediff-anchor" href="#a_${h.FID('', filediff.patch['filename'])}">ΒΆ</a>
385 <a class="pill filediff-anchor" href="#a_${h.FID('', filediff.patch['filename'])}">ΒΆ</a>
385
386
386 <span class="pill-group" style="float: right">
387 <span class="pill-group" style="float: right">
387 %if BIN_FILENODE in filediff.patch['stats']['ops']:
388 %if BIN_FILENODE in filediff.patch['stats']['ops']:
388 <span class="pill" op="binary">binary</span>
389 <span class="pill" op="binary">binary</span>
389 %if MOD_FILENODE in filediff.patch['stats']['ops']:
390 %if MOD_FILENODE in filediff.patch['stats']['ops']:
390 <span class="pill" op="modified">modified</span>
391 <span class="pill" op="modified">modified</span>
391 %endif
392 %endif
392 %endif
393 %endif
393 %if filediff.patch['stats']['added']:
394 %if filediff.patch['stats']['added']:
394 <span class="pill" op="added">+${filediff.patch['stats']['added']}</span>
395 <span class="pill" op="added">+${filediff.patch['stats']['added']}</span>
395 %endif
396 %endif
396 %if filediff.patch['stats']['deleted']:
397 %if filediff.patch['stats']['deleted']:
397 <span class="pill" op="deleted">-${filediff.patch['stats']['deleted']}</span>
398 <span class="pill" op="deleted">-${filediff.patch['stats']['deleted']}</span>
398 %endif
399 %endif
399 </span>
400 </span>
400
401
401 </%def>
402 </%def>
402
403
403 <%def name="nice_mode(filemode)">
404 <%def name="nice_mode(filemode)">
404 ${filemode.startswith('100') and filemode[3:] or filemode}
405 ${filemode.startswith('100') and filemode[3:] or filemode}
405 </%def>
406 </%def>
406
407
407 <%def name="diff_menu(filediff, use_comments=False)">
408 <%def name="diff_menu(filediff, use_comments=False)">
408 <div class="filediff-menu">
409 <div class="filediff-menu">
409 %if filediff.diffset.source_ref:
410 %if filediff.diffset.source_ref:
410 %if filediff.operation in ['D', 'M']:
411 %if filediff.operation in ['D', 'M']:
411 <a
412 <a
412 class="tooltip"
413 class="tooltip"
413 href="${h.route_path('repo_files',repo_name=filediff.diffset.repo_name,commit_id=filediff.diffset.source_ref,f_path=filediff.source_file_path)}"
414 href="${h.route_path('repo_files',repo_name=filediff.diffset.repo_name,commit_id=filediff.diffset.source_ref,f_path=filediff.source_file_path)}"
414 title="${h.tooltip(_('Show file at commit: %(commit_id)s') % {'commit_id': filediff.diffset.source_ref[:12]})}"
415 title="${h.tooltip(_('Show file at commit: %(commit_id)s') % {'commit_id': filediff.diffset.source_ref[:12]})}"
415 >
416 >
416 ${_('Show file before')}
417 ${_('Show file before')}
417 </a> |
418 </a> |
418 %else:
419 %else:
419 <span
420 <span
420 class="tooltip"
421 class="tooltip"
421 title="${h.tooltip(_('File no longer present at commit: %(commit_id)s') % {'commit_id': filediff.diffset.source_ref[:12]})}"
422 title="${h.tooltip(_('File no longer present at commit: %(commit_id)s') % {'commit_id': filediff.diffset.source_ref[:12]})}"
422 >
423 >
423 ${_('Show file before')}
424 ${_('Show file before')}
424 </span> |
425 </span> |
425 %endif
426 %endif
426 %if filediff.operation in ['A', 'M']:
427 %if filediff.operation in ['A', 'M']:
427 <a
428 <a
428 class="tooltip"
429 class="tooltip"
429 href="${h.route_path('repo_files',repo_name=filediff.diffset.source_repo_name,commit_id=filediff.diffset.target_ref,f_path=filediff.target_file_path)}"
430 href="${h.route_path('repo_files',repo_name=filediff.diffset.source_repo_name,commit_id=filediff.diffset.target_ref,f_path=filediff.target_file_path)}"
430 title="${h.tooltip(_('Show file at commit: %(commit_id)s') % {'commit_id': filediff.diffset.target_ref[:12]})}"
431 title="${h.tooltip(_('Show file at commit: %(commit_id)s') % {'commit_id': filediff.diffset.target_ref[:12]})}"
431 >
432 >
432 ${_('Show file after')}
433 ${_('Show file after')}
433 </a> |
434 </a> |
434 %else:
435 %else:
435 <span
436 <span
436 class="tooltip"
437 class="tooltip"
437 title="${h.tooltip(_('File no longer present at commit: %(commit_id)s') % {'commit_id': filediff.diffset.target_ref[:12]})}"
438 title="${h.tooltip(_('File no longer present at commit: %(commit_id)s') % {'commit_id': filediff.diffset.target_ref[:12]})}"
438 >
439 >
439 ${_('Show file after')}
440 ${_('Show file after')}
440 </span> |
441 </span> |
441 %endif
442 %endif
442 <a
443 <a
443 class="tooltip"
444 class="tooltip"
444 title="${h.tooltip(_('Raw diff'))}"
445 title="${h.tooltip(_('Raw diff'))}"
445 href="${h.route_path('repo_files_diff',repo_name=filediff.diffset.repo_name,f_path=filediff.target_file_path, _query=dict(diff2=filediff.diffset.target_ref,diff1=filediff.diffset.source_ref,diff='raw'))}"
446 href="${h.route_path('repo_files_diff',repo_name=filediff.diffset.repo_name,f_path=filediff.target_file_path, _query=dict(diff2=filediff.diffset.target_ref,diff1=filediff.diffset.source_ref,diff='raw'))}"
446 >
447 >
447 ${_('Raw diff')}
448 ${_('Raw diff')}
448 </a> |
449 </a> |
449 <a
450 <a
450 class="tooltip"
451 class="tooltip"
451 title="${h.tooltip(_('Download diff'))}"
452 title="${h.tooltip(_('Download diff'))}"
452 href="${h.route_path('repo_files_diff',repo_name=filediff.diffset.repo_name,f_path=filediff.target_file_path, _query=dict(diff2=filediff.diffset.target_ref,diff1=filediff.diffset.source_ref,diff='download'))}"
453 href="${h.route_path('repo_files_diff',repo_name=filediff.diffset.repo_name,f_path=filediff.target_file_path, _query=dict(diff2=filediff.diffset.target_ref,diff1=filediff.diffset.source_ref,diff='download'))}"
453 >
454 >
454 ${_('Download diff')}
455 ${_('Download diff')}
455 </a>
456 </a>
456 % if use_comments:
457 % if use_comments:
457 |
458 |
458 % endif
459 % endif
459
460
460 ## TODO: dan: refactor ignorews_url and context_url into the diff renderer same as diffmode=unified/sideside. Also use ajax to load more context (by clicking hunks)
461 ## TODO: dan: refactor ignorews_url and context_url into the diff renderer same as diffmode=unified/sideside. Also use ajax to load more context (by clicking hunks)
461 %if hasattr(c, 'ignorews_url'):
462 %if hasattr(c, 'ignorews_url'):
462 ${c.ignorews_url(request, h.FID('', filediff.patch['filename']))}
463 ${c.ignorews_url(request, h.FID('', filediff.patch['filename']))}
463 %endif
464 %endif
464 %if hasattr(c, 'context_url'):
465 %if hasattr(c, 'context_url'):
465 ${c.context_url(request, h.FID('', filediff.patch['filename']))}
466 ${c.context_url(request, h.FID('', filediff.patch['filename']))}
466 %endif
467 %endif
467
468
468 %if use_comments:
469 %if use_comments:
469 <a href="#" onclick="return Rhodecode.comments.toggleComments(this);">
470 <a href="#" onclick="return Rhodecode.comments.toggleComments(this);">
470 <span class="show-comment-button">${_('Show comments')}</span><span class="hide-comment-button">${_('Hide comments')}</span>
471 <span class="show-comment-button">${_('Show comments')}</span><span class="hide-comment-button">${_('Hide comments')}</span>
471 </a>
472 </a>
472 %endif
473 %endif
473 %endif
474 %endif
474 </div>
475 </div>
475 </%def>
476 </%def>
476
477
477
478
478 <%def name="inline_comments_container(comments)">
479 <%def name="inline_comments_container(comments)">
479 <div class="inline-comments">
480 <div class="inline-comments">
480 %for comment in comments:
481 %for comment in comments:
481 ${commentblock.comment_block(comment, inline=True)}
482 ${commentblock.comment_block(comment, inline=True)}
482 %endfor
483 %endfor
483
484
484 % if comments and comments[-1].outdated:
485 % if comments and comments[-1].outdated:
485 <span class="btn btn-secondary cb-comment-add-button comment-outdated}"
486 <span class="btn btn-secondary cb-comment-add-button comment-outdated}"
486 style="display: none;}">
487 style="display: none;}">
487 ${_('Add another comment')}
488 ${_('Add another comment')}
488 </span>
489 </span>
489 % else:
490 % else:
490 <span onclick="return Rhodecode.comments.createComment(this)"
491 <span onclick="return Rhodecode.comments.createComment(this)"
491 class="btn btn-secondary cb-comment-add-button">
492 class="btn btn-secondary cb-comment-add-button">
492 ${_('Add another comment')}
493 ${_('Add another comment')}
493 </span>
494 </span>
494 % endif
495 % endif
495
496
496 </div>
497 </div>
497 </%def>
498 </%def>
498
499
499
500
500 <%def name="render_hunk_lines_sideside(hunk, use_comments=False)">
501 <%def name="render_hunk_lines_sideside(hunk, use_comments=False)">
501 %for i, line in enumerate(hunk.sideside):
502 %for i, line in enumerate(hunk.sideside):
502 <%
503 <%
503 old_line_anchor, new_line_anchor = None, None
504 old_line_anchor, new_line_anchor = None, None
504 if line.original.lineno:
505 if line.original.lineno:
505 old_line_anchor = diff_line_anchor(hunk.source_file_path, line.original.lineno, 'o')
506 old_line_anchor = diff_line_anchor(hunk.source_file_path, line.original.lineno, 'o')
506 if line.modified.lineno:
507 if line.modified.lineno:
507 new_line_anchor = diff_line_anchor(hunk.target_file_path, line.modified.lineno, 'n')
508 new_line_anchor = diff_line_anchor(hunk.target_file_path, line.modified.lineno, 'n')
508 %>
509 %>
509
510
510 <tr class="cb-line">
511 <tr class="cb-line">
511 <td class="cb-data ${action_class(line.original.action)}"
512 <td class="cb-data ${action_class(line.original.action)}"
512 data-line-number="${line.original.lineno}"
513 data-line-number="${line.original.lineno}"
513 >
514 >
514 <div>
515 <div>
515 %if line.original.comments:
516 %if line.original.comments:
516 <i class="icon-comment" onclick="return Rhodecode.comments.toggleLineComments(this)"></i>
517 <i class="icon-comment" onclick="return Rhodecode.comments.toggleLineComments(this)"></i>
517 %endif
518 %endif
518 </div>
519 </div>
519 </td>
520 </td>
520 <td class="cb-lineno ${action_class(line.original.action)}"
521 <td class="cb-lineno ${action_class(line.original.action)}"
521 data-line-number="${line.original.lineno}"
522 data-line-number="${line.original.lineno}"
522 %if old_line_anchor:
523 %if old_line_anchor:
523 id="${old_line_anchor}"
524 id="${old_line_anchor}"
524 %endif
525 %endif
525 >
526 >
526 %if line.original.lineno:
527 %if line.original.lineno:
527 <a name="${old_line_anchor}" href="#${old_line_anchor}">${line.original.lineno}</a>
528 <a name="${old_line_anchor}" href="#${old_line_anchor}">${line.original.lineno}</a>
528 %endif
529 %endif
529 </td>
530 </td>
530 <td class="cb-content ${action_class(line.original.action)}"
531 <td class="cb-content ${action_class(line.original.action)}"
531 data-line-number="o${line.original.lineno}"
532 data-line-number="o${line.original.lineno}"
532 >
533 >
533 %if use_comments and line.original.lineno:
534 %if use_comments and line.original.lineno:
534 ${render_add_comment_button()}
535 ${render_add_comment_button()}
535 %endif
536 %endif
536 <span class="cb-code">${line.original.action} ${line.original.content or '' | n}</span>
537 <span class="cb-code">${line.original.action} ${line.original.content or '' | n}</span>
537 %if use_comments and line.original.lineno and line.original.comments:
538 %if use_comments and line.original.lineno and line.original.comments:
538 ${inline_comments_container(line.original.comments)}
539 ${inline_comments_container(line.original.comments)}
539 %endif
540 %endif
540 </td>
541 </td>
541 <td class="cb-data ${action_class(line.modified.action)}"
542 <td class="cb-data ${action_class(line.modified.action)}"
542 data-line-number="${line.modified.lineno}"
543 data-line-number="${line.modified.lineno}"
543 >
544 >
544 <div>
545 <div>
545 %if line.modified.comments:
546 %if line.modified.comments:
546 <i class="icon-comment" onclick="return Rhodecode.comments.toggleLineComments(this)"></i>
547 <i class="icon-comment" onclick="return Rhodecode.comments.toggleLineComments(this)"></i>
547 %endif
548 %endif
548 </div>
549 </div>
549 </td>
550 </td>
550 <td class="cb-lineno ${action_class(line.modified.action)}"
551 <td class="cb-lineno ${action_class(line.modified.action)}"
551 data-line-number="${line.modified.lineno}"
552 data-line-number="${line.modified.lineno}"
552 %if new_line_anchor:
553 %if new_line_anchor:
553 id="${new_line_anchor}"
554 id="${new_line_anchor}"
554 %endif
555 %endif
555 >
556 >
556 %if line.modified.lineno:
557 %if line.modified.lineno:
557 <a name="${new_line_anchor}" href="#${new_line_anchor}">${line.modified.lineno}</a>
558 <a name="${new_line_anchor}" href="#${new_line_anchor}">${line.modified.lineno}</a>
558 %endif
559 %endif
559 </td>
560 </td>
560 <td class="cb-content ${action_class(line.modified.action)}"
561 <td class="cb-content ${action_class(line.modified.action)}"
561 data-line-number="n${line.modified.lineno}"
562 data-line-number="n${line.modified.lineno}"
562 >
563 >
563 %if use_comments and line.modified.lineno:
564 %if use_comments and line.modified.lineno:
564 ${render_add_comment_button()}
565 ${render_add_comment_button()}
565 %endif
566 %endif
566 <span class="cb-code">${line.modified.action} ${line.modified.content or '' | n}</span>
567 <span class="cb-code">${line.modified.action} ${line.modified.content or '' | n}</span>
567 %if use_comments and line.modified.lineno and line.modified.comments:
568 %if use_comments and line.modified.lineno and line.modified.comments:
568 ${inline_comments_container(line.modified.comments)}
569 ${inline_comments_container(line.modified.comments)}
569 %endif
570 %endif
570 </td>
571 </td>
571 </tr>
572 </tr>
572 %endfor
573 %endfor
573 </%def>
574 </%def>
574
575
575
576
576 <%def name="render_hunk_lines_unified(hunk, use_comments=False)">
577 <%def name="render_hunk_lines_unified(hunk, use_comments=False)">
577 %for old_line_no, new_line_no, action, content, comments in hunk.unified:
578 %for old_line_no, new_line_no, action, content, comments in hunk.unified:
578 <%
579 <%
579 old_line_anchor, new_line_anchor = None, None
580 old_line_anchor, new_line_anchor = None, None
580 if old_line_no:
581 if old_line_no:
581 old_line_anchor = diff_line_anchor(hunk.source_file_path, old_line_no, 'o')
582 old_line_anchor = diff_line_anchor(hunk.source_file_path, old_line_no, 'o')
582 if new_line_no:
583 if new_line_no:
583 new_line_anchor = diff_line_anchor(hunk.target_file_path, new_line_no, 'n')
584 new_line_anchor = diff_line_anchor(hunk.target_file_path, new_line_no, 'n')
584 %>
585 %>
585 <tr class="cb-line">
586 <tr class="cb-line">
586 <td class="cb-data ${action_class(action)}">
587 <td class="cb-data ${action_class(action)}">
587 <div>
588 <div>
588 %if comments:
589 %if comments:
589 <i class="icon-comment" onclick="return Rhodecode.comments.toggleLineComments(this)"></i>
590 <i class="icon-comment" onclick="return Rhodecode.comments.toggleLineComments(this)"></i>
590 %endif
591 %endif
591 </div>
592 </div>
592 </td>
593 </td>
593 <td class="cb-lineno ${action_class(action)}"
594 <td class="cb-lineno ${action_class(action)}"
594 data-line-number="${old_line_no}"
595 data-line-number="${old_line_no}"
595 %if old_line_anchor:
596 %if old_line_anchor:
596 id="${old_line_anchor}"
597 id="${old_line_anchor}"
597 %endif
598 %endif
598 >
599 >
599 %if old_line_anchor:
600 %if old_line_anchor:
600 <a name="${old_line_anchor}" href="#${old_line_anchor}">${old_line_no}</a>
601 <a name="${old_line_anchor}" href="#${old_line_anchor}">${old_line_no}</a>
601 %endif
602 %endif
602 </td>
603 </td>
603 <td class="cb-lineno ${action_class(action)}"
604 <td class="cb-lineno ${action_class(action)}"
604 data-line-number="${new_line_no}"
605 data-line-number="${new_line_no}"
605 %if new_line_anchor:
606 %if new_line_anchor:
606 id="${new_line_anchor}"
607 id="${new_line_anchor}"
607 %endif
608 %endif
608 >
609 >
609 %if new_line_anchor:
610 %if new_line_anchor:
610 <a name="${new_line_anchor}" href="#${new_line_anchor}">${new_line_no}</a>
611 <a name="${new_line_anchor}" href="#${new_line_anchor}">${new_line_no}</a>
611 %endif
612 %endif
612 </td>
613 </td>
613 <td class="cb-content ${action_class(action)}"
614 <td class="cb-content ${action_class(action)}"
614 data-line-number="${new_line_no and 'n' or 'o'}${new_line_no or old_line_no}"
615 data-line-number="${new_line_no and 'n' or 'o'}${new_line_no or old_line_no}"
615 >
616 >
616 %if use_comments:
617 %if use_comments:
617 ${render_add_comment_button()}
618 ${render_add_comment_button()}
618 %endif
619 %endif
619 <span class="cb-code">${action} ${content or '' | n}</span>
620 <span class="cb-code">${action} ${content or '' | n}</span>
620 %if use_comments and comments:
621 %if use_comments and comments:
621 ${inline_comments_container(comments)}
622 ${inline_comments_container(comments)}
622 %endif
623 %endif
623 </td>
624 </td>
624 </tr>
625 </tr>
625 %endfor
626 %endfor
626 </%def>
627 </%def>
627
628
628 <%def name="render_add_comment_button()">
629 <%def name="render_add_comment_button()">
629 <button class="btn btn-small btn-primary cb-comment-box-opener" onclick="return Rhodecode.comments.createComment(this)">
630 <button class="btn btn-small btn-primary cb-comment-box-opener" onclick="return Rhodecode.comments.createComment(this)">
630 <span><i class="icon-comment"></i></span>
631 <span><i class="icon-comment"></i></span>
631 </button>
632 </button>
632 </%def>
633 </%def>
633
634
634 <%def name="render_diffset_menu()">
635 <%def name="render_diffset_menu()">
635
636
636 <div class="diffset-menu clearinner">
637 <div class="diffset-menu clearinner">
637 <div class="pull-right">
638 <div class="pull-right">
638 <div class="btn-group">
639 <div class="btn-group">
639
640
640 <a
641 <a
641 class="btn ${c.diffmode == 'sideside' and 'btn-primary'} tooltip"
642 class="btn ${c.diffmode == 'sideside' and 'btn-primary'} tooltip"
642 title="${h.tooltip(_('View side by side'))}"
643 title="${h.tooltip(_('View side by side'))}"
643 href="${h.url_replace(diffmode='sideside')}">
644 href="${h.url_replace(diffmode='sideside')}">
644 <span>${_('Side by Side')}</span>
645 <span>${_('Side by Side')}</span>
645 </a>
646 </a>
646 <a
647 <a
647 class="btn ${c.diffmode == 'unified' and 'btn-primary'} tooltip"
648 class="btn ${c.diffmode == 'unified' and 'btn-primary'} tooltip"
648 title="${h.tooltip(_('View unified'))}" href="${h.url_replace(diffmode='unified')}">
649 title="${h.tooltip(_('View unified'))}" href="${h.url_replace(diffmode='unified')}">
649 <span>${_('Unified')}</span>
650 <span>${_('Unified')}</span>
650 </a>
651 </a>
651 </div>
652 </div>
652 </div>
653 </div>
653
654
654 <div class="pull-left">
655 <div class="pull-left">
655 <div class="btn-group">
656 <div class="btn-group">
656 <a
657 <a
657 class="btn"
658 class="btn"
658 href="#"
659 href="#"
659 onclick="$('input[class=filediff-collapse-state]').prop('checked', false); return false">${_('Expand All Files')}</a>
660 onclick="$('input[class=filediff-collapse-state]').prop('checked', false); return false">${_('Expand All Files')}</a>
660 <a
661 <a
661 class="btn"
662 class="btn"
662 href="#"
663 href="#"
663 onclick="$('input[class=filediff-collapse-state]').prop('checked', true); return false">${_('Collapse All Files')}</a>
664 onclick="$('input[class=filediff-collapse-state]').prop('checked', true); return false">${_('Collapse All Files')}</a>
664 <a
665 <a
665 class="btn"
666 class="btn"
666 href="#"
667 href="#"
667 onclick="return Rhodecode.comments.toggleWideMode(this)">${_('Wide Mode Diff')}</a>
668 onclick="return Rhodecode.comments.toggleWideMode(this)">${_('Wide Mode Diff')}</a>
668 </div>
669 </div>
669 </div>
670 </div>
670 </div>
671 </div>
671 </%def>
672 </%def>
@@ -1,74 +1,74 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2 <%inherit file="/debug_style/index.html"/>
2 <%inherit file="/debug_style/index.html"/>
3
3
4
4
5 <%def name="breadcrumbs_links()">
5 <%def name="breadcrumbs_links()">
6 ${h.link_to(_('Style'), h.route_path('debug_style_home'))}
6 ${h.link_to(_('Style'), h.route_path('debug_style_home'))}
7 &raquo;
7 &raquo;
8 ${c.active}
8 ${c.active}
9 </%def>
9 </%def>
10
10
11
11
12 <%def name="real_main()">
12 <%def name="real_main()">
13 <div class="box">
13 <div class="box">
14 <div class="title">
14 <div class="title">
15 ${self.breadcrumbs()}
15 ${self.breadcrumbs()}
16 </div>
16 </div>
17
17
18 ##main
18 ##main
19 <div class='sidebar-col-wrapper'>
19 <div class='sidebar-col-wrapper'>
20 ${self.sidebar()}
20 ${self.sidebar()}
21
21
22 <div class="main-content">
22 <div class="main-content">
23
23
24
24
25 <div class="bs-example pull-left">
25 <div class="bs-example pull-left">
26
26
27 <div id="quick_login">
27 <div id="quick_login">
28 <h4>${_('Sign in to your account')}</h4>
28 <h4>${_('Sign in to your account')}</h4>
29
29
30 ${h.form(h.url('login_home',came_from=h.url.current()), needs_csrf_token=False)}
30 ${h.form(h.route_path('login'), needs_csrf_token=False)}
31 <div class="form form-vertical">
31 <div class="form form-vertical">
32 <div class="fields">
32 <div class="fields">
33
33
34 <div class="field">
34 <div class="field">
35 <div class="label">
35 <div class="label">
36 <label for="username">${_('Username')}:</label>
36 <label for="username">${_('Username')}:</label>
37 </div>
37 </div>
38 <div class="input">
38 <div class="input">
39 ${h.text('username',class_='focus',tabindex=1)}
39 ${h.text('username',class_='focus',tabindex=1)}
40 </div>
40 </div>
41 </div>
41 </div>
42
42
43 <div class="field">
43 <div class="field">
44 <div class="label">
44 <div class="label">
45 <label for="password">${_('Password')}:</label>
45 <label for="password">${_('Password')}:</label>
46 <span class="forgot_password">${h.link_to(_('(Forgot password?)'),h.url('reset_password'))}</span>
46 <span class="forgot_password">${h.link_to(_('(Forgot password?)'),h.route_path('reset_password'))}</span>
47 </div>
47 </div>
48 <div class="input">
48 <div class="input">
49 ${h.password('password',class_='focus',tabindex=2)}
49 ${h.password('password',class_='focus',tabindex=2)}
50 </div>
50 </div>
51 </div>
51 </div>
52
52
53 <div class="buttons">
53 <div class="buttons">
54 <div class="register">
54 <div class="register">
55 %if h.HasPermissionAny('hg.admin', 'hg.register.auto_activate', 'hg.register.manual_activate')():
55 %if h.HasPermissionAny('hg.admin', 'hg.register.auto_activate', 'hg.register.manual_activate')():
56 ${h.link_to(_("Don't have an account ?"),h.url('register'))}
56 ${h.link_to(_("Don't have an account ?"),h.route_path('register'))}
57 %endif
57 %endif
58 </div>
58 </div>
59 <div class="submit">
59 <div class="submit">
60 ${h.submit('sign_in',_('Sign In'),class_="btn btn-small",tabindex=3)}
60 ${h.submit('sign_in',_('Sign In'),class_="btn btn-small",tabindex=3)}
61 </div>
61 </div>
62 </div>
62 </div>
63
63
64 </div>
64 </div>
65 </div>
65 </div>
66 ${h.end_form()}
66 ${h.end_form()}
67 </div>
67 </div>
68
68
69 </div>
69 </div>
70 </div>
70 </div>
71 </div>
71 </div>
72 </div>
72 </div>
73
73
74 </%def>
74 </%def>
General Comments 0
You need to be logged in to leave comments. Login now