##// END OF EJS Templates
comments: added support for adding comment attachments using the artifacts logic....
bart -
r3973:d9b26077 default
parent child Browse files
Show More

The requested changes are too big and content was truncated. Show full diff

@@ -1,231 +1,240 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2019 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 time
22 import time
23 import shutil
23 import shutil
24 import hashlib
24 import hashlib
25
25
26 from rhodecode.lib.ext_json import json
26 from rhodecode.lib.ext_json import json
27 from rhodecode.apps.file_store import utils
27 from rhodecode.apps.file_store import utils
28 from rhodecode.apps.file_store.extensions import resolve_extensions
28 from rhodecode.apps.file_store.extensions import resolve_extensions
29 from rhodecode.apps.file_store.exceptions import FileNotAllowedException
29 from rhodecode.apps.file_store.exceptions import (
30 FileNotAllowedException, FileOverSizeException)
30
31
31 METADATA_VER = 'v1'
32 METADATA_VER = 'v1'
32
33
33
34
34 class LocalFileStorage(object):
35 class LocalFileStorage(object):
35
36
36 @classmethod
37 @classmethod
37 def resolve_name(cls, name, directory):
38 def resolve_name(cls, name, directory):
38 """
39 """
39 Resolves a unique name and the correct path. If a filename
40 Resolves a unique name and the correct path. If a filename
40 for that path already exists then a numeric prefix with values > 0 will be
41 for that path already exists then a numeric prefix with values > 0 will be
41 added, for example test.jpg -> 1-test.jpg etc. initially file would have 0 prefix.
42 added, for example test.jpg -> 1-test.jpg etc. initially file would have 0 prefix.
42
43
43 :param name: base name of file
44 :param name: base name of file
44 :param directory: absolute directory path
45 :param directory: absolute directory path
45 """
46 """
46
47
47 counter = 0
48 counter = 0
48 while True:
49 while True:
49 name = '%d-%s' % (counter, name)
50 name = '%d-%s' % (counter, name)
50
51
51 # sub_store prefix to optimize disk usage, e.g some_path/ab/final_file
52 # sub_store prefix to optimize disk usage, e.g some_path/ab/final_file
52 sub_store = cls._sub_store_from_filename(name)
53 sub_store = cls._sub_store_from_filename(name)
53 sub_store_path = os.path.join(directory, sub_store)
54 sub_store_path = os.path.join(directory, sub_store)
54 if not os.path.exists(sub_store_path):
55 if not os.path.exists(sub_store_path):
55 os.makedirs(sub_store_path)
56 os.makedirs(sub_store_path)
56
57
57 path = os.path.join(sub_store_path, name)
58 path = os.path.join(sub_store_path, name)
58 if not os.path.exists(path):
59 if not os.path.exists(path):
59 return name, path
60 return name, path
60 counter += 1
61 counter += 1
61
62
62 @classmethod
63 @classmethod
63 def _sub_store_from_filename(cls, filename):
64 def _sub_store_from_filename(cls, filename):
64 return filename[:2]
65 return filename[:2]
65
66
66 @classmethod
67 @classmethod
67 def calculate_path_hash(cls, file_path):
68 def calculate_path_hash(cls, file_path):
68 """
69 """
69 Efficient calculation of file_path sha256 sum
70 Efficient calculation of file_path sha256 sum
70
71
71 :param file_path:
72 :param file_path:
72 :return: sha256sum
73 :return: sha256sum
73 """
74 """
74 digest = hashlib.sha256()
75 digest = hashlib.sha256()
75 with open(file_path, 'rb') as f:
76 with open(file_path, 'rb') as f:
76 for chunk in iter(lambda: f.read(1024 * 100), b""):
77 for chunk in iter(lambda: f.read(1024 * 100), b""):
77 digest.update(chunk)
78 digest.update(chunk)
78
79
79 return digest.hexdigest()
80 return digest.hexdigest()
80
81
81 def __init__(self, base_path, extension_groups=None):
82 def __init__(self, base_path, extension_groups=None):
82
83
83 """
84 """
84 Local file storage
85 Local file storage
85
86
86 :param base_path: the absolute base path where uploads are stored
87 :param base_path: the absolute base path where uploads are stored
87 :param extension_groups: extensions string
88 :param extension_groups: extensions string
88 """
89 """
89
90
90 extension_groups = extension_groups or ['any']
91 extension_groups = extension_groups or ['any']
91 self.base_path = base_path
92 self.base_path = base_path
92 self.extensions = resolve_extensions([], groups=extension_groups)
93 self.extensions = resolve_extensions([], groups=extension_groups)
93
94
94 def __repr__(self):
95 def __repr__(self):
95 return '{}@{}'.format(self.__class__, self.base_path)
96 return '{}@{}'.format(self.__class__, self.base_path)
96
97
97 def store_path(self, filename):
98 def store_path(self, filename):
98 """
99 """
99 Returns absolute file path of the filename, joined to the
100 Returns absolute file path of the filename, joined to the
100 base_path.
101 base_path.
101
102
102 :param filename: base name of file
103 :param filename: base name of file
103 """
104 """
104 sub_store = self._sub_store_from_filename(filename)
105 sub_store = self._sub_store_from_filename(filename)
105 return os.path.join(self.base_path, sub_store, filename)
106 return os.path.join(self.base_path, sub_store, filename)
106
107
107 def delete(self, filename):
108 def delete(self, filename):
108 """
109 """
109 Deletes the filename. Filename is resolved with the
110 Deletes the filename. Filename is resolved with the
110 absolute path based on base_path. If file does not exist,
111 absolute path based on base_path. If file does not exist,
111 returns **False**, otherwise **True**
112 returns **False**, otherwise **True**
112
113
113 :param filename: base name of file
114 :param filename: base name of file
114 """
115 """
115 if self.exists(filename):
116 if self.exists(filename):
116 os.remove(self.store_path(filename))
117 os.remove(self.store_path(filename))
117 return True
118 return True
118 return False
119 return False
119
120
120 def exists(self, filename):
121 def exists(self, filename):
121 """
122 """
122 Checks if file exists. Resolves filename's absolute
123 Checks if file exists. Resolves filename's absolute
123 path based on base_path.
124 path based on base_path.
124
125
125 :param filename: base name of file
126 :param filename: base name of file
126 """
127 """
127 return os.path.exists(self.store_path(filename))
128 return os.path.exists(self.store_path(filename))
128
129
129 def filename_allowed(self, filename, extensions=None):
130 def filename_allowed(self, filename, extensions=None):
130 """Checks if a filename has an allowed extension
131 """Checks if a filename has an allowed extension
131
132
132 :param filename: base name of file
133 :param filename: base name of file
133 :param extensions: iterable of extensions (or self.extensions)
134 :param extensions: iterable of extensions (or self.extensions)
134 """
135 """
135 _, ext = os.path.splitext(filename)
136 _, ext = os.path.splitext(filename)
136 return self.extension_allowed(ext, extensions)
137 return self.extension_allowed(ext, extensions)
137
138
138 def extension_allowed(self, ext, extensions=None):
139 def extension_allowed(self, ext, extensions=None):
139 """
140 """
140 Checks if an extension is permitted. Both e.g. ".jpg" and
141 Checks if an extension is permitted. Both e.g. ".jpg" and
141 "jpg" can be passed in. Extension lookup is case-insensitive.
142 "jpg" can be passed in. Extension lookup is case-insensitive.
142
143
143 :param ext: extension to check
144 :param ext: extension to check
144 :param extensions: iterable of extensions to validate against (or self.extensions)
145 :param extensions: iterable of extensions to validate against (or self.extensions)
145 """
146 """
146 def normalize_ext(_ext):
147 def normalize_ext(_ext):
147 if _ext.startswith('.'):
148 if _ext.startswith('.'):
148 _ext = _ext[1:]
149 _ext = _ext[1:]
149 return _ext.lower()
150 return _ext.lower()
150
151
151 extensions = extensions or self.extensions
152 extensions = extensions or self.extensions
152 if not extensions:
153 if not extensions:
153 return True
154 return True
154
155
155 ext = normalize_ext(ext)
156 ext = normalize_ext(ext)
156
157
157 return ext in [normalize_ext(x) for x in extensions]
158 return ext in [normalize_ext(x) for x in extensions]
158
159
159 def save_file(self, file_obj, filename, directory=None, extensions=None,
160 def save_file(self, file_obj, filename, directory=None, extensions=None,
160 extra_metadata=None, **kwargs):
161 extra_metadata=None, max_filesize=None, **kwargs):
161 """
162 """
162 Saves a file object to the uploads location.
163 Saves a file object to the uploads location.
163 Returns the resolved filename, i.e. the directory +
164 Returns the resolved filename, i.e. the directory +
164 the (randomized/incremented) base name.
165 the (randomized/incremented) base name.
165
166
166 :param file_obj: **cgi.FieldStorage** object (or similar)
167 :param file_obj: **cgi.FieldStorage** object (or similar)
167 :param filename: original filename
168 :param filename: original filename
168 :param directory: relative path of sub-directory
169 :param directory: relative path of sub-directory
169 :param extensions: iterable of allowed extensions, if not default
170 :param extensions: iterable of allowed extensions, if not default
171 :param max_filesize: maximum size of file that should be allowed
170 :param extra_metadata: extra JSON metadata to store next to the file with .meta suffix
172 :param extra_metadata: extra JSON metadata to store next to the file with .meta suffix
173
171 """
174 """
172
175
173 extensions = extensions or self.extensions
176 extensions = extensions or self.extensions
174
177
175 if not self.filename_allowed(filename, extensions):
178 if not self.filename_allowed(filename, extensions):
176 raise FileNotAllowedException()
179 raise FileNotAllowedException()
177
180
178 if directory:
181 if directory:
179 dest_directory = os.path.join(self.base_path, directory)
182 dest_directory = os.path.join(self.base_path, directory)
180 else:
183 else:
181 dest_directory = self.base_path
184 dest_directory = self.base_path
182
185
183 if not os.path.exists(dest_directory):
186 if not os.path.exists(dest_directory):
184 os.makedirs(dest_directory)
187 os.makedirs(dest_directory)
185
188
186 filename = utils.uid_filename(filename)
189 filename = utils.uid_filename(filename)
187
190
188 # resolve also produces special sub-dir for file optimized store
191 # resolve also produces special sub-dir for file optimized store
189 filename, path = self.resolve_name(filename, dest_directory)
192 filename, path = self.resolve_name(filename, dest_directory)
190 stored_file_dir = os.path.dirname(path)
193 stored_file_dir = os.path.dirname(path)
191
194
192 file_obj.seek(0)
195 file_obj.seek(0)
193
196
194 with open(path, "wb") as dest:
197 with open(path, "wb") as dest:
195 shutil.copyfileobj(file_obj, dest)
198 shutil.copyfileobj(file_obj, dest)
196
199
197 metadata = {}
200 metadata = {}
198 if extra_metadata:
201 if extra_metadata:
199 metadata = extra_metadata
202 metadata = extra_metadata
200
203
201 size = os.stat(path).st_size
204 size = os.stat(path).st_size
205
206 if max_filesize and size > max_filesize:
207 # free up the copied file, and raise exc
208 os.remove(path)
209 raise FileOverSizeException()
210
202 file_hash = self.calculate_path_hash(path)
211 file_hash = self.calculate_path_hash(path)
203
212
204 metadata.update(
213 metadata.update(
205 {"filename": filename,
214 {"filename": filename,
206 "size": size,
215 "size": size,
207 "time": time.time(),
216 "time": time.time(),
208 "sha256": file_hash,
217 "sha256": file_hash,
209 "meta_ver": METADATA_VER})
218 "meta_ver": METADATA_VER})
210
219
211 filename_meta = filename + '.meta'
220 filename_meta = filename + '.meta'
212 with open(os.path.join(stored_file_dir, filename_meta), "wb") as dest_meta:
221 with open(os.path.join(stored_file_dir, filename_meta), "wb") as dest_meta:
213 dest_meta.write(json.dumps(metadata))
222 dest_meta.write(json.dumps(metadata))
214
223
215 if directory:
224 if directory:
216 filename = os.path.join(directory, filename)
225 filename = os.path.join(directory, filename)
217
226
218 return filename, metadata
227 return filename, metadata
219
228
220 def get_metadata(self, filename):
229 def get_metadata(self, filename):
221 """
230 """
222 Reads JSON stored metadata for a file
231 Reads JSON stored metadata for a file
223
232
224 :param filename:
233 :param filename:
225 :return:
234 :return:
226 """
235 """
227 filename = self.store_path(filename)
236 filename = self.store_path(filename)
228 filename_meta = filename + '.meta'
237 filename_meta = filename + '.meta'
229
238
230 with open(filename_meta, "rb") as source_meta:
239 with open(filename_meta, "rb") as source_meta:
231 return json.loads(source_meta.read())
240 return json.loads(source_meta.read())
@@ -1,512 +1,516 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2019 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 from rhodecode.apps._base import add_route_with_slash
20 from rhodecode.apps._base import add_route_with_slash
21
21
22
22
23 def includeme(config):
23 def includeme(config):
24
24
25 # repo creating checks, special cases that aren't repo routes
25 # repo creating checks, special cases that aren't repo routes
26 config.add_route(
26 config.add_route(
27 name='repo_creating',
27 name='repo_creating',
28 pattern='/{repo_name:.*?[^/]}/repo_creating')
28 pattern='/{repo_name:.*?[^/]}/repo_creating')
29
29
30 config.add_route(
30 config.add_route(
31 name='repo_creating_check',
31 name='repo_creating_check',
32 pattern='/{repo_name:.*?[^/]}/repo_creating_check')
32 pattern='/{repo_name:.*?[^/]}/repo_creating_check')
33
33
34 # Summary
34 # Summary
35 # NOTE(marcink): one additional route is defined in very bottom, catch
35 # NOTE(marcink): one additional route is defined in very bottom, catch
36 # all pattern
36 # all pattern
37 config.add_route(
37 config.add_route(
38 name='repo_summary_explicit',
38 name='repo_summary_explicit',
39 pattern='/{repo_name:.*?[^/]}/summary', repo_route=True)
39 pattern='/{repo_name:.*?[^/]}/summary', repo_route=True)
40 config.add_route(
40 config.add_route(
41 name='repo_summary_commits',
41 name='repo_summary_commits',
42 pattern='/{repo_name:.*?[^/]}/summary-commits', repo_route=True)
42 pattern='/{repo_name:.*?[^/]}/summary-commits', repo_route=True)
43
43
44 # Commits
44 # Commits
45 config.add_route(
45 config.add_route(
46 name='repo_commit',
46 name='repo_commit',
47 pattern='/{repo_name:.*?[^/]}/changeset/{commit_id}', repo_route=True)
47 pattern='/{repo_name:.*?[^/]}/changeset/{commit_id}', repo_route=True)
48
48
49 config.add_route(
49 config.add_route(
50 name='repo_commit_children',
50 name='repo_commit_children',
51 pattern='/{repo_name:.*?[^/]}/changeset_children/{commit_id}', repo_route=True)
51 pattern='/{repo_name:.*?[^/]}/changeset_children/{commit_id}', repo_route=True)
52
52
53 config.add_route(
53 config.add_route(
54 name='repo_commit_parents',
54 name='repo_commit_parents',
55 pattern='/{repo_name:.*?[^/]}/changeset_parents/{commit_id}', repo_route=True)
55 pattern='/{repo_name:.*?[^/]}/changeset_parents/{commit_id}', repo_route=True)
56
56
57 config.add_route(
57 config.add_route(
58 name='repo_commit_raw',
58 name='repo_commit_raw',
59 pattern='/{repo_name:.*?[^/]}/changeset-diff/{commit_id}', repo_route=True)
59 pattern='/{repo_name:.*?[^/]}/changeset-diff/{commit_id}', repo_route=True)
60
60
61 config.add_route(
61 config.add_route(
62 name='repo_commit_patch',
62 name='repo_commit_patch',
63 pattern='/{repo_name:.*?[^/]}/changeset-patch/{commit_id}', repo_route=True)
63 pattern='/{repo_name:.*?[^/]}/changeset-patch/{commit_id}', repo_route=True)
64
64
65 config.add_route(
65 config.add_route(
66 name='repo_commit_download',
66 name='repo_commit_download',
67 pattern='/{repo_name:.*?[^/]}/changeset-download/{commit_id}', repo_route=True)
67 pattern='/{repo_name:.*?[^/]}/changeset-download/{commit_id}', repo_route=True)
68
68
69 config.add_route(
69 config.add_route(
70 name='repo_commit_data',
70 name='repo_commit_data',
71 pattern='/{repo_name:.*?[^/]}/changeset-data/{commit_id}', repo_route=True)
71 pattern='/{repo_name:.*?[^/]}/changeset-data/{commit_id}', repo_route=True)
72
72
73 config.add_route(
73 config.add_route(
74 name='repo_commit_comment_create',
74 name='repo_commit_comment_create',
75 pattern='/{repo_name:.*?[^/]}/changeset/{commit_id}/comment/create', repo_route=True)
75 pattern='/{repo_name:.*?[^/]}/changeset/{commit_id}/comment/create', repo_route=True)
76
76
77 config.add_route(
77 config.add_route(
78 name='repo_commit_comment_preview',
78 name='repo_commit_comment_preview',
79 pattern='/{repo_name:.*?[^/]}/changeset/{commit_id}/comment/preview', repo_route=True)
79 pattern='/{repo_name:.*?[^/]}/changeset/{commit_id}/comment/preview', repo_route=True)
80
80
81 config.add_route(
81 config.add_route(
82 name='repo_commit_comment_attachment_upload',
83 pattern='/{repo_name:.*?[^/]}/changeset/{commit_id}/comment/attachment_upload', repo_route=True)
84
85 config.add_route(
82 name='repo_commit_comment_delete',
86 name='repo_commit_comment_delete',
83 pattern='/{repo_name:.*?[^/]}/changeset/{commit_id}/comment/{comment_id}/delete', repo_route=True)
87 pattern='/{repo_name:.*?[^/]}/changeset/{commit_id}/comment/{comment_id}/delete', repo_route=True)
84
88
85 # still working url for backward compat.
89 # still working url for backward compat.
86 config.add_route(
90 config.add_route(
87 name='repo_commit_raw_deprecated',
91 name='repo_commit_raw_deprecated',
88 pattern='/{repo_name:.*?[^/]}/raw-changeset/{commit_id}', repo_route=True)
92 pattern='/{repo_name:.*?[^/]}/raw-changeset/{commit_id}', repo_route=True)
89
93
90 # Files
94 # Files
91 config.add_route(
95 config.add_route(
92 name='repo_archivefile',
96 name='repo_archivefile',
93 pattern='/{repo_name:.*?[^/]}/archive/{fname:.*}', repo_route=True)
97 pattern='/{repo_name:.*?[^/]}/archive/{fname:.*}', repo_route=True)
94
98
95 config.add_route(
99 config.add_route(
96 name='repo_files_diff',
100 name='repo_files_diff',
97 pattern='/{repo_name:.*?[^/]}/diff/{f_path:.*}', repo_route=True)
101 pattern='/{repo_name:.*?[^/]}/diff/{f_path:.*}', repo_route=True)
98 config.add_route( # legacy route to make old links work
102 config.add_route( # legacy route to make old links work
99 name='repo_files_diff_2way_redirect',
103 name='repo_files_diff_2way_redirect',
100 pattern='/{repo_name:.*?[^/]}/diff-2way/{f_path:.*}', repo_route=True)
104 pattern='/{repo_name:.*?[^/]}/diff-2way/{f_path:.*}', repo_route=True)
101
105
102 config.add_route(
106 config.add_route(
103 name='repo_files',
107 name='repo_files',
104 pattern='/{repo_name:.*?[^/]}/files/{commit_id}/{f_path:.*}', repo_route=True)
108 pattern='/{repo_name:.*?[^/]}/files/{commit_id}/{f_path:.*}', repo_route=True)
105 config.add_route(
109 config.add_route(
106 name='repo_files:default_path',
110 name='repo_files:default_path',
107 pattern='/{repo_name:.*?[^/]}/files/{commit_id}/', repo_route=True)
111 pattern='/{repo_name:.*?[^/]}/files/{commit_id}/', repo_route=True)
108 config.add_route(
112 config.add_route(
109 name='repo_files:default_commit',
113 name='repo_files:default_commit',
110 pattern='/{repo_name:.*?[^/]}/files', repo_route=True)
114 pattern='/{repo_name:.*?[^/]}/files', repo_route=True)
111
115
112 config.add_route(
116 config.add_route(
113 name='repo_files:rendered',
117 name='repo_files:rendered',
114 pattern='/{repo_name:.*?[^/]}/render/{commit_id}/{f_path:.*}', repo_route=True)
118 pattern='/{repo_name:.*?[^/]}/render/{commit_id}/{f_path:.*}', repo_route=True)
115
119
116 config.add_route(
120 config.add_route(
117 name='repo_files:annotated',
121 name='repo_files:annotated',
118 pattern='/{repo_name:.*?[^/]}/annotate/{commit_id}/{f_path:.*}', repo_route=True)
122 pattern='/{repo_name:.*?[^/]}/annotate/{commit_id}/{f_path:.*}', repo_route=True)
119 config.add_route(
123 config.add_route(
120 name='repo_files:annotated_previous',
124 name='repo_files:annotated_previous',
121 pattern='/{repo_name:.*?[^/]}/annotate-previous/{commit_id}/{f_path:.*}', repo_route=True)
125 pattern='/{repo_name:.*?[^/]}/annotate-previous/{commit_id}/{f_path:.*}', repo_route=True)
122
126
123 config.add_route(
127 config.add_route(
124 name='repo_nodetree_full',
128 name='repo_nodetree_full',
125 pattern='/{repo_name:.*?[^/]}/nodetree_full/{commit_id}/{f_path:.*}', repo_route=True)
129 pattern='/{repo_name:.*?[^/]}/nodetree_full/{commit_id}/{f_path:.*}', repo_route=True)
126 config.add_route(
130 config.add_route(
127 name='repo_nodetree_full:default_path',
131 name='repo_nodetree_full:default_path',
128 pattern='/{repo_name:.*?[^/]}/nodetree_full/{commit_id}/', repo_route=True)
132 pattern='/{repo_name:.*?[^/]}/nodetree_full/{commit_id}/', repo_route=True)
129
133
130 config.add_route(
134 config.add_route(
131 name='repo_files_nodelist',
135 name='repo_files_nodelist',
132 pattern='/{repo_name:.*?[^/]}/nodelist/{commit_id}/{f_path:.*}', repo_route=True)
136 pattern='/{repo_name:.*?[^/]}/nodelist/{commit_id}/{f_path:.*}', repo_route=True)
133
137
134 config.add_route(
138 config.add_route(
135 name='repo_file_raw',
139 name='repo_file_raw',
136 pattern='/{repo_name:.*?[^/]}/raw/{commit_id}/{f_path:.*}', repo_route=True)
140 pattern='/{repo_name:.*?[^/]}/raw/{commit_id}/{f_path:.*}', repo_route=True)
137
141
138 config.add_route(
142 config.add_route(
139 name='repo_file_download',
143 name='repo_file_download',
140 pattern='/{repo_name:.*?[^/]}/download/{commit_id}/{f_path:.*}', repo_route=True)
144 pattern='/{repo_name:.*?[^/]}/download/{commit_id}/{f_path:.*}', repo_route=True)
141 config.add_route( # backward compat to keep old links working
145 config.add_route( # backward compat to keep old links working
142 name='repo_file_download:legacy',
146 name='repo_file_download:legacy',
143 pattern='/{repo_name:.*?[^/]}/rawfile/{commit_id}/{f_path:.*}',
147 pattern='/{repo_name:.*?[^/]}/rawfile/{commit_id}/{f_path:.*}',
144 repo_route=True)
148 repo_route=True)
145
149
146 config.add_route(
150 config.add_route(
147 name='repo_file_history',
151 name='repo_file_history',
148 pattern='/{repo_name:.*?[^/]}/history/{commit_id}/{f_path:.*}', repo_route=True)
152 pattern='/{repo_name:.*?[^/]}/history/{commit_id}/{f_path:.*}', repo_route=True)
149
153
150 config.add_route(
154 config.add_route(
151 name='repo_file_authors',
155 name='repo_file_authors',
152 pattern='/{repo_name:.*?[^/]}/authors/{commit_id}/{f_path:.*}', repo_route=True)
156 pattern='/{repo_name:.*?[^/]}/authors/{commit_id}/{f_path:.*}', repo_route=True)
153
157
154 config.add_route(
158 config.add_route(
155 name='repo_files_remove_file',
159 name='repo_files_remove_file',
156 pattern='/{repo_name:.*?[^/]}/remove_file/{commit_id}/{f_path:.*}',
160 pattern='/{repo_name:.*?[^/]}/remove_file/{commit_id}/{f_path:.*}',
157 repo_route=True)
161 repo_route=True)
158 config.add_route(
162 config.add_route(
159 name='repo_files_delete_file',
163 name='repo_files_delete_file',
160 pattern='/{repo_name:.*?[^/]}/delete_file/{commit_id}/{f_path:.*}',
164 pattern='/{repo_name:.*?[^/]}/delete_file/{commit_id}/{f_path:.*}',
161 repo_route=True)
165 repo_route=True)
162 config.add_route(
166 config.add_route(
163 name='repo_files_edit_file',
167 name='repo_files_edit_file',
164 pattern='/{repo_name:.*?[^/]}/edit_file/{commit_id}/{f_path:.*}',
168 pattern='/{repo_name:.*?[^/]}/edit_file/{commit_id}/{f_path:.*}',
165 repo_route=True)
169 repo_route=True)
166 config.add_route(
170 config.add_route(
167 name='repo_files_update_file',
171 name='repo_files_update_file',
168 pattern='/{repo_name:.*?[^/]}/update_file/{commit_id}/{f_path:.*}',
172 pattern='/{repo_name:.*?[^/]}/update_file/{commit_id}/{f_path:.*}',
169 repo_route=True)
173 repo_route=True)
170 config.add_route(
174 config.add_route(
171 name='repo_files_add_file',
175 name='repo_files_add_file',
172 pattern='/{repo_name:.*?[^/]}/add_file/{commit_id}/{f_path:.*}',
176 pattern='/{repo_name:.*?[^/]}/add_file/{commit_id}/{f_path:.*}',
173 repo_route=True)
177 repo_route=True)
174 config.add_route(
178 config.add_route(
175 name='repo_files_upload_file',
179 name='repo_files_upload_file',
176 pattern='/{repo_name:.*?[^/]}/upload_file/{commit_id}/{f_path:.*}',
180 pattern='/{repo_name:.*?[^/]}/upload_file/{commit_id}/{f_path:.*}',
177 repo_route=True)
181 repo_route=True)
178 config.add_route(
182 config.add_route(
179 name='repo_files_create_file',
183 name='repo_files_create_file',
180 pattern='/{repo_name:.*?[^/]}/create_file/{commit_id}/{f_path:.*}',
184 pattern='/{repo_name:.*?[^/]}/create_file/{commit_id}/{f_path:.*}',
181 repo_route=True)
185 repo_route=True)
182
186
183 # Refs data
187 # Refs data
184 config.add_route(
188 config.add_route(
185 name='repo_refs_data',
189 name='repo_refs_data',
186 pattern='/{repo_name:.*?[^/]}/refs-data', repo_route=True)
190 pattern='/{repo_name:.*?[^/]}/refs-data', repo_route=True)
187
191
188 config.add_route(
192 config.add_route(
189 name='repo_refs_changelog_data',
193 name='repo_refs_changelog_data',
190 pattern='/{repo_name:.*?[^/]}/refs-data-changelog', repo_route=True)
194 pattern='/{repo_name:.*?[^/]}/refs-data-changelog', repo_route=True)
191
195
192 config.add_route(
196 config.add_route(
193 name='repo_stats',
197 name='repo_stats',
194 pattern='/{repo_name:.*?[^/]}/repo_stats/{commit_id}', repo_route=True)
198 pattern='/{repo_name:.*?[^/]}/repo_stats/{commit_id}', repo_route=True)
195
199
196 # Commits
200 # Commits
197 config.add_route(
201 config.add_route(
198 name='repo_commits',
202 name='repo_commits',
199 pattern='/{repo_name:.*?[^/]}/commits', repo_route=True)
203 pattern='/{repo_name:.*?[^/]}/commits', repo_route=True)
200 config.add_route(
204 config.add_route(
201 name='repo_commits_file',
205 name='repo_commits_file',
202 pattern='/{repo_name:.*?[^/]}/commits/{commit_id}/{f_path:.*}', repo_route=True)
206 pattern='/{repo_name:.*?[^/]}/commits/{commit_id}/{f_path:.*}', repo_route=True)
203 config.add_route(
207 config.add_route(
204 name='repo_commits_elements',
208 name='repo_commits_elements',
205 pattern='/{repo_name:.*?[^/]}/commits_elements', repo_route=True)
209 pattern='/{repo_name:.*?[^/]}/commits_elements', repo_route=True)
206 config.add_route(
210 config.add_route(
207 name='repo_commits_elements_file',
211 name='repo_commits_elements_file',
208 pattern='/{repo_name:.*?[^/]}/commits_elements/{commit_id}/{f_path:.*}', repo_route=True)
212 pattern='/{repo_name:.*?[^/]}/commits_elements/{commit_id}/{f_path:.*}', repo_route=True)
209
213
210 # Changelog (old deprecated name for commits page)
214 # Changelog (old deprecated name for commits page)
211 config.add_route(
215 config.add_route(
212 name='repo_changelog',
216 name='repo_changelog',
213 pattern='/{repo_name:.*?[^/]}/changelog', repo_route=True)
217 pattern='/{repo_name:.*?[^/]}/changelog', repo_route=True)
214 config.add_route(
218 config.add_route(
215 name='repo_changelog_file',
219 name='repo_changelog_file',
216 pattern='/{repo_name:.*?[^/]}/changelog/{commit_id}/{f_path:.*}', repo_route=True)
220 pattern='/{repo_name:.*?[^/]}/changelog/{commit_id}/{f_path:.*}', repo_route=True)
217
221
218 # Compare
222 # Compare
219 config.add_route(
223 config.add_route(
220 name='repo_compare_select',
224 name='repo_compare_select',
221 pattern='/{repo_name:.*?[^/]}/compare', repo_route=True)
225 pattern='/{repo_name:.*?[^/]}/compare', repo_route=True)
222
226
223 config.add_route(
227 config.add_route(
224 name='repo_compare',
228 name='repo_compare',
225 pattern='/{repo_name:.*?[^/]}/compare/{source_ref_type}@{source_ref:.*?}...{target_ref_type}@{target_ref:.*?}', repo_route=True)
229 pattern='/{repo_name:.*?[^/]}/compare/{source_ref_type}@{source_ref:.*?}...{target_ref_type}@{target_ref:.*?}', repo_route=True)
226
230
227 # Tags
231 # Tags
228 config.add_route(
232 config.add_route(
229 name='tags_home',
233 name='tags_home',
230 pattern='/{repo_name:.*?[^/]}/tags', repo_route=True)
234 pattern='/{repo_name:.*?[^/]}/tags', repo_route=True)
231
235
232 # Branches
236 # Branches
233 config.add_route(
237 config.add_route(
234 name='branches_home',
238 name='branches_home',
235 pattern='/{repo_name:.*?[^/]}/branches', repo_route=True)
239 pattern='/{repo_name:.*?[^/]}/branches', repo_route=True)
236
240
237 # Bookmarks
241 # Bookmarks
238 config.add_route(
242 config.add_route(
239 name='bookmarks_home',
243 name='bookmarks_home',
240 pattern='/{repo_name:.*?[^/]}/bookmarks', repo_route=True)
244 pattern='/{repo_name:.*?[^/]}/bookmarks', repo_route=True)
241
245
242 # Forks
246 # Forks
243 config.add_route(
247 config.add_route(
244 name='repo_fork_new',
248 name='repo_fork_new',
245 pattern='/{repo_name:.*?[^/]}/fork', repo_route=True,
249 pattern='/{repo_name:.*?[^/]}/fork', repo_route=True,
246 repo_forbid_when_archived=True,
250 repo_forbid_when_archived=True,
247 repo_accepted_types=['hg', 'git'])
251 repo_accepted_types=['hg', 'git'])
248
252
249 config.add_route(
253 config.add_route(
250 name='repo_fork_create',
254 name='repo_fork_create',
251 pattern='/{repo_name:.*?[^/]}/fork/create', repo_route=True,
255 pattern='/{repo_name:.*?[^/]}/fork/create', repo_route=True,
252 repo_forbid_when_archived=True,
256 repo_forbid_when_archived=True,
253 repo_accepted_types=['hg', 'git'])
257 repo_accepted_types=['hg', 'git'])
254
258
255 config.add_route(
259 config.add_route(
256 name='repo_forks_show_all',
260 name='repo_forks_show_all',
257 pattern='/{repo_name:.*?[^/]}/forks', repo_route=True,
261 pattern='/{repo_name:.*?[^/]}/forks', repo_route=True,
258 repo_accepted_types=['hg', 'git'])
262 repo_accepted_types=['hg', 'git'])
259 config.add_route(
263 config.add_route(
260 name='repo_forks_data',
264 name='repo_forks_data',
261 pattern='/{repo_name:.*?[^/]}/forks/data', repo_route=True,
265 pattern='/{repo_name:.*?[^/]}/forks/data', repo_route=True,
262 repo_accepted_types=['hg', 'git'])
266 repo_accepted_types=['hg', 'git'])
263
267
264 # Pull Requests
268 # Pull Requests
265 config.add_route(
269 config.add_route(
266 name='pullrequest_show',
270 name='pullrequest_show',
267 pattern='/{repo_name:.*?[^/]}/pull-request/{pull_request_id:\d+}',
271 pattern='/{repo_name:.*?[^/]}/pull-request/{pull_request_id:\d+}',
268 repo_route=True)
272 repo_route=True)
269
273
270 config.add_route(
274 config.add_route(
271 name='pullrequest_show_all',
275 name='pullrequest_show_all',
272 pattern='/{repo_name:.*?[^/]}/pull-request',
276 pattern='/{repo_name:.*?[^/]}/pull-request',
273 repo_route=True, repo_accepted_types=['hg', 'git'])
277 repo_route=True, repo_accepted_types=['hg', 'git'])
274
278
275 config.add_route(
279 config.add_route(
276 name='pullrequest_show_all_data',
280 name='pullrequest_show_all_data',
277 pattern='/{repo_name:.*?[^/]}/pull-request-data',
281 pattern='/{repo_name:.*?[^/]}/pull-request-data',
278 repo_route=True, repo_accepted_types=['hg', 'git'])
282 repo_route=True, repo_accepted_types=['hg', 'git'])
279
283
280 config.add_route(
284 config.add_route(
281 name='pullrequest_repo_refs',
285 name='pullrequest_repo_refs',
282 pattern='/{repo_name:.*?[^/]}/pull-request/refs/{target_repo_name:.*?[^/]}',
286 pattern='/{repo_name:.*?[^/]}/pull-request/refs/{target_repo_name:.*?[^/]}',
283 repo_route=True)
287 repo_route=True)
284
288
285 config.add_route(
289 config.add_route(
286 name='pullrequest_repo_targets',
290 name='pullrequest_repo_targets',
287 pattern='/{repo_name:.*?[^/]}/pull-request/repo-targets',
291 pattern='/{repo_name:.*?[^/]}/pull-request/repo-targets',
288 repo_route=True)
292 repo_route=True)
289
293
290 config.add_route(
294 config.add_route(
291 name='pullrequest_new',
295 name='pullrequest_new',
292 pattern='/{repo_name:.*?[^/]}/pull-request/new',
296 pattern='/{repo_name:.*?[^/]}/pull-request/new',
293 repo_route=True, repo_accepted_types=['hg', 'git'],
297 repo_route=True, repo_accepted_types=['hg', 'git'],
294 repo_forbid_when_archived=True)
298 repo_forbid_when_archived=True)
295
299
296 config.add_route(
300 config.add_route(
297 name='pullrequest_create',
301 name='pullrequest_create',
298 pattern='/{repo_name:.*?[^/]}/pull-request/create',
302 pattern='/{repo_name:.*?[^/]}/pull-request/create',
299 repo_route=True, repo_accepted_types=['hg', 'git'],
303 repo_route=True, repo_accepted_types=['hg', 'git'],
300 repo_forbid_when_archived=True)
304 repo_forbid_when_archived=True)
301
305
302 config.add_route(
306 config.add_route(
303 name='pullrequest_update',
307 name='pullrequest_update',
304 pattern='/{repo_name:.*?[^/]}/pull-request/{pull_request_id:\d+}/update',
308 pattern='/{repo_name:.*?[^/]}/pull-request/{pull_request_id:\d+}/update',
305 repo_route=True, repo_forbid_when_archived=True)
309 repo_route=True, repo_forbid_when_archived=True)
306
310
307 config.add_route(
311 config.add_route(
308 name='pullrequest_merge',
312 name='pullrequest_merge',
309 pattern='/{repo_name:.*?[^/]}/pull-request/{pull_request_id:\d+}/merge',
313 pattern='/{repo_name:.*?[^/]}/pull-request/{pull_request_id:\d+}/merge',
310 repo_route=True, repo_forbid_when_archived=True)
314 repo_route=True, repo_forbid_when_archived=True)
311
315
312 config.add_route(
316 config.add_route(
313 name='pullrequest_delete',
317 name='pullrequest_delete',
314 pattern='/{repo_name:.*?[^/]}/pull-request/{pull_request_id:\d+}/delete',
318 pattern='/{repo_name:.*?[^/]}/pull-request/{pull_request_id:\d+}/delete',
315 repo_route=True, repo_forbid_when_archived=True)
319 repo_route=True, repo_forbid_when_archived=True)
316
320
317 config.add_route(
321 config.add_route(
318 name='pullrequest_comment_create',
322 name='pullrequest_comment_create',
319 pattern='/{repo_name:.*?[^/]}/pull-request/{pull_request_id:\d+}/comment',
323 pattern='/{repo_name:.*?[^/]}/pull-request/{pull_request_id:\d+}/comment',
320 repo_route=True)
324 repo_route=True)
321
325
322 config.add_route(
326 config.add_route(
323 name='pullrequest_comment_delete',
327 name='pullrequest_comment_delete',
324 pattern='/{repo_name:.*?[^/]}/pull-request/{pull_request_id:\d+}/comment/{comment_id}/delete',
328 pattern='/{repo_name:.*?[^/]}/pull-request/{pull_request_id:\d+}/comment/{comment_id}/delete',
325 repo_route=True, repo_accepted_types=['hg', 'git'])
329 repo_route=True, repo_accepted_types=['hg', 'git'])
326
330
327 # Artifacts, (EE feature)
331 # Artifacts, (EE feature)
328 config.add_route(
332 config.add_route(
329 name='repo_artifacts_list',
333 name='repo_artifacts_list',
330 pattern='/{repo_name:.*?[^/]}/artifacts', repo_route=True)
334 pattern='/{repo_name:.*?[^/]}/artifacts', repo_route=True)
331
335
332 # Settings
336 # Settings
333 config.add_route(
337 config.add_route(
334 name='edit_repo',
338 name='edit_repo',
335 pattern='/{repo_name:.*?[^/]}/settings', repo_route=True)
339 pattern='/{repo_name:.*?[^/]}/settings', repo_route=True)
336 # update is POST on edit_repo
340 # update is POST on edit_repo
337
341
338 # Settings advanced
342 # Settings advanced
339 config.add_route(
343 config.add_route(
340 name='edit_repo_advanced',
344 name='edit_repo_advanced',
341 pattern='/{repo_name:.*?[^/]}/settings/advanced', repo_route=True)
345 pattern='/{repo_name:.*?[^/]}/settings/advanced', repo_route=True)
342 config.add_route(
346 config.add_route(
343 name='edit_repo_advanced_archive',
347 name='edit_repo_advanced_archive',
344 pattern='/{repo_name:.*?[^/]}/settings/advanced/archive', repo_route=True)
348 pattern='/{repo_name:.*?[^/]}/settings/advanced/archive', repo_route=True)
345 config.add_route(
349 config.add_route(
346 name='edit_repo_advanced_delete',
350 name='edit_repo_advanced_delete',
347 pattern='/{repo_name:.*?[^/]}/settings/advanced/delete', repo_route=True)
351 pattern='/{repo_name:.*?[^/]}/settings/advanced/delete', repo_route=True)
348 config.add_route(
352 config.add_route(
349 name='edit_repo_advanced_locking',
353 name='edit_repo_advanced_locking',
350 pattern='/{repo_name:.*?[^/]}/settings/advanced/locking', repo_route=True)
354 pattern='/{repo_name:.*?[^/]}/settings/advanced/locking', repo_route=True)
351 config.add_route(
355 config.add_route(
352 name='edit_repo_advanced_journal',
356 name='edit_repo_advanced_journal',
353 pattern='/{repo_name:.*?[^/]}/settings/advanced/journal', repo_route=True)
357 pattern='/{repo_name:.*?[^/]}/settings/advanced/journal', repo_route=True)
354 config.add_route(
358 config.add_route(
355 name='edit_repo_advanced_fork',
359 name='edit_repo_advanced_fork',
356 pattern='/{repo_name:.*?[^/]}/settings/advanced/fork', repo_route=True)
360 pattern='/{repo_name:.*?[^/]}/settings/advanced/fork', repo_route=True)
357
361
358 config.add_route(
362 config.add_route(
359 name='edit_repo_advanced_hooks',
363 name='edit_repo_advanced_hooks',
360 pattern='/{repo_name:.*?[^/]}/settings/advanced/hooks', repo_route=True)
364 pattern='/{repo_name:.*?[^/]}/settings/advanced/hooks', repo_route=True)
361
365
362 # Caches
366 # Caches
363 config.add_route(
367 config.add_route(
364 name='edit_repo_caches',
368 name='edit_repo_caches',
365 pattern='/{repo_name:.*?[^/]}/settings/caches', repo_route=True)
369 pattern='/{repo_name:.*?[^/]}/settings/caches', repo_route=True)
366
370
367 # Permissions
371 # Permissions
368 config.add_route(
372 config.add_route(
369 name='edit_repo_perms',
373 name='edit_repo_perms',
370 pattern='/{repo_name:.*?[^/]}/settings/permissions', repo_route=True)
374 pattern='/{repo_name:.*?[^/]}/settings/permissions', repo_route=True)
371
375
372 config.add_route(
376 config.add_route(
373 name='edit_repo_perms_set_private',
377 name='edit_repo_perms_set_private',
374 pattern='/{repo_name:.*?[^/]}/settings/permissions/set_private', repo_route=True)
378 pattern='/{repo_name:.*?[^/]}/settings/permissions/set_private', repo_route=True)
375
379
376 # Permissions Branch (EE feature)
380 # Permissions Branch (EE feature)
377 config.add_route(
381 config.add_route(
378 name='edit_repo_perms_branch',
382 name='edit_repo_perms_branch',
379 pattern='/{repo_name:.*?[^/]}/settings/branch_permissions', repo_route=True)
383 pattern='/{repo_name:.*?[^/]}/settings/branch_permissions', repo_route=True)
380 config.add_route(
384 config.add_route(
381 name='edit_repo_perms_branch_delete',
385 name='edit_repo_perms_branch_delete',
382 pattern='/{repo_name:.*?[^/]}/settings/branch_permissions/{rule_id}/delete',
386 pattern='/{repo_name:.*?[^/]}/settings/branch_permissions/{rule_id}/delete',
383 repo_route=True)
387 repo_route=True)
384
388
385 # Maintenance
389 # Maintenance
386 config.add_route(
390 config.add_route(
387 name='edit_repo_maintenance',
391 name='edit_repo_maintenance',
388 pattern='/{repo_name:.*?[^/]}/settings/maintenance', repo_route=True)
392 pattern='/{repo_name:.*?[^/]}/settings/maintenance', repo_route=True)
389
393
390 config.add_route(
394 config.add_route(
391 name='edit_repo_maintenance_execute',
395 name='edit_repo_maintenance_execute',
392 pattern='/{repo_name:.*?[^/]}/settings/maintenance/execute', repo_route=True)
396 pattern='/{repo_name:.*?[^/]}/settings/maintenance/execute', repo_route=True)
393
397
394 # Fields
398 # Fields
395 config.add_route(
399 config.add_route(
396 name='edit_repo_fields',
400 name='edit_repo_fields',
397 pattern='/{repo_name:.*?[^/]}/settings/fields', repo_route=True)
401 pattern='/{repo_name:.*?[^/]}/settings/fields', repo_route=True)
398 config.add_route(
402 config.add_route(
399 name='edit_repo_fields_create',
403 name='edit_repo_fields_create',
400 pattern='/{repo_name:.*?[^/]}/settings/fields/create', repo_route=True)
404 pattern='/{repo_name:.*?[^/]}/settings/fields/create', repo_route=True)
401 config.add_route(
405 config.add_route(
402 name='edit_repo_fields_delete',
406 name='edit_repo_fields_delete',
403 pattern='/{repo_name:.*?[^/]}/settings/fields/{field_id}/delete', repo_route=True)
407 pattern='/{repo_name:.*?[^/]}/settings/fields/{field_id}/delete', repo_route=True)
404
408
405 # Locking
409 # Locking
406 config.add_route(
410 config.add_route(
407 name='repo_edit_toggle_locking',
411 name='repo_edit_toggle_locking',
408 pattern='/{repo_name:.*?[^/]}/settings/toggle_locking', repo_route=True)
412 pattern='/{repo_name:.*?[^/]}/settings/toggle_locking', repo_route=True)
409
413
410 # Remote
414 # Remote
411 config.add_route(
415 config.add_route(
412 name='edit_repo_remote',
416 name='edit_repo_remote',
413 pattern='/{repo_name:.*?[^/]}/settings/remote', repo_route=True)
417 pattern='/{repo_name:.*?[^/]}/settings/remote', repo_route=True)
414 config.add_route(
418 config.add_route(
415 name='edit_repo_remote_pull',
419 name='edit_repo_remote_pull',
416 pattern='/{repo_name:.*?[^/]}/settings/remote/pull', repo_route=True)
420 pattern='/{repo_name:.*?[^/]}/settings/remote/pull', repo_route=True)
417 config.add_route(
421 config.add_route(
418 name='edit_repo_remote_push',
422 name='edit_repo_remote_push',
419 pattern='/{repo_name:.*?[^/]}/settings/remote/push', repo_route=True)
423 pattern='/{repo_name:.*?[^/]}/settings/remote/push', repo_route=True)
420
424
421 # Statistics
425 # Statistics
422 config.add_route(
426 config.add_route(
423 name='edit_repo_statistics',
427 name='edit_repo_statistics',
424 pattern='/{repo_name:.*?[^/]}/settings/statistics', repo_route=True)
428 pattern='/{repo_name:.*?[^/]}/settings/statistics', repo_route=True)
425 config.add_route(
429 config.add_route(
426 name='edit_repo_statistics_reset',
430 name='edit_repo_statistics_reset',
427 pattern='/{repo_name:.*?[^/]}/settings/statistics/update', repo_route=True)
431 pattern='/{repo_name:.*?[^/]}/settings/statistics/update', repo_route=True)
428
432
429 # Issue trackers
433 # Issue trackers
430 config.add_route(
434 config.add_route(
431 name='edit_repo_issuetracker',
435 name='edit_repo_issuetracker',
432 pattern='/{repo_name:.*?[^/]}/settings/issue_trackers', repo_route=True)
436 pattern='/{repo_name:.*?[^/]}/settings/issue_trackers', repo_route=True)
433 config.add_route(
437 config.add_route(
434 name='edit_repo_issuetracker_test',
438 name='edit_repo_issuetracker_test',
435 pattern='/{repo_name:.*?[^/]}/settings/issue_trackers/test', repo_route=True)
439 pattern='/{repo_name:.*?[^/]}/settings/issue_trackers/test', repo_route=True)
436 config.add_route(
440 config.add_route(
437 name='edit_repo_issuetracker_delete',
441 name='edit_repo_issuetracker_delete',
438 pattern='/{repo_name:.*?[^/]}/settings/issue_trackers/delete', repo_route=True)
442 pattern='/{repo_name:.*?[^/]}/settings/issue_trackers/delete', repo_route=True)
439 config.add_route(
443 config.add_route(
440 name='edit_repo_issuetracker_update',
444 name='edit_repo_issuetracker_update',
441 pattern='/{repo_name:.*?[^/]}/settings/issue_trackers/update', repo_route=True)
445 pattern='/{repo_name:.*?[^/]}/settings/issue_trackers/update', repo_route=True)
442
446
443 # VCS Settings
447 # VCS Settings
444 config.add_route(
448 config.add_route(
445 name='edit_repo_vcs',
449 name='edit_repo_vcs',
446 pattern='/{repo_name:.*?[^/]}/settings/vcs', repo_route=True)
450 pattern='/{repo_name:.*?[^/]}/settings/vcs', repo_route=True)
447 config.add_route(
451 config.add_route(
448 name='edit_repo_vcs_update',
452 name='edit_repo_vcs_update',
449 pattern='/{repo_name:.*?[^/]}/settings/vcs/update', repo_route=True)
453 pattern='/{repo_name:.*?[^/]}/settings/vcs/update', repo_route=True)
450
454
451 # svn pattern
455 # svn pattern
452 config.add_route(
456 config.add_route(
453 name='edit_repo_vcs_svn_pattern_delete',
457 name='edit_repo_vcs_svn_pattern_delete',
454 pattern='/{repo_name:.*?[^/]}/settings/vcs/svn_pattern/delete', repo_route=True)
458 pattern='/{repo_name:.*?[^/]}/settings/vcs/svn_pattern/delete', repo_route=True)
455
459
456 # Repo Review Rules (EE feature)
460 # Repo Review Rules (EE feature)
457 config.add_route(
461 config.add_route(
458 name='repo_reviewers',
462 name='repo_reviewers',
459 pattern='/{repo_name:.*?[^/]}/settings/review/rules', repo_route=True)
463 pattern='/{repo_name:.*?[^/]}/settings/review/rules', repo_route=True)
460
464
461 config.add_route(
465 config.add_route(
462 name='repo_default_reviewers_data',
466 name='repo_default_reviewers_data',
463 pattern='/{repo_name:.*?[^/]}/settings/review/default-reviewers', repo_route=True)
467 pattern='/{repo_name:.*?[^/]}/settings/review/default-reviewers', repo_route=True)
464
468
465 # Repo Automation (EE feature)
469 # Repo Automation (EE feature)
466 config.add_route(
470 config.add_route(
467 name='repo_automation',
471 name='repo_automation',
468 pattern='/{repo_name:.*?[^/]}/settings/automation', repo_route=True)
472 pattern='/{repo_name:.*?[^/]}/settings/automation', repo_route=True)
469
473
470 # Strip
474 # Strip
471 config.add_route(
475 config.add_route(
472 name='edit_repo_strip',
476 name='edit_repo_strip',
473 pattern='/{repo_name:.*?[^/]}/settings/strip', repo_route=True)
477 pattern='/{repo_name:.*?[^/]}/settings/strip', repo_route=True)
474
478
475 config.add_route(
479 config.add_route(
476 name='strip_check',
480 name='strip_check',
477 pattern='/{repo_name:.*?[^/]}/settings/strip_check', repo_route=True)
481 pattern='/{repo_name:.*?[^/]}/settings/strip_check', repo_route=True)
478
482
479 config.add_route(
483 config.add_route(
480 name='strip_execute',
484 name='strip_execute',
481 pattern='/{repo_name:.*?[^/]}/settings/strip_execute', repo_route=True)
485 pattern='/{repo_name:.*?[^/]}/settings/strip_execute', repo_route=True)
482
486
483 # Audit logs
487 # Audit logs
484 config.add_route(
488 config.add_route(
485 name='edit_repo_audit_logs',
489 name='edit_repo_audit_logs',
486 pattern='/{repo_name:.*?[^/]}/settings/audit_logs', repo_route=True)
490 pattern='/{repo_name:.*?[^/]}/settings/audit_logs', repo_route=True)
487
491
488 # ATOM/RSS Feed, shouldn't contain slashes for outlook compatibility
492 # ATOM/RSS Feed, shouldn't contain slashes for outlook compatibility
489 config.add_route(
493 config.add_route(
490 name='rss_feed_home',
494 name='rss_feed_home',
491 pattern='/{repo_name:.*?[^/]}/feed-rss', repo_route=True)
495 pattern='/{repo_name:.*?[^/]}/feed-rss', repo_route=True)
492
496
493 config.add_route(
497 config.add_route(
494 name='atom_feed_home',
498 name='atom_feed_home',
495 pattern='/{repo_name:.*?[^/]}/feed-atom', repo_route=True)
499 pattern='/{repo_name:.*?[^/]}/feed-atom', repo_route=True)
496
500
497 config.add_route(
501 config.add_route(
498 name='rss_feed_home_old',
502 name='rss_feed_home_old',
499 pattern='/{repo_name:.*?[^/]}/feed/rss', repo_route=True)
503 pattern='/{repo_name:.*?[^/]}/feed/rss', repo_route=True)
500
504
501 config.add_route(
505 config.add_route(
502 name='atom_feed_home_old',
506 name='atom_feed_home_old',
503 pattern='/{repo_name:.*?[^/]}/feed/atom', repo_route=True)
507 pattern='/{repo_name:.*?[^/]}/feed/atom', repo_route=True)
504
508
505 # NOTE(marcink): needs to be at the end for catch-all
509 # NOTE(marcink): needs to be at the end for catch-all
506 add_route_with_slash(
510 add_route_with_slash(
507 config,
511 config,
508 name='repo_summary',
512 name='repo_summary',
509 pattern='/{repo_name:.*?[^/]}', repo_route=True)
513 pattern='/{repo_name:.*?[^/]}', repo_route=True)
510
514
511 # Scan module for configuration decorators.
515 # Scan module for configuration decorators.
512 config.scan('.views', ignore='.tests')
516 config.scan('.views', ignore='.tests')
@@ -1,503 +1,600 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2019 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 import logging
22 import logging
23 import collections
23 import collections
24
24
25 from pyramid.httpexceptions import HTTPNotFound, HTTPBadRequest, HTTPFound
25 from pyramid.httpexceptions import HTTPNotFound, HTTPBadRequest, HTTPFound
26 from pyramid.view import view_config
26 from pyramid.view import view_config
27 from pyramid.renderers import render
27 from pyramid.renderers import render
28 from pyramid.response import Response
28 from pyramid.response import Response
29
29
30 from rhodecode.apps._base import RepoAppView
30 from rhodecode.apps._base import RepoAppView
31 from rhodecode.apps.file_store import utils as store_utils
32 from rhodecode.apps.file_store.exceptions import FileNotAllowedException, FileOverSizeException
31
33
32 from rhodecode.lib import diffs, codeblocks
34 from rhodecode.lib import diffs, codeblocks
33 from rhodecode.lib.auth import (
35 from rhodecode.lib.auth import (
34 LoginRequired, HasRepoPermissionAnyDecorator, NotAnonymous, CSRFRequired)
36 LoginRequired, HasRepoPermissionAnyDecorator, NotAnonymous, CSRFRequired)
35
37
36 from rhodecode.lib.compat import OrderedDict
38 from rhodecode.lib.compat import OrderedDict
37 from rhodecode.lib.diffs import (
39 from rhodecode.lib.diffs import (
38 cache_diff, load_cached_diff, diff_cache_exist, get_diff_context,
40 cache_diff, load_cached_diff, diff_cache_exist, get_diff_context,
39 get_diff_whitespace_flag)
41 get_diff_whitespace_flag)
40 from rhodecode.lib.exceptions import StatusChangeOnClosedPullRequestError
42 from rhodecode.lib.exceptions import StatusChangeOnClosedPullRequestError
41 import rhodecode.lib.helpers as h
43 import rhodecode.lib.helpers as h
42 from rhodecode.lib.utils2 import safe_unicode, str2bool
44 from rhodecode.lib.utils2 import safe_unicode, str2bool
43 from rhodecode.lib.vcs.backends.base import EmptyCommit
45 from rhodecode.lib.vcs.backends.base import EmptyCommit
44 from rhodecode.lib.vcs.exceptions import (
46 from rhodecode.lib.vcs.exceptions import (
45 RepositoryError, CommitDoesNotExistError)
47 RepositoryError, CommitDoesNotExistError)
46 from rhodecode.model.db import ChangesetComment, ChangesetStatus
48 from rhodecode.model.db import ChangesetComment, ChangesetStatus, FileStore
47 from rhodecode.model.changeset_status import ChangesetStatusModel
49 from rhodecode.model.changeset_status import ChangesetStatusModel
48 from rhodecode.model.comment import CommentsModel
50 from rhodecode.model.comment import CommentsModel
49 from rhodecode.model.meta import Session
51 from rhodecode.model.meta import Session
50 from rhodecode.model.settings import VcsSettingsModel
52 from rhodecode.model.settings import VcsSettingsModel
51
53
52 log = logging.getLogger(__name__)
54 log = logging.getLogger(__name__)
53
55
54
56
55 def _update_with_GET(params, request):
57 def _update_with_GET(params, request):
56 for k in ['diff1', 'diff2', 'diff']:
58 for k in ['diff1', 'diff2', 'diff']:
57 params[k] += request.GET.getall(k)
59 params[k] += request.GET.getall(k)
58
60
59
61
60 class RepoCommitsView(RepoAppView):
62 class RepoCommitsView(RepoAppView):
61 def load_default_context(self):
63 def load_default_context(self):
62 c = self._get_local_tmpl_context(include_app_defaults=True)
64 c = self._get_local_tmpl_context(include_app_defaults=True)
63 c.rhodecode_repo = self.rhodecode_vcs_repo
65 c.rhodecode_repo = self.rhodecode_vcs_repo
64
66
65 return c
67 return c
66
68
67 def _is_diff_cache_enabled(self, target_repo):
69 def _is_diff_cache_enabled(self, target_repo):
68 caching_enabled = self._get_general_setting(
70 caching_enabled = self._get_general_setting(
69 target_repo, 'rhodecode_diff_cache')
71 target_repo, 'rhodecode_diff_cache')
70 log.debug('Diff caching enabled: %s', caching_enabled)
72 log.debug('Diff caching enabled: %s', caching_enabled)
71 return caching_enabled
73 return caching_enabled
72
74
73 def _commit(self, commit_id_range, method):
75 def _commit(self, commit_id_range, method):
74 _ = self.request.translate
76 _ = self.request.translate
75 c = self.load_default_context()
77 c = self.load_default_context()
76 c.fulldiff = self.request.GET.get('fulldiff')
78 c.fulldiff = self.request.GET.get('fulldiff')
77
79
78 # fetch global flags of ignore ws or context lines
80 # fetch global flags of ignore ws or context lines
79 diff_context = get_diff_context(self.request)
81 diff_context = get_diff_context(self.request)
80 hide_whitespace_changes = get_diff_whitespace_flag(self.request)
82 hide_whitespace_changes = get_diff_whitespace_flag(self.request)
81
83
82 # diff_limit will cut off the whole diff if the limit is applied
84 # diff_limit will cut off the whole diff if the limit is applied
83 # otherwise it will just hide the big files from the front-end
85 # otherwise it will just hide the big files from the front-end
84 diff_limit = c.visual.cut_off_limit_diff
86 diff_limit = c.visual.cut_off_limit_diff
85 file_limit = c.visual.cut_off_limit_file
87 file_limit = c.visual.cut_off_limit_file
86
88
87 # get ranges of commit ids if preset
89 # get ranges of commit ids if preset
88 commit_range = commit_id_range.split('...')[:2]
90 commit_range = commit_id_range.split('...')[:2]
89
91
90 try:
92 try:
91 pre_load = ['affected_files', 'author', 'branch', 'date',
93 pre_load = ['affected_files', 'author', 'branch', 'date',
92 'message', 'parents']
94 'message', 'parents']
93 if self.rhodecode_vcs_repo.alias == 'hg':
95 if self.rhodecode_vcs_repo.alias == 'hg':
94 pre_load += ['hidden', 'obsolete', 'phase']
96 pre_load += ['hidden', 'obsolete', 'phase']
95
97
96 if len(commit_range) == 2:
98 if len(commit_range) == 2:
97 commits = self.rhodecode_vcs_repo.get_commits(
99 commits = self.rhodecode_vcs_repo.get_commits(
98 start_id=commit_range[0], end_id=commit_range[1],
100 start_id=commit_range[0], end_id=commit_range[1],
99 pre_load=pre_load, translate_tags=False)
101 pre_load=pre_load, translate_tags=False)
100 commits = list(commits)
102 commits = list(commits)
101 else:
103 else:
102 commits = [self.rhodecode_vcs_repo.get_commit(
104 commits = [self.rhodecode_vcs_repo.get_commit(
103 commit_id=commit_id_range, pre_load=pre_load)]
105 commit_id=commit_id_range, pre_load=pre_load)]
104
106
105 c.commit_ranges = commits
107 c.commit_ranges = commits
106 if not c.commit_ranges:
108 if not c.commit_ranges:
107 raise RepositoryError('The commit range returned an empty result')
109 raise RepositoryError('The commit range returned an empty result')
108 except CommitDoesNotExistError as e:
110 except CommitDoesNotExistError as e:
109 msg = _('No such commit exists. Org exception: `{}`').format(e)
111 msg = _('No such commit exists. Org exception: `{}`').format(e)
110 h.flash(msg, category='error')
112 h.flash(msg, category='error')
111 raise HTTPNotFound()
113 raise HTTPNotFound()
112 except Exception:
114 except Exception:
113 log.exception("General failure")
115 log.exception("General failure")
114 raise HTTPNotFound()
116 raise HTTPNotFound()
115
117
116 c.changes = OrderedDict()
118 c.changes = OrderedDict()
117 c.lines_added = 0
119 c.lines_added = 0
118 c.lines_deleted = 0
120 c.lines_deleted = 0
119
121
120 # auto collapse if we have more than limit
122 # auto collapse if we have more than limit
121 collapse_limit = diffs.DiffProcessor._collapse_commits_over
123 collapse_limit = diffs.DiffProcessor._collapse_commits_over
122 c.collapse_all_commits = len(c.commit_ranges) > collapse_limit
124 c.collapse_all_commits = len(c.commit_ranges) > collapse_limit
123
125
124 c.commit_statuses = ChangesetStatus.STATUSES
126 c.commit_statuses = ChangesetStatus.STATUSES
125 c.inline_comments = []
127 c.inline_comments = []
126 c.files = []
128 c.files = []
127
129
128 c.statuses = []
130 c.statuses = []
129 c.comments = []
131 c.comments = []
130 c.unresolved_comments = []
132 c.unresolved_comments = []
131 c.resolved_comments = []
133 c.resolved_comments = []
132 if len(c.commit_ranges) == 1:
134 if len(c.commit_ranges) == 1:
133 commit = c.commit_ranges[0]
135 commit = c.commit_ranges[0]
134 c.comments = CommentsModel().get_comments(
136 c.comments = CommentsModel().get_comments(
135 self.db_repo.repo_id,
137 self.db_repo.repo_id,
136 revision=commit.raw_id)
138 revision=commit.raw_id)
137 c.statuses.append(ChangesetStatusModel().get_status(
139 c.statuses.append(ChangesetStatusModel().get_status(
138 self.db_repo.repo_id, commit.raw_id))
140 self.db_repo.repo_id, commit.raw_id))
139 # comments from PR
141 # comments from PR
140 statuses = ChangesetStatusModel().get_statuses(
142 statuses = ChangesetStatusModel().get_statuses(
141 self.db_repo.repo_id, commit.raw_id,
143 self.db_repo.repo_id, commit.raw_id,
142 with_revisions=True)
144 with_revisions=True)
143 prs = set(st.pull_request for st in statuses
145 prs = set(st.pull_request for st in statuses
144 if st.pull_request is not None)
146 if st.pull_request is not None)
145 # from associated statuses, check the pull requests, and
147 # from associated statuses, check the pull requests, and
146 # show comments from them
148 # show comments from them
147 for pr in prs:
149 for pr in prs:
148 c.comments.extend(pr.comments)
150 c.comments.extend(pr.comments)
149
151
150 c.unresolved_comments = CommentsModel()\
152 c.unresolved_comments = CommentsModel()\
151 .get_commit_unresolved_todos(commit.raw_id)
153 .get_commit_unresolved_todos(commit.raw_id)
152 c.resolved_comments = CommentsModel()\
154 c.resolved_comments = CommentsModel()\
153 .get_commit_resolved_todos(commit.raw_id)
155 .get_commit_resolved_todos(commit.raw_id)
154
156
155 diff = None
157 diff = None
156 # Iterate over ranges (default commit view is always one commit)
158 # Iterate over ranges (default commit view is always one commit)
157 for commit in c.commit_ranges:
159 for commit in c.commit_ranges:
158 c.changes[commit.raw_id] = []
160 c.changes[commit.raw_id] = []
159
161
160 commit2 = commit
162 commit2 = commit
161 commit1 = commit.first_parent
163 commit1 = commit.first_parent
162
164
163 if method == 'show':
165 if method == 'show':
164 inline_comments = CommentsModel().get_inline_comments(
166 inline_comments = CommentsModel().get_inline_comments(
165 self.db_repo.repo_id, revision=commit.raw_id)
167 self.db_repo.repo_id, revision=commit.raw_id)
166 c.inline_cnt = CommentsModel().get_inline_comments_count(
168 c.inline_cnt = CommentsModel().get_inline_comments_count(
167 inline_comments)
169 inline_comments)
168 c.inline_comments = inline_comments
170 c.inline_comments = inline_comments
169
171
170 cache_path = self.rhodecode_vcs_repo.get_create_shadow_cache_pr_path(
172 cache_path = self.rhodecode_vcs_repo.get_create_shadow_cache_pr_path(
171 self.db_repo)
173 self.db_repo)
172 cache_file_path = diff_cache_exist(
174 cache_file_path = diff_cache_exist(
173 cache_path, 'diff', commit.raw_id,
175 cache_path, 'diff', commit.raw_id,
174 hide_whitespace_changes, diff_context, c.fulldiff)
176 hide_whitespace_changes, diff_context, c.fulldiff)
175
177
176 caching_enabled = self._is_diff_cache_enabled(self.db_repo)
178 caching_enabled = self._is_diff_cache_enabled(self.db_repo)
177 force_recache = str2bool(self.request.GET.get('force_recache'))
179 force_recache = str2bool(self.request.GET.get('force_recache'))
178
180
179 cached_diff = None
181 cached_diff = None
180 if caching_enabled:
182 if caching_enabled:
181 cached_diff = load_cached_diff(cache_file_path)
183 cached_diff = load_cached_diff(cache_file_path)
182
184
183 has_proper_diff_cache = cached_diff and cached_diff.get('diff')
185 has_proper_diff_cache = cached_diff and cached_diff.get('diff')
184 if not force_recache and has_proper_diff_cache:
186 if not force_recache and has_proper_diff_cache:
185 diffset = cached_diff['diff']
187 diffset = cached_diff['diff']
186 else:
188 else:
187 vcs_diff = self.rhodecode_vcs_repo.get_diff(
189 vcs_diff = self.rhodecode_vcs_repo.get_diff(
188 commit1, commit2,
190 commit1, commit2,
189 ignore_whitespace=hide_whitespace_changes,
191 ignore_whitespace=hide_whitespace_changes,
190 context=diff_context)
192 context=diff_context)
191
193
192 diff_processor = diffs.DiffProcessor(
194 diff_processor = diffs.DiffProcessor(
193 vcs_diff, format='newdiff', diff_limit=diff_limit,
195 vcs_diff, format='newdiff', diff_limit=diff_limit,
194 file_limit=file_limit, show_full_diff=c.fulldiff)
196 file_limit=file_limit, show_full_diff=c.fulldiff)
195
197
196 _parsed = diff_processor.prepare()
198 _parsed = diff_processor.prepare()
197
199
198 diffset = codeblocks.DiffSet(
200 diffset = codeblocks.DiffSet(
199 repo_name=self.db_repo_name,
201 repo_name=self.db_repo_name,
200 source_node_getter=codeblocks.diffset_node_getter(commit1),
202 source_node_getter=codeblocks.diffset_node_getter(commit1),
201 target_node_getter=codeblocks.diffset_node_getter(commit2))
203 target_node_getter=codeblocks.diffset_node_getter(commit2))
202
204
203 diffset = self.path_filter.render_patchset_filtered(
205 diffset = self.path_filter.render_patchset_filtered(
204 diffset, _parsed, commit1.raw_id, commit2.raw_id)
206 diffset, _parsed, commit1.raw_id, commit2.raw_id)
205
207
206 # save cached diff
208 # save cached diff
207 if caching_enabled:
209 if caching_enabled:
208 cache_diff(cache_file_path, diffset, None)
210 cache_diff(cache_file_path, diffset, None)
209
211
210 c.limited_diff = diffset.limited_diff
212 c.limited_diff = diffset.limited_diff
211 c.changes[commit.raw_id] = diffset
213 c.changes[commit.raw_id] = diffset
212 else:
214 else:
213 # TODO(marcink): no cache usage here...
215 # TODO(marcink): no cache usage here...
214 _diff = self.rhodecode_vcs_repo.get_diff(
216 _diff = self.rhodecode_vcs_repo.get_diff(
215 commit1, commit2,
217 commit1, commit2,
216 ignore_whitespace=hide_whitespace_changes, context=diff_context)
218 ignore_whitespace=hide_whitespace_changes, context=diff_context)
217 diff_processor = diffs.DiffProcessor(
219 diff_processor = diffs.DiffProcessor(
218 _diff, format='newdiff', diff_limit=diff_limit,
220 _diff, format='newdiff', diff_limit=diff_limit,
219 file_limit=file_limit, show_full_diff=c.fulldiff)
221 file_limit=file_limit, show_full_diff=c.fulldiff)
220 # downloads/raw we only need RAW diff nothing else
222 # downloads/raw we only need RAW diff nothing else
221 diff = self.path_filter.get_raw_patch(diff_processor)
223 diff = self.path_filter.get_raw_patch(diff_processor)
222 c.changes[commit.raw_id] = [None, None, None, None, diff, None, None]
224 c.changes[commit.raw_id] = [None, None, None, None, diff, None, None]
223
225
224 # sort comments by how they were generated
226 # sort comments by how they were generated
225 c.comments = sorted(c.comments, key=lambda x: x.comment_id)
227 c.comments = sorted(c.comments, key=lambda x: x.comment_id)
226
228
227 if len(c.commit_ranges) == 1:
229 if len(c.commit_ranges) == 1:
228 c.commit = c.commit_ranges[0]
230 c.commit = c.commit_ranges[0]
229 c.parent_tmpl = ''.join(
231 c.parent_tmpl = ''.join(
230 '# Parent %s\n' % x.raw_id for x in c.commit.parents)
232 '# Parent %s\n' % x.raw_id for x in c.commit.parents)
231
233
232 if method == 'download':
234 if method == 'download':
233 response = Response(diff)
235 response = Response(diff)
234 response.content_type = 'text/plain'
236 response.content_type = 'text/plain'
235 response.content_disposition = (
237 response.content_disposition = (
236 'attachment; filename=%s.diff' % commit_id_range[:12])
238 'attachment; filename=%s.diff' % commit_id_range[:12])
237 return response
239 return response
238 elif method == 'patch':
240 elif method == 'patch':
239 c.diff = safe_unicode(diff)
241 c.diff = safe_unicode(diff)
240 patch = render(
242 patch = render(
241 'rhodecode:templates/changeset/patch_changeset.mako',
243 'rhodecode:templates/changeset/patch_changeset.mako',
242 self._get_template_context(c), self.request)
244 self._get_template_context(c), self.request)
243 response = Response(patch)
245 response = Response(patch)
244 response.content_type = 'text/plain'
246 response.content_type = 'text/plain'
245 return response
247 return response
246 elif method == 'raw':
248 elif method == 'raw':
247 response = Response(diff)
249 response = Response(diff)
248 response.content_type = 'text/plain'
250 response.content_type = 'text/plain'
249 return response
251 return response
250 elif method == 'show':
252 elif method == 'show':
251 if len(c.commit_ranges) == 1:
253 if len(c.commit_ranges) == 1:
252 html = render(
254 html = render(
253 'rhodecode:templates/changeset/changeset.mako',
255 'rhodecode:templates/changeset/changeset.mako',
254 self._get_template_context(c), self.request)
256 self._get_template_context(c), self.request)
255 return Response(html)
257 return Response(html)
256 else:
258 else:
257 c.ancestor = None
259 c.ancestor = None
258 c.target_repo = self.db_repo
260 c.target_repo = self.db_repo
259 html = render(
261 html = render(
260 'rhodecode:templates/changeset/changeset_range.mako',
262 'rhodecode:templates/changeset/changeset_range.mako',
261 self._get_template_context(c), self.request)
263 self._get_template_context(c), self.request)
262 return Response(html)
264 return Response(html)
263
265
264 raise HTTPBadRequest()
266 raise HTTPBadRequest()
265
267
266 @LoginRequired()
268 @LoginRequired()
267 @HasRepoPermissionAnyDecorator(
269 @HasRepoPermissionAnyDecorator(
268 'repository.read', 'repository.write', 'repository.admin')
270 'repository.read', 'repository.write', 'repository.admin')
269 @view_config(
271 @view_config(
270 route_name='repo_commit', request_method='GET',
272 route_name='repo_commit', request_method='GET',
271 renderer=None)
273 renderer=None)
272 def repo_commit_show(self):
274 def repo_commit_show(self):
273 commit_id = self.request.matchdict['commit_id']
275 commit_id = self.request.matchdict['commit_id']
274 return self._commit(commit_id, method='show')
276 return self._commit(commit_id, method='show')
275
277
276 @LoginRequired()
278 @LoginRequired()
277 @HasRepoPermissionAnyDecorator(
279 @HasRepoPermissionAnyDecorator(
278 'repository.read', 'repository.write', 'repository.admin')
280 'repository.read', 'repository.write', 'repository.admin')
279 @view_config(
281 @view_config(
280 route_name='repo_commit_raw', request_method='GET',
282 route_name='repo_commit_raw', request_method='GET',
281 renderer=None)
283 renderer=None)
282 @view_config(
284 @view_config(
283 route_name='repo_commit_raw_deprecated', request_method='GET',
285 route_name='repo_commit_raw_deprecated', request_method='GET',
284 renderer=None)
286 renderer=None)
285 def repo_commit_raw(self):
287 def repo_commit_raw(self):
286 commit_id = self.request.matchdict['commit_id']
288 commit_id = self.request.matchdict['commit_id']
287 return self._commit(commit_id, method='raw')
289 return self._commit(commit_id, method='raw')
288
290
289 @LoginRequired()
291 @LoginRequired()
290 @HasRepoPermissionAnyDecorator(
292 @HasRepoPermissionAnyDecorator(
291 'repository.read', 'repository.write', 'repository.admin')
293 'repository.read', 'repository.write', 'repository.admin')
292 @view_config(
294 @view_config(
293 route_name='repo_commit_patch', request_method='GET',
295 route_name='repo_commit_patch', request_method='GET',
294 renderer=None)
296 renderer=None)
295 def repo_commit_patch(self):
297 def repo_commit_patch(self):
296 commit_id = self.request.matchdict['commit_id']
298 commit_id = self.request.matchdict['commit_id']
297 return self._commit(commit_id, method='patch')
299 return self._commit(commit_id, method='patch')
298
300
299 @LoginRequired()
301 @LoginRequired()
300 @HasRepoPermissionAnyDecorator(
302 @HasRepoPermissionAnyDecorator(
301 'repository.read', 'repository.write', 'repository.admin')
303 'repository.read', 'repository.write', 'repository.admin')
302 @view_config(
304 @view_config(
303 route_name='repo_commit_download', request_method='GET',
305 route_name='repo_commit_download', request_method='GET',
304 renderer=None)
306 renderer=None)
305 def repo_commit_download(self):
307 def repo_commit_download(self):
306 commit_id = self.request.matchdict['commit_id']
308 commit_id = self.request.matchdict['commit_id']
307 return self._commit(commit_id, method='download')
309 return self._commit(commit_id, method='download')
308
310
309 @LoginRequired()
311 @LoginRequired()
310 @NotAnonymous()
312 @NotAnonymous()
311 @HasRepoPermissionAnyDecorator(
313 @HasRepoPermissionAnyDecorator(
312 'repository.read', 'repository.write', 'repository.admin')
314 'repository.read', 'repository.write', 'repository.admin')
313 @CSRFRequired()
315 @CSRFRequired()
314 @view_config(
316 @view_config(
315 route_name='repo_commit_comment_create', request_method='POST',
317 route_name='repo_commit_comment_create', request_method='POST',
316 renderer='json_ext')
318 renderer='json_ext')
317 def repo_commit_comment_create(self):
319 def repo_commit_comment_create(self):
318 _ = self.request.translate
320 _ = self.request.translate
319 commit_id = self.request.matchdict['commit_id']
321 commit_id = self.request.matchdict['commit_id']
320
322
321 c = self.load_default_context()
323 c = self.load_default_context()
322 status = self.request.POST.get('changeset_status', None)
324 status = self.request.POST.get('changeset_status', None)
323 text = self.request.POST.get('text')
325 text = self.request.POST.get('text')
324 comment_type = self.request.POST.get('comment_type')
326 comment_type = self.request.POST.get('comment_type')
325 resolves_comment_id = self.request.POST.get('resolves_comment_id', None)
327 resolves_comment_id = self.request.POST.get('resolves_comment_id', None)
326
328
327 if status:
329 if status:
328 text = text or (_('Status change %(transition_icon)s %(status)s')
330 text = text or (_('Status change %(transition_icon)s %(status)s')
329 % {'transition_icon': '>',
331 % {'transition_icon': '>',
330 'status': ChangesetStatus.get_status_lbl(status)})
332 'status': ChangesetStatus.get_status_lbl(status)})
331
333
332 multi_commit_ids = []
334 multi_commit_ids = []
333 for _commit_id in self.request.POST.get('commit_ids', '').split(','):
335 for _commit_id in self.request.POST.get('commit_ids', '').split(','):
334 if _commit_id not in ['', None, EmptyCommit.raw_id]:
336 if _commit_id not in ['', None, EmptyCommit.raw_id]:
335 if _commit_id not in multi_commit_ids:
337 if _commit_id not in multi_commit_ids:
336 multi_commit_ids.append(_commit_id)
338 multi_commit_ids.append(_commit_id)
337
339
338 commit_ids = multi_commit_ids or [commit_id]
340 commit_ids = multi_commit_ids or [commit_id]
339
341
340 comment = None
342 comment = None
341 for current_id in filter(None, commit_ids):
343 for current_id in filter(None, commit_ids):
342 comment = CommentsModel().create(
344 comment = CommentsModel().create(
343 text=text,
345 text=text,
344 repo=self.db_repo.repo_id,
346 repo=self.db_repo.repo_id,
345 user=self._rhodecode_db_user.user_id,
347 user=self._rhodecode_db_user.user_id,
346 commit_id=current_id,
348 commit_id=current_id,
347 f_path=self.request.POST.get('f_path'),
349 f_path=self.request.POST.get('f_path'),
348 line_no=self.request.POST.get('line'),
350 line_no=self.request.POST.get('line'),
349 status_change=(ChangesetStatus.get_status_lbl(status)
351 status_change=(ChangesetStatus.get_status_lbl(status)
350 if status else None),
352 if status else None),
351 status_change_type=status,
353 status_change_type=status,
352 comment_type=comment_type,
354 comment_type=comment_type,
353 resolves_comment_id=resolves_comment_id,
355 resolves_comment_id=resolves_comment_id,
354 auth_user=self._rhodecode_user
356 auth_user=self._rhodecode_user
355 )
357 )
356
358
357 # get status if set !
359 # get status if set !
358 if status:
360 if status:
359 # if latest status was from pull request and it's closed
361 # if latest status was from pull request and it's closed
360 # disallow changing status !
362 # disallow changing status !
361 # dont_allow_on_closed_pull_request = True !
363 # dont_allow_on_closed_pull_request = True !
362
364
363 try:
365 try:
364 ChangesetStatusModel().set_status(
366 ChangesetStatusModel().set_status(
365 self.db_repo.repo_id,
367 self.db_repo.repo_id,
366 status,
368 status,
367 self._rhodecode_db_user.user_id,
369 self._rhodecode_db_user.user_id,
368 comment,
370 comment,
369 revision=current_id,
371 revision=current_id,
370 dont_allow_on_closed_pull_request=True
372 dont_allow_on_closed_pull_request=True
371 )
373 )
372 except StatusChangeOnClosedPullRequestError:
374 except StatusChangeOnClosedPullRequestError:
373 msg = _('Changing the status of a commit associated with '
375 msg = _('Changing the status of a commit associated with '
374 'a closed pull request is not allowed')
376 'a closed pull request is not allowed')
375 log.exception(msg)
377 log.exception(msg)
376 h.flash(msg, category='warning')
378 h.flash(msg, category='warning')
377 raise HTTPFound(h.route_path(
379 raise HTTPFound(h.route_path(
378 'repo_commit', repo_name=self.db_repo_name,
380 'repo_commit', repo_name=self.db_repo_name,
379 commit_id=current_id))
381 commit_id=current_id))
380
382
381 # finalize, commit and redirect
383 # finalize, commit and redirect
382 Session().commit()
384 Session().commit()
383
385
384 data = {
386 data = {
385 'target_id': h.safeid(h.safe_unicode(
387 'target_id': h.safeid(h.safe_unicode(
386 self.request.POST.get('f_path'))),
388 self.request.POST.get('f_path'))),
387 }
389 }
388 if comment:
390 if comment:
389 c.co = comment
391 c.co = comment
390 rendered_comment = render(
392 rendered_comment = render(
391 'rhodecode:templates/changeset/changeset_comment_block.mako',
393 'rhodecode:templates/changeset/changeset_comment_block.mako',
392 self._get_template_context(c), self.request)
394 self._get_template_context(c), self.request)
393
395
394 data.update(comment.get_dict())
396 data.update(comment.get_dict())
395 data.update({'rendered_text': rendered_comment})
397 data.update({'rendered_text': rendered_comment})
396
398
397 return data
399 return data
398
400
399 @LoginRequired()
401 @LoginRequired()
400 @NotAnonymous()
402 @NotAnonymous()
401 @HasRepoPermissionAnyDecorator(
403 @HasRepoPermissionAnyDecorator(
402 'repository.read', 'repository.write', 'repository.admin')
404 'repository.read', 'repository.write', 'repository.admin')
403 @CSRFRequired()
405 @CSRFRequired()
404 @view_config(
406 @view_config(
405 route_name='repo_commit_comment_preview', request_method='POST',
407 route_name='repo_commit_comment_preview', request_method='POST',
406 renderer='string', xhr=True)
408 renderer='string', xhr=True)
407 def repo_commit_comment_preview(self):
409 def repo_commit_comment_preview(self):
408 # Technically a CSRF token is not needed as no state changes with this
410 # Technically a CSRF token is not needed as no state changes with this
409 # call. However, as this is a POST is better to have it, so automated
411 # call. However, as this is a POST is better to have it, so automated
410 # tools don't flag it as potential CSRF.
412 # tools don't flag it as potential CSRF.
411 # Post is required because the payload could be bigger than the maximum
413 # Post is required because the payload could be bigger than the maximum
412 # allowed by GET.
414 # allowed by GET.
413
415
414 text = self.request.POST.get('text')
416 text = self.request.POST.get('text')
415 renderer = self.request.POST.get('renderer') or 'rst'
417 renderer = self.request.POST.get('renderer') or 'rst'
416 if text:
418 if text:
417 return h.render(text, renderer=renderer, mentions=True)
419 return h.render(text, renderer=renderer, mentions=True)
418 return ''
420 return ''
419
421
420 @LoginRequired()
422 @LoginRequired()
421 @NotAnonymous()
423 @NotAnonymous()
422 @HasRepoPermissionAnyDecorator(
424 @HasRepoPermissionAnyDecorator(
423 'repository.read', 'repository.write', 'repository.admin')
425 'repository.read', 'repository.write', 'repository.admin')
424 @CSRFRequired()
426 @CSRFRequired()
425 @view_config(
427 @view_config(
428 route_name='repo_commit_comment_attachment_upload', request_method='POST',
429 renderer='json_ext', xhr=True)
430 def repo_commit_comment_attachment_upload(self):
431 c = self.load_default_context()
432 upload_key = 'attachment'
433
434 file_obj = self.request.POST.get(upload_key)
435
436 if file_obj is None:
437 self.request.response.status = 400
438 return {'store_fid': None,
439 'access_path': None,
440 'error': '{} data field is missing'.format(upload_key)}
441
442 if not hasattr(file_obj, 'filename'):
443 self.request.response.status = 400
444 return {'store_fid': None,
445 'access_path': None,
446 'error': 'filename cannot be read from the data field'}
447
448 filename = file_obj.filename
449 file_display_name = filename
450
451 metadata = {
452 'user_uploaded': {'username': self._rhodecode_user.username,
453 'user_id': self._rhodecode_user.user_id,
454 'ip': self._rhodecode_user.ip_addr}}
455
456 # TODO(marcink): allow .ini configuration for allowed_extensions, and file-size
457 allowed_extensions = [
458 'gif', '.jpeg', '.jpg', '.png', '.docx', '.gz', '.log', '.pdf',
459 '.pptx', '.txt', '.xlsx', '.zip']
460 max_file_size = 10 * 1024 * 1024 # 10MB, also validated via dropzone.js
461
462 try:
463 storage = store_utils.get_file_storage(self.request.registry.settings)
464 store_uid, metadata = storage.save_file(
465 file_obj.file, filename, extra_metadata=metadata,
466 extensions=allowed_extensions, max_filesize=max_file_size)
467 except FileNotAllowedException:
468 self.request.response.status = 400
469 permitted_extensions = ', '.join(allowed_extensions)
470 error_msg = 'File `{}` is not allowed. ' \
471 'Only following extensions are permitted: {}'.format(
472 filename, permitted_extensions)
473 return {'store_fid': None,
474 'access_path': None,
475 'error': error_msg}
476 except FileOverSizeException:
477 self.request.response.status = 400
478 limit_mb = h.format_byte_size_binary(max_file_size)
479 return {'store_fid': None,
480 'access_path': None,
481 'error': 'File {} is exceeding allowed limit of {}.'.format(
482 filename, limit_mb)}
483
484 try:
485 entry = FileStore.create(
486 file_uid=store_uid, filename=metadata["filename"],
487 file_hash=metadata["sha256"], file_size=metadata["size"],
488 file_display_name=file_display_name,
489 file_description=u'comment attachment `{}`'.format(safe_unicode(filename)),
490 hidden=True, check_acl=True, user_id=self._rhodecode_user.user_id,
491 scope_repo_id=self.db_repo.repo_id
492 )
493 Session().add(entry)
494 Session().commit()
495 log.debug('Stored upload in DB as %s', entry)
496 except Exception:
497 log.exception('Failed to store file %s', filename)
498 self.request.response.status = 400
499 return {'store_fid': None,
500 'access_path': None,
501 'error': 'File {} failed to store in DB.'.format(filename)}
502
503 Session().commit()
504
505 return {
506 'store_fid': store_uid,
507 'access_path': h.route_path(
508 'download_file', fid=store_uid),
509 'fqn_access_path': h.route_url(
510 'download_file', fid=store_uid),
511 'repo_access_path': h.route_path(
512 'repo_artifacts_get', repo_name=self.db_repo_name, uid=store_uid),
513 'repo_fqn_access_path': h.route_url(
514 'repo_artifacts_get', repo_name=self.db_repo_name, uid=store_uid),
515 }
516
517 @LoginRequired()
518 @NotAnonymous()
519 @HasRepoPermissionAnyDecorator(
520 'repository.read', 'repository.write', 'repository.admin')
521 @CSRFRequired()
522 @view_config(
426 route_name='repo_commit_comment_delete', request_method='POST',
523 route_name='repo_commit_comment_delete', request_method='POST',
427 renderer='json_ext')
524 renderer='json_ext')
428 def repo_commit_comment_delete(self):
525 def repo_commit_comment_delete(self):
429 commit_id = self.request.matchdict['commit_id']
526 commit_id = self.request.matchdict['commit_id']
430 comment_id = self.request.matchdict['comment_id']
527 comment_id = self.request.matchdict['comment_id']
431
528
432 comment = ChangesetComment.get_or_404(comment_id)
529 comment = ChangesetComment.get_or_404(comment_id)
433 if not comment:
530 if not comment:
434 log.debug('Comment with id:%s not found, skipping', comment_id)
531 log.debug('Comment with id:%s not found, skipping', comment_id)
435 # comment already deleted in another call probably
532 # comment already deleted in another call probably
436 return True
533 return True
437
534
438 is_repo_admin = h.HasRepoPermissionAny('repository.admin')(self.db_repo_name)
535 is_repo_admin = h.HasRepoPermissionAny('repository.admin')(self.db_repo_name)
439 super_admin = h.HasPermissionAny('hg.admin')()
536 super_admin = h.HasPermissionAny('hg.admin')()
440 comment_owner = (comment.author.user_id == self._rhodecode_db_user.user_id)
537 comment_owner = (comment.author.user_id == self._rhodecode_db_user.user_id)
441 is_repo_comment = comment.repo.repo_name == self.db_repo_name
538 is_repo_comment = comment.repo.repo_name == self.db_repo_name
442 comment_repo_admin = is_repo_admin and is_repo_comment
539 comment_repo_admin = is_repo_admin and is_repo_comment
443
540
444 if super_admin or comment_owner or comment_repo_admin:
541 if super_admin or comment_owner or comment_repo_admin:
445 CommentsModel().delete(comment=comment, auth_user=self._rhodecode_user)
542 CommentsModel().delete(comment=comment, auth_user=self._rhodecode_user)
446 Session().commit()
543 Session().commit()
447 return True
544 return True
448 else:
545 else:
449 log.warning('No permissions for user %s to delete comment_id: %s',
546 log.warning('No permissions for user %s to delete comment_id: %s',
450 self._rhodecode_db_user, comment_id)
547 self._rhodecode_db_user, comment_id)
451 raise HTTPNotFound()
548 raise HTTPNotFound()
452
549
453 @LoginRequired()
550 @LoginRequired()
454 @HasRepoPermissionAnyDecorator(
551 @HasRepoPermissionAnyDecorator(
455 'repository.read', 'repository.write', 'repository.admin')
552 'repository.read', 'repository.write', 'repository.admin')
456 @view_config(
553 @view_config(
457 route_name='repo_commit_data', request_method='GET',
554 route_name='repo_commit_data', request_method='GET',
458 renderer='json_ext', xhr=True)
555 renderer='json_ext', xhr=True)
459 def repo_commit_data(self):
556 def repo_commit_data(self):
460 commit_id = self.request.matchdict['commit_id']
557 commit_id = self.request.matchdict['commit_id']
461 self.load_default_context()
558 self.load_default_context()
462
559
463 try:
560 try:
464 return self.rhodecode_vcs_repo.get_commit(commit_id=commit_id)
561 return self.rhodecode_vcs_repo.get_commit(commit_id=commit_id)
465 except CommitDoesNotExistError as e:
562 except CommitDoesNotExistError as e:
466 return EmptyCommit(message=str(e))
563 return EmptyCommit(message=str(e))
467
564
468 @LoginRequired()
565 @LoginRequired()
469 @HasRepoPermissionAnyDecorator(
566 @HasRepoPermissionAnyDecorator(
470 'repository.read', 'repository.write', 'repository.admin')
567 'repository.read', 'repository.write', 'repository.admin')
471 @view_config(
568 @view_config(
472 route_name='repo_commit_children', request_method='GET',
569 route_name='repo_commit_children', request_method='GET',
473 renderer='json_ext', xhr=True)
570 renderer='json_ext', xhr=True)
474 def repo_commit_children(self):
571 def repo_commit_children(self):
475 commit_id = self.request.matchdict['commit_id']
572 commit_id = self.request.matchdict['commit_id']
476 self.load_default_context()
573 self.load_default_context()
477
574
478 try:
575 try:
479 commit = self.rhodecode_vcs_repo.get_commit(commit_id=commit_id)
576 commit = self.rhodecode_vcs_repo.get_commit(commit_id=commit_id)
480 children = commit.children
577 children = commit.children
481 except CommitDoesNotExistError:
578 except CommitDoesNotExistError:
482 children = []
579 children = []
483
580
484 result = {"results": children}
581 result = {"results": children}
485 return result
582 return result
486
583
487 @LoginRequired()
584 @LoginRequired()
488 @HasRepoPermissionAnyDecorator(
585 @HasRepoPermissionAnyDecorator(
489 'repository.read', 'repository.write', 'repository.admin')
586 'repository.read', 'repository.write', 'repository.admin')
490 @view_config(
587 @view_config(
491 route_name='repo_commit_parents', request_method='GET',
588 route_name='repo_commit_parents', request_method='GET',
492 renderer='json_ext')
589 renderer='json_ext')
493 def repo_commit_parents(self):
590 def repo_commit_parents(self):
494 commit_id = self.request.matchdict['commit_id']
591 commit_id = self.request.matchdict['commit_id']
495 self.load_default_context()
592 self.load_default_context()
496
593
497 try:
594 try:
498 commit = self.rhodecode_vcs_repo.get_commit(commit_id=commit_id)
595 commit = self.rhodecode_vcs_repo.get_commit(commit_id=commit_id)
499 parents = commit.parents
596 parents = commit.parents
500 except CommitDoesNotExistError:
597 except CommitDoesNotExistError:
501 parents = []
598 parents = []
502 result = {"results": parents}
599 result = {"results": parents}
503 return result
600 return result
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
@@ -1,585 +1,611 b''
1 // comments.less
1 // comments.less
2 // For use in RhodeCode applications;
2 // For use in RhodeCode applications;
3 // see style guide documentation for guidelines.
3 // see style guide documentation for guidelines.
4
4
5
5
6 // Comments
6 // Comments
7 @comment-outdated-opacity: 0.6;
7 @comment-outdated-opacity: 0.6;
8
8
9 .comments {
9 .comments {
10 width: 100%;
10 width: 100%;
11 }
11 }
12
12
13 .comments-heading {
13 .comments-heading {
14 margin-bottom: -1px;
14 margin-bottom: -1px;
15 background: @grey6;
15 background: @grey6;
16 display: block;
16 display: block;
17 padding: 10px 0px;
17 padding: 10px 0px;
18 font-size: 18px
18 font-size: 18px
19 }
19 }
20
20
21 #comment-tr-show {
21 #comment-tr-show {
22 padding: 5px 0;
22 padding: 5px 0;
23 }
23 }
24
24
25 tr.inline-comments div {
25 tr.inline-comments div {
26 max-width: 100%;
26 max-width: 100%;
27
27
28 p {
28 p {
29 white-space: normal;
29 white-space: normal;
30 }
30 }
31
31
32 code, pre, .code, dd {
32 code, pre, .code, dd {
33 overflow-x: auto;
33 overflow-x: auto;
34 width: 1062px;
34 width: 1062px;
35 }
35 }
36
36
37 dd {
37 dd {
38 width: auto;
38 width: auto;
39 }
39 }
40 }
40 }
41
41
42 #injected_page_comments {
42 #injected_page_comments {
43 .comment-previous-link,
43 .comment-previous-link,
44 .comment-next-link,
44 .comment-next-link,
45 .comment-links-divider {
45 .comment-links-divider {
46 display: none;
46 display: none;
47 }
47 }
48 }
48 }
49
49
50 .add-comment {
50 .add-comment {
51 margin-bottom: 10px;
51 margin-bottom: 10px;
52 }
52 }
53 .hide-comment-button .add-comment {
53 .hide-comment-button .add-comment {
54 display: none;
54 display: none;
55 }
55 }
56
56
57 .comment-bubble {
57 .comment-bubble {
58 color: @grey4;
58 color: @grey4;
59 margin-top: 4px;
59 margin-top: 4px;
60 margin-right: 30px;
60 margin-right: 30px;
61 visibility: hidden;
61 visibility: hidden;
62 }
62 }
63
63
64 .comment-label {
64 .comment-label {
65 float: left;
65 float: left;
66
66
67 padding: 0.4em 0.4em;
67 padding: 0.4em 0.4em;
68 margin: 3px 5px 0px -10px;
68 margin: 3px 5px 0px -10px;
69 display: inline-block;
69 display: inline-block;
70 min-height: 0;
70 min-height: 0;
71
71
72 text-align: center;
72 text-align: center;
73 font-size: 10px;
73 font-size: 10px;
74 line-height: .8em;
74 line-height: .8em;
75
75
76 font-family: @text-italic;
76 font-family: @text-italic;
77 font-style: italic;
77 font-style: italic;
78 background: #fff none;
78 background: #fff none;
79 color: @grey4;
79 color: @grey4;
80 border: 1px solid @grey4;
80 border: 1px solid @grey4;
81 white-space: nowrap;
81 white-space: nowrap;
82
82
83 text-transform: uppercase;
83 text-transform: uppercase;
84 min-width: 40px;
84 min-width: 40px;
85
85
86 &.todo {
86 &.todo {
87 color: @color5;
87 color: @color5;
88 font-style: italic;
88 font-style: italic;
89 font-weight: @text-bold-italic-weight;
89 font-weight: @text-bold-italic-weight;
90 font-family: @text-bold-italic;
90 font-family: @text-bold-italic;
91 }
91 }
92
92
93 .resolve {
93 .resolve {
94 cursor: pointer;
94 cursor: pointer;
95 text-decoration: underline;
95 text-decoration: underline;
96 }
96 }
97
97
98 .resolved {
98 .resolved {
99 text-decoration: line-through;
99 text-decoration: line-through;
100 color: @color1;
100 color: @color1;
101 }
101 }
102 .resolved a {
102 .resolved a {
103 text-decoration: line-through;
103 text-decoration: line-through;
104 color: @color1;
104 color: @color1;
105 }
105 }
106 .resolve-text {
106 .resolve-text {
107 color: @color1;
107 color: @color1;
108 margin: 2px 8px;
108 margin: 2px 8px;
109 font-family: @text-italic;
109 font-family: @text-italic;
110 font-style: italic;
110 font-style: italic;
111 }
111 }
112 }
112 }
113
113
114 .has-spacer-after {
114 .has-spacer-after {
115 &:after {
115 &:after {
116 content: ' | ';
116 content: ' | ';
117 color: @grey5;
117 color: @grey5;
118 }
118 }
119 }
119 }
120
120
121 .has-spacer-before {
121 .has-spacer-before {
122 &:before {
122 &:before {
123 content: ' | ';
123 content: ' | ';
124 color: @grey5;
124 color: @grey5;
125 }
125 }
126 }
126 }
127
127
128 .comment {
128 .comment {
129
129
130 &.comment-general {
130 &.comment-general {
131 border: 1px solid @grey5;
131 border: 1px solid @grey5;
132 padding: 5px 5px 5px 5px;
132 padding: 5px 5px 5px 5px;
133 }
133 }
134
134
135 margin: @padding 0;
135 margin: @padding 0;
136 padding: 4px 0 0 0;
136 padding: 4px 0 0 0;
137 line-height: 1em;
137 line-height: 1em;
138
138
139 .rc-user {
139 .rc-user {
140 min-width: 0;
140 min-width: 0;
141 margin: 0px .5em 0 0;
141 margin: 0px .5em 0 0;
142
142
143 .user {
143 .user {
144 display: inline;
144 display: inline;
145 }
145 }
146 }
146 }
147
147
148 .meta {
148 .meta {
149 position: relative;
149 position: relative;
150 width: 100%;
150 width: 100%;
151 border-bottom: 1px solid @grey5;
151 border-bottom: 1px solid @grey5;
152 margin: -5px 0px;
152 margin: -5px 0px;
153 line-height: 24px;
153 line-height: 24px;
154
154
155 &:hover .permalink {
155 &:hover .permalink {
156 visibility: visible;
156 visibility: visible;
157 color: @rcblue;
157 color: @rcblue;
158 }
158 }
159 }
159 }
160
160
161 .author,
161 .author,
162 .date {
162 .date {
163 display: inline;
163 display: inline;
164
164
165 &:after {
165 &:after {
166 content: ' | ';
166 content: ' | ';
167 color: @grey5;
167 color: @grey5;
168 }
168 }
169 }
169 }
170
170
171 .author-general img {
171 .author-general img {
172 top: 3px;
172 top: 3px;
173 }
173 }
174 .author-inline img {
174 .author-inline img {
175 top: 3px;
175 top: 3px;
176 }
176 }
177
177
178 .status-change,
178 .status-change,
179 .permalink,
179 .permalink,
180 .changeset-status-lbl {
180 .changeset-status-lbl {
181 display: inline;
181 display: inline;
182 }
182 }
183
183
184 .permalink {
184 .permalink {
185 visibility: hidden;
185 visibility: hidden;
186 }
186 }
187
187
188 .comment-links-divider {
188 .comment-links-divider {
189 display: inline;
189 display: inline;
190 }
190 }
191
191
192 .comment-links-block {
192 .comment-links-block {
193 float:right;
193 float:right;
194 text-align: right;
194 text-align: right;
195 min-width: 85px;
195 min-width: 85px;
196
196
197 [class^="icon-"]:before,
197 [class^="icon-"]:before,
198 [class*=" icon-"]:before {
198 [class*=" icon-"]:before {
199 margin-left: 0;
199 margin-left: 0;
200 margin-right: 0;
200 margin-right: 0;
201 }
201 }
202 }
202 }
203
203
204 .comment-previous-link {
204 .comment-previous-link {
205 display: inline-block;
205 display: inline-block;
206
206
207 .arrow_comment_link{
207 .arrow_comment_link{
208 cursor: pointer;
208 cursor: pointer;
209 i {
209 i {
210 font-size:10px;
210 font-size:10px;
211 }
211 }
212 }
212 }
213 .arrow_comment_link.disabled {
213 .arrow_comment_link.disabled {
214 cursor: default;
214 cursor: default;
215 color: @grey5;
215 color: @grey5;
216 }
216 }
217 }
217 }
218
218
219 .comment-next-link {
219 .comment-next-link {
220 display: inline-block;
220 display: inline-block;
221
221
222 .arrow_comment_link{
222 .arrow_comment_link{
223 cursor: pointer;
223 cursor: pointer;
224 i {
224 i {
225 font-size:10px;
225 font-size:10px;
226 }
226 }
227 }
227 }
228 .arrow_comment_link.disabled {
228 .arrow_comment_link.disabled {
229 cursor: default;
229 cursor: default;
230 color: @grey5;
230 color: @grey5;
231 }
231 }
232 }
232 }
233
233
234 .delete-comment {
234 .delete-comment {
235 display: inline-block;
235 display: inline-block;
236 color: @rcblue;
236 color: @rcblue;
237
237
238 &:hover {
238 &:hover {
239 cursor: pointer;
239 cursor: pointer;
240 }
240 }
241 }
241 }
242
242
243 .text {
243 .text {
244 clear: both;
244 clear: both;
245 .border-radius(@border-radius);
245 .border-radius(@border-radius);
246 .box-sizing(border-box);
246 .box-sizing(border-box);
247
247
248 .markdown-block p,
248 .markdown-block p,
249 .rst-block p {
249 .rst-block p {
250 margin: .5em 0 !important;
250 margin: .5em 0 !important;
251 // TODO: lisa: This is needed because of other rst !important rules :[
251 // TODO: lisa: This is needed because of other rst !important rules :[
252 }
252 }
253 }
253 }
254
254
255 .pr-version {
255 .pr-version {
256 float: left;
256 float: left;
257 margin: 0px 4px;
257 margin: 0px 4px;
258 }
258 }
259 .pr-version-inline {
259 .pr-version-inline {
260 float: left;
260 float: left;
261 margin: 0px 4px;
261 margin: 0px 4px;
262 }
262 }
263 .pr-version-num {
263 .pr-version-num {
264 font-size: 10px;
264 font-size: 10px;
265 }
265 }
266 }
266 }
267
267
268 @comment-padding: 5px;
268 @comment-padding: 5px;
269
269
270 .general-comments {
270 .general-comments {
271 .comment-outdated {
271 .comment-outdated {
272 opacity: @comment-outdated-opacity;
272 opacity: @comment-outdated-opacity;
273 }
273 }
274 }
274 }
275
275
276 .inline-comments {
276 .inline-comments {
277 border-radius: @border-radius;
277 border-radius: @border-radius;
278 .comment {
278 .comment {
279 margin: 0;
279 margin: 0;
280 border-radius: @border-radius;
280 border-radius: @border-radius;
281 }
281 }
282 .comment-outdated {
282 .comment-outdated {
283 opacity: @comment-outdated-opacity;
283 opacity: @comment-outdated-opacity;
284 }
284 }
285
285
286 .comment-inline {
286 .comment-inline {
287 background: white;
287 background: white;
288 padding: @comment-padding @comment-padding;
288 padding: @comment-padding @comment-padding;
289 border: @comment-padding solid @grey6;
289 border: @comment-padding solid @grey6;
290
290
291 .text {
291 .text {
292 border: none;
292 border: none;
293 }
293 }
294 .meta {
294 .meta {
295 border-bottom: 1px solid @grey6;
295 border-bottom: 1px solid @grey6;
296 margin: -5px 0px;
296 margin: -5px 0px;
297 line-height: 24px;
297 line-height: 24px;
298 }
298 }
299 }
299 }
300 .comment-selected {
300 .comment-selected {
301 border-left: 6px solid @comment-highlight-color;
301 border-left: 6px solid @comment-highlight-color;
302 }
302 }
303 .comment-inline-form {
303 .comment-inline-form {
304 padding: @comment-padding;
304 padding: @comment-padding;
305 display: none;
305 display: none;
306 }
306 }
307 .cb-comment-add-button {
307 .cb-comment-add-button {
308 margin: @comment-padding;
308 margin: @comment-padding;
309 }
309 }
310 /* hide add comment button when form is open */
310 /* hide add comment button when form is open */
311 .comment-inline-form-open ~ .cb-comment-add-button {
311 .comment-inline-form-open ~ .cb-comment-add-button {
312 display: none;
312 display: none;
313 }
313 }
314 .comment-inline-form-open {
314 .comment-inline-form-open {
315 display: block;
315 display: block;
316 }
316 }
317 /* hide add comment button when form but no comments */
317 /* hide add comment button when form but no comments */
318 .comment-inline-form:first-child + .cb-comment-add-button {
318 .comment-inline-form:first-child + .cb-comment-add-button {
319 display: none;
319 display: none;
320 }
320 }
321 /* hide add comment button when no comments or form */
321 /* hide add comment button when no comments or form */
322 .cb-comment-add-button:first-child {
322 .cb-comment-add-button:first-child {
323 display: none;
323 display: none;
324 }
324 }
325 /* hide add comment button when only comment is being deleted */
325 /* hide add comment button when only comment is being deleted */
326 .comment-deleting:first-child + .cb-comment-add-button {
326 .comment-deleting:first-child + .cb-comment-add-button {
327 display: none;
327 display: none;
328 }
328 }
329 }
329 }
330
330
331
331
332 .show-outdated-comments {
332 .show-outdated-comments {
333 display: inline;
333 display: inline;
334 color: @rcblue;
334 color: @rcblue;
335 }
335 }
336
336
337 // Comment Form
337 // Comment Form
338 div.comment-form {
338 div.comment-form {
339 margin-top: 20px;
339 margin-top: 20px;
340 }
340 }
341
341
342 .comment-form strong {
342 .comment-form strong {
343 display: block;
343 display: block;
344 margin-bottom: 15px;
344 margin-bottom: 15px;
345 }
345 }
346
346
347 .comment-form textarea {
347 .comment-form textarea {
348 width: 100%;
348 width: 100%;
349 height: 100px;
349 height: 100px;
350 font-family: @text-monospace;
350 font-family: @text-monospace;
351 }
351 }
352
352
353 form.comment-form {
353 form.comment-form {
354 margin-top: 10px;
354 margin-top: 10px;
355 margin-left: 10px;
355 margin-left: 10px;
356 }
356 }
357
357
358 .comment-inline-form .comment-block-ta,
358 .comment-inline-form .comment-block-ta,
359 .comment-form .comment-block-ta,
359 .comment-form .comment-block-ta,
360 .comment-form .preview-box {
360 .comment-form .preview-box {
361 .border-radius(@border-radius);
361 .border-radius(@border-radius);
362 .box-sizing(border-box);
362 .box-sizing(border-box);
363 background-color: white;
363 background-color: white;
364 }
364 }
365
365
366 .comment-form-submit {
366 .comment-form-submit {
367 margin-top: 5px;
367 margin-top: 5px;
368 margin-left: 525px;
368 margin-left: 525px;
369 }
369 }
370
370
371 .file-comments {
371 .file-comments {
372 display: none;
372 display: none;
373 }
373 }
374
374
375 .comment-form .preview-box.unloaded,
375 .comment-form .preview-box.unloaded,
376 .comment-inline-form .preview-box.unloaded {
376 .comment-inline-form .preview-box.unloaded {
377 height: 50px;
377 height: 50px;
378 text-align: center;
378 text-align: center;
379 padding: 20px;
379 padding: 20px;
380 background-color: white;
380 background-color: white;
381 }
381 }
382
382
383 .comment-footer {
383 .comment-footer {
384 position: relative;
384 position: relative;
385 width: 100%;
385 width: 100%;
386 min-height: 42px;
386 min-height: 42px;
387
387
388 .status_box,
388 .status_box,
389 .cancel-button {
389 .cancel-button {
390 float: left;
390 float: left;
391 display: inline-block;
391 display: inline-block;
392 }
392 }
393
393
394 .action-buttons {
394 .action-buttons {
395 float: right;
395 float: right;
396 display: inline-block;
396 display: inline-block;
397 }
397 }
398
398
399 .action-buttons-extra {
399 .action-buttons-extra {
400 display: inline-block;
400 display: inline-block;
401 }
401 }
402 }
402 }
403
403
404 .comment-form {
404 .comment-form {
405
405
406 .comment {
406 .comment {
407 margin-left: 10px;
407 margin-left: 10px;
408 }
408 }
409
409
410 .comment-help {
410 .comment-help {
411 color: @grey4;
411 color: @grey4;
412 padding: 5px 0 5px 0;
412 padding: 5px 0 5px 0;
413 }
413 }
414
414
415 .comment-title {
415 .comment-title {
416 padding: 5px 0 5px 0;
416 padding: 5px 0 5px 0;
417 }
417 }
418
418
419 .comment-button {
419 .comment-button {
420 display: inline-block;
420 display: inline-block;
421 }
421 }
422
422
423 .comment-button-input {
423 .comment-button-input {
424 margin-right: 0;
424 margin-right: 0;
425 }
425 }
426
426
427 .comment-footer {
427 .comment-footer {
428 margin-bottom: 110px;
428 margin-bottom: 110px;
429 margin-top: 10px;
429 margin-top: 10px;
430 }
430 }
431 }
431 }
432
432
433
433
434 .comment-form-login {
434 .comment-form-login {
435 .comment-help {
435 .comment-help {
436 padding: 0.7em; //same as the button
436 padding: 0.7em; //same as the button
437 }
437 }
438
438
439 div.clearfix {
439 div.clearfix {
440 clear: both;
440 clear: both;
441 width: 100%;
441 width: 100%;
442 display: block;
442 display: block;
443 }
443 }
444 }
444 }
445
445
446 .comment-type {
446 .comment-type {
447 margin: 0px;
447 margin: 0px;
448 border-radius: inherit;
448 border-radius: inherit;
449 border-color: @grey6;
449 border-color: @grey6;
450 }
450 }
451
451
452 .preview-box {
452 .preview-box {
453 min-height: 105px;
453 min-height: 105px;
454 margin-bottom: 15px;
454 margin-bottom: 15px;
455 background-color: white;
455 background-color: white;
456 .border-radius(@border-radius);
456 .border-radius(@border-radius);
457 .box-sizing(border-box);
457 .box-sizing(border-box);
458 }
458 }
459
459
460 .add-another-button {
460 .add-another-button {
461 margin-left: 10px;
461 margin-left: 10px;
462 margin-top: 10px;
462 margin-top: 10px;
463 margin-bottom: 10px;
463 margin-bottom: 10px;
464 }
464 }
465
465
466 .comment .buttons {
466 .comment .buttons {
467 float: right;
467 float: right;
468 margin: -1px 0px 0px 0px;
468 margin: -1px 0px 0px 0px;
469 }
469 }
470
470
471 // Inline Comment Form
471 // Inline Comment Form
472 .injected_diff .comment-inline-form,
472 .injected_diff .comment-inline-form,
473 .comment-inline-form {
473 .comment-inline-form {
474 background-color: white;
474 background-color: white;
475 margin-top: 10px;
475 margin-top: 10px;
476 margin-bottom: 20px;
476 margin-bottom: 20px;
477 }
477 }
478
478
479 .inline-form {
479 .inline-form {
480 padding: 10px 7px;
480 padding: 10px 7px;
481 }
481 }
482
482
483 .inline-form div {
483 .inline-form div {
484 max-width: 100%;
484 max-width: 100%;
485 }
485 }
486
486
487 .overlay {
487 .overlay {
488 display: none;
488 display: none;
489 position: absolute;
489 position: absolute;
490 width: 100%;
490 width: 100%;
491 text-align: center;
491 text-align: center;
492 vertical-align: middle;
492 vertical-align: middle;
493 font-size: 16px;
493 font-size: 16px;
494 background: none repeat scroll 0 0 white;
494 background: none repeat scroll 0 0 white;
495
495
496 &.submitting {
496 &.submitting {
497 display: block;
497 display: block;
498 opacity: 0.5;
498 opacity: 0.5;
499 z-index: 100;
499 z-index: 100;
500 }
500 }
501 }
501 }
502 .comment-inline-form .overlay.submitting .overlay-text {
502 .comment-inline-form .overlay.submitting .overlay-text {
503 margin-top: 5%;
503 margin-top: 5%;
504 }
504 }
505
505
506 .comment-inline-form .clearfix,
506 .comment-inline-form .clearfix,
507 .comment-form .clearfix {
507 .comment-form .clearfix {
508 .border-radius(@border-radius);
508 .border-radius(@border-radius);
509 margin: 0px;
509 margin: 0px;
510 }
510 }
511
511
512 .comment-inline-form .comment-footer {
512 .comment-inline-form .comment-footer {
513 margin: 10px 0px 0px 0px;
513 margin: 10px 0px 0px 0px;
514 }
514 }
515
515
516 .hide-inline-form-button {
516 .hide-inline-form-button {
517 margin-left: 5px;
517 margin-left: 5px;
518 }
518 }
519 .comment-button .hide-inline-form {
519 .comment-button .hide-inline-form {
520 background: white;
520 background: white;
521 }
521 }
522
522
523 .comment-area {
523 .comment-area {
524 padding: 8px 12px;
524 padding: 8px 12px;
525 border: 1px solid @grey5;
525 border: 1px solid @grey5;
526 .border-radius(@border-radius);
526 .border-radius(@border-radius);
527
527
528 .resolve-action {
528 .resolve-action {
529 padding: 1px 0px 0px 6px;
529 padding: 1px 0px 0px 6px;
530 }
530 }
531
531
532 }
532 }
533
533
534 .comment-area-header .nav-links {
534 .comment-area-header .nav-links {
535 display: flex;
535 display: flex;
536 flex-flow: row wrap;
536 flex-flow: row wrap;
537 -webkit-flex-flow: row wrap;
537 -webkit-flex-flow: row wrap;
538 width: 100%;
538 width: 100%;
539 }
539 }
540
540
541 .comment-area-footer {
541 .comment-area-footer {
542 display: flex;
542 min-height: 30px;
543 }
543 }
544
544
545 .comment-footer .toolbar {
545 .comment-footer .toolbar {
546
546
547 }
547 }
548
548
549 .comment-attachment-uploader {
550 border: 1px dashed white;
551 border-radius: @border-radius;
552 margin-top: -10px;
553
554 &.dz-drag-hover {
555 border-color: @grey3;
556 }
557
558 .dz-error-message {
559 padding-top: 0;
560 }
561 }
562
563 .comment-attachment-text {
564 clear: both;
565 font-size: 11px;
566 color: #8F8F8F;
567 width: 100%;
568 .pick-attachment {
569 color: #8F8F8F;
570 }
571 .pick-attachment:hover {
572 color: @rcblue;
573 }
574 }
575
549 .nav-links {
576 .nav-links {
550 padding: 0;
577 padding: 0;
551 margin: 0;
578 margin: 0;
552 list-style: none;
579 list-style: none;
553 height: auto;
580 height: auto;
554 border-bottom: 1px solid @grey5;
581 border-bottom: 1px solid @grey5;
555 }
582 }
556 .nav-links li {
583 .nav-links li {
557 display: inline-block;
584 display: inline-block;
558 list-style-type: none;
585 list-style-type: none;
559 }
586 }
560
587
561 .nav-links li a.disabled {
588 .nav-links li a.disabled {
562 cursor: not-allowed;
589 cursor: not-allowed;
563 }
590 }
564
591
565 .nav-links li.active a {
592 .nav-links li.active a {
566 border-bottom: 2px solid @rcblue;
593 border-bottom: 2px solid @rcblue;
567 color: #000;
594 color: #000;
568 font-weight: 600;
595 font-weight: 600;
569 }
596 }
570 .nav-links li a {
597 .nav-links li a {
571 display: inline-block;
598 display: inline-block;
572 padding: 0px 10px 5px 10px;
599 padding: 0px 10px 5px 10px;
573 margin-bottom: -1px;
600 margin-bottom: -1px;
574 font-size: 14px;
601 font-size: 14px;
575 line-height: 28px;
602 line-height: 28px;
576 color: #8f8f8f;
603 color: #8f8f8f;
577 border-bottom: 2px solid transparent;
604 border-bottom: 2px solid transparent;
578 }
605 }
579
606
580 .toolbar-text {
607 .toolbar-text {
581 float: left;
608 float: left;
582 margin: -5px 0px 0px 0px;
583 font-size: 12px;
609 font-size: 12px;
584 }
610 }
585
611
@@ -1,397 +1,398 b''
1
1
2 /** MODAL **/
2 /** MODAL **/
3 .modal-open {
3 .modal-open {
4 overflow:hidden;
4 overflow:hidden;
5 }
5 }
6 body.modal-open, .modal-open .navbar-fixed-top, .modal-open .navbar-fixed-bottom {
6 body.modal-open, .modal-open .navbar-fixed-top, .modal-open .navbar-fixed-bottom {
7 margin-right:15px;
7 margin-right:15px;
8 }
8 }
9 .modal {
9 .modal {
10 position:fixed;
10 position:fixed;
11 top:0;
11 top:0;
12 right:0;
12 right:0;
13 bottom:0;
13 bottom:0;
14 left:0;
14 left:0;
15 z-index:1040;
15 z-index:1040;
16 display:none;
16 display:none;
17 overflow-y:scroll;
17 overflow-y:scroll;
18 &.fade .modal-dialog {
18 &.fade .modal-dialog {
19 -webkit-transform:translate(0,-25%);
19 -webkit-transform:translate(0,-25%);
20 -ms-transform:translate(0,-25%);
20 -ms-transform:translate(0,-25%);
21 transform:translate(0,-25%);
21 transform:translate(0,-25%);
22 -webkit-transition:-webkit-transform 0.3s ease-out;
22 -webkit-transition:-webkit-transform 0.3s ease-out;
23 -moz-transition:-moz-transform 0.3s ease-out;
23 -moz-transition:-moz-transform 0.3s ease-out;
24 -o-transition:-o-transform 0.3s ease-out;
24 -o-transition:-o-transform 0.3s ease-out;
25 transition:transform 0.3s ease-out;
25 transition:transform 0.3s ease-out;
26 }
26 }
27 &.in .modal-dialog {
27 &.in .modal-dialog {
28 -webkit-transform:translate(0,0);
28 -webkit-transform:translate(0,0);
29 -ms-transform:translate(0,0);
29 -ms-transform:translate(0,0);
30 transform:translate(0,0);
30 transform:translate(0,0);
31 }
31 }
32 }
32 }
33 .modal-dialog {
33 .modal-dialog {
34 z-index:1050;
34 z-index:1050;
35 width:auto;
35 width:auto;
36 padding:10px;
36 padding:10px;
37 margin-right:auto;
37 margin-right:auto;
38 margin-left:auto;
38 margin-left:auto;
39 }
39 }
40 .modal-content {
40 .modal-content {
41 position:relative;
41 position:relative;
42 background-color:#ffffff;
42 background-color:#ffffff;
43 border: @border-thickness solid rgba(0,0,0,0.2);
43 border: @border-thickness solid rgba(0,0,0,0.2);
44 .border-radius(@border-radius);
44 .border-radius(@border-radius);
45 outline:none;
45 outline:none;
46 -webkit-box-shadow:0 3px 9px rgba(0,0,0,0.5);
46 -webkit-box-shadow:0 3px 9px rgba(0,0,0,0.5);
47 box-shadow:0 3px 9px rgba(0,0,0,0.5);
47 box-shadow:0 3px 9px rgba(0,0,0,0.5);
48 background-clip:padding-box;
48 background-clip:padding-box;
49 }
49 }
50 .modal-backdrop {
50 .modal-backdrop {
51 position:fixed;
51 position:fixed;
52 top:0;
52 top:0;
53 right:0;
53 right:0;
54 bottom:0;
54 bottom:0;
55 left:0;
55 left:0;
56 z-index:1030;
56 z-index:1030;
57 background-color:#000000;
57 background-color:#000000;
58
58
59 &.modal-backdrop.fade {
59 &.modal-backdrop.fade {
60 opacity:0;
60 opacity:0;
61 filter:alpha(opacity=0);
61 filter:alpha(opacity=0);
62 }
62 }
63 &.in {
63 &.in {
64 opacity:0.5;
64 opacity:0.5;
65 filter:alpha(opacity=50);
65 filter:alpha(opacity=50);
66 }
66 }
67 }
67 }
68 .modal-header {
68 .modal-header {
69 min-height:16.428571429px;
69 min-height:16.428571429px;
70 padding:15px;
70 padding:15px;
71 border-bottom: @border-thickness solid @grey6;
71 border-bottom: @border-thickness solid @grey6;
72 .close {
72 .close {
73 margin-top:-2px;
73 margin-top:-2px;
74 }
74 }
75 }
75 }
76 .modal-title {
76 .modal-title {
77 margin:0;
77 margin:0;
78 line-height:1.428571429;
78 line-height:1.428571429;
79 }
79 }
80 .modal-body {
80 .modal-body {
81 position:relative;
81 position:relative;
82 padding:20px;
82 padding:20px;
83 }
83 }
84 .modal-footer {
84 .modal-footer {
85 padding:19px 20px 20px;
85 padding:19px 20px 20px;
86 margin-top:15px;
86 margin-top:15px;
87 text-align:right;
87 text-align:right;
88 border-top:1px solid #e5e5e5;
88 border-top:1px solid #e5e5e5;
89 .btn + .btn {
89 .btn + .btn {
90 margin-bottom:0;
90 margin-bottom:0;
91 margin-left:5px;
91 margin-left:5px;
92 }
92 }
93 .btn-group .btn + .btn {
93 .btn-group .btn + .btn {
94 margin-left:-1px;
94 margin-left:-1px;
95 }
95 }
96 .btn-block + .btn-block {
96 .btn-block + .btn-block {
97 margin-left:0;
97 margin-left:0;
98 }
98 }
99 &:before {
99 &:before {
100 display:table;
100 display:table;
101 content:" ";
101 content:" ";
102 }
102 }
103 &:after {
103 &:after {
104 display:table;
104 display:table;
105 content:" ";
105 content:" ";
106 clear:both;
106 clear:both;
107 }
107 }
108 }
108 }
109
109
110 /** MARKDOWN styling **/
110 /** MARKDOWN styling **/
111 div.markdown-block {
111 div.markdown-block {
112 clear: both;
112 clear: both;
113 overflow: hidden;
113 overflow: hidden;
114 margin: 0;
114 margin: 0;
115 padding: 3px 15px 3px;
115 padding: 3px 15px 3px;
116 }
116 }
117
117
118 div.markdown-block h1,
118 div.markdown-block h1,
119 div.markdown-block h2,
119 div.markdown-block h2,
120 div.markdown-block h3,
120 div.markdown-block h3,
121 div.markdown-block h4,
121 div.markdown-block h4,
122 div.markdown-block h5,
122 div.markdown-block h5,
123 div.markdown-block h6 {
123 div.markdown-block h6 {
124 border-bottom: none !important;
124 border-bottom: none !important;
125 padding: 0 !important;
125 padding: 0 !important;
126 overflow: visible !important;
126 overflow: visible !important;
127 }
127 }
128
128
129 div.markdown-block h1,
129 div.markdown-block h1,
130 div.markdown-block h2 {
130 div.markdown-block h2 {
131 border-bottom: 1px #e6e5e5 solid !important;
131 border-bottom: 1px #e6e5e5 solid !important;
132 }
132 }
133
133
134 div.markdown-block h1 {
134 div.markdown-block h1 {
135 font-size: 32px;
135 font-size: 32px;
136 margin: 15px 0 15px 0 !important;
136 margin: 15px 0 15px 0 !important;
137 padding-bottom: 5px !important;
137 padding-bottom: 5px !important;
138 }
138 }
139
139
140 div.markdown-block h2 {
140 div.markdown-block h2 {
141 font-size: 24px !important;
141 font-size: 24px !important;
142 margin: 34px 0 10px 0 !important;
142 margin: 34px 0 10px 0 !important;
143 padding-top: 15px !important;
143 padding-top: 15px !important;
144 padding-bottom: 8px !important;
144 padding-bottom: 8px !important;
145 }
145 }
146
146
147 div.markdown-block h3 {
147 div.markdown-block h3 {
148 font-size: 18px !important;
148 font-size: 18px !important;
149 margin: 30px 0 8px 0 !important;
149 margin: 30px 0 8px 0 !important;
150 padding-bottom: 2px !important;
150 padding-bottom: 2px !important;
151 }
151 }
152
152
153 div.markdown-block h4 {
153 div.markdown-block h4 {
154 font-size: 13px !important;
154 font-size: 13px !important;
155 margin: 18px 0 3px 0 !important;
155 margin: 18px 0 3px 0 !important;
156 }
156 }
157
157
158 div.markdown-block h5 {
158 div.markdown-block h5 {
159 font-size: 12px !important;
159 font-size: 12px !important;
160 margin: 15px 0 3px 0 !important;
160 margin: 15px 0 3px 0 !important;
161 }
161 }
162
162
163 div.markdown-block h6 {
163 div.markdown-block h6 {
164 font-size: 12px;
164 font-size: 12px;
165 color: #777777;
165 color: #777777;
166 margin: 15px 0 3px 0 !important;
166 margin: 15px 0 3px 0 !important;
167 }
167 }
168
168
169 div.markdown-block hr {
169 div.markdown-block hr {
170 border: 0;
170 border: 0;
171 color: #e6e5e5;
171 color: #e6e5e5;
172 background-color: #e6e5e5;
172 background-color: #e6e5e5;
173 height: 3px;
173 height: 3px;
174 margin-bottom: 13px;
174 margin-bottom: 13px;
175 }
175 }
176
176
177 div.markdown-block ol,
177 div.markdown-block ol,
178 div.markdown-block ul,
178 div.markdown-block ul,
179 div.markdown-block p,
179 div.markdown-block p,
180 div.markdown-block blockquote,
180 div.markdown-block blockquote,
181 div.markdown-block dl,
181 div.markdown-block dl,
182 div.markdown-block li,
182 div.markdown-block li,
183 div.markdown-block table {
183 div.markdown-block table {
184 margin: 3px 0px 13px 0px !important;
184 margin: 3px 0px 13px 0px !important;
185 color: #424242 !important;
185 color: #424242 !important;
186 font-size: 13px !important;
186 font-size: 13px !important;
187 font-family: @text-regular;
187 font-family: @text-regular;
188 font-weight: normal !important;
188 font-weight: normal !important;
189 overflow: visible !important;
189 overflow: visible !important;
190 line-height: 140% !important;
190 line-height: 140% !important;
191 }
191 }
192
192
193 div.markdown-block pre {
193 div.markdown-block pre {
194 margin: 3px 0px 13px 0px !important;
194 margin: 3px 0px 13px 0px !important;
195 padding: .5em;
195 padding: .5em;
196 color: #424242 !important;
196 color: #424242 !important;
197 font-size: 13px !important;
197 font-size: 13px !important;
198 overflow: visible !important;
198 overflow: visible !important;
199 line-height: 140% !important;
199 line-height: 140% !important;
200 background-color: @grey7;
200 background-color: @grey7;
201 }
201 }
202
202
203 div.markdown-block img {
203 div.markdown-block img {
204 border-style: none;
204 border-style: none;
205 background-color: #fff;
205 background-color: #fff;
206 padding-right: 20px;
206 padding-right: 20px;
207 max-width: 100%;
207 }
208 }
208
209
209
210
210 div.markdown-block strong {
211 div.markdown-block strong {
211 font-weight: 600;
212 font-weight: 600;
212 margin: 0;
213 margin: 0;
213 }
214 }
214
215
215 div.markdown-block ul.checkbox,
216 div.markdown-block ul.checkbox,
216 div.markdown-block ol.checkbox {
217 div.markdown-block ol.checkbox {
217 padding-left: 20px !important;
218 padding-left: 20px !important;
218 margin-top: 0px !important;
219 margin-top: 0px !important;
219 margin-bottom: 18px !important;
220 margin-bottom: 18px !important;
220 }
221 }
221
222
222 div.markdown-block ul,
223 div.markdown-block ul,
223 div.markdown-block ol {
224 div.markdown-block ol {
224 padding-left: 30px !important;
225 padding-left: 30px !important;
225 margin-top: 0px !important;
226 margin-top: 0px !important;
226 margin-bottom: 18px !important;
227 margin-bottom: 18px !important;
227 }
228 }
228
229
229 div.markdown-block ul.checkbox li,
230 div.markdown-block ul.checkbox li,
230 div.markdown-block ol.checkbox li {
231 div.markdown-block ol.checkbox li {
231 list-style: none !important;
232 list-style: none !important;
232 margin: 6px !important;
233 margin: 6px !important;
233 padding: 0 !important;
234 padding: 0 !important;
234 }
235 }
235
236
236 div.markdown-block ul li,
237 div.markdown-block ul li,
237 div.markdown-block ol li {
238 div.markdown-block ol li {
238 list-style: disc !important;
239 list-style: disc !important;
239 margin: 6px !important;
240 margin: 6px !important;
240 padding: 0 !important;
241 padding: 0 !important;
241 }
242 }
242
243
243 div.markdown-block ol li {
244 div.markdown-block ol li {
244 list-style: decimal !important;
245 list-style: decimal !important;
245 }
246 }
246
247
247 /*
248 /*
248 div.markdown-block a,
249 div.markdown-block a,
249 div.markdown-block a:visited {
250 div.markdown-block a:visited {
250 color: #4183C4 !important;
251 color: #4183C4 !important;
251 background-color: inherit;
252 background-color: inherit;
252 text-decoration: none;
253 text-decoration: none;
253 }
254 }
254 */
255 */
255
256
256 div.markdown-block #message {
257 div.markdown-block #message {
257 .border-radius(@border-radius);
258 .border-radius(@border-radius);
258 border: @border-thickness solid @grey5;
259 border: @border-thickness solid @grey5;
259 display: block;
260 display: block;
260 width: 100%;
261 width: 100%;
261 height: 60px;
262 height: 60px;
262 margin: 6px 0px;
263 margin: 6px 0px;
263 }
264 }
264
265
265 div.markdown-block button,
266 div.markdown-block button,
266 div.markdown-block #ws {
267 div.markdown-block #ws {
267 font-size: @basefontsize;
268 font-size: @basefontsize;
268 padding: 4px 6px;
269 padding: 4px 6px;
269 .border-radius(@border-radius);
270 .border-radius(@border-radius);
270 border: @border-thickness solid @grey5;
271 border: @border-thickness solid @grey5;
271 background-color: @grey6;
272 background-color: @grey6;
272 }
273 }
273
274
274 div.markdown-block code,
275 div.markdown-block code,
275 div.markdown-block pre,
276 div.markdown-block pre,
276 div.markdown-block #ws,
277 div.markdown-block #ws,
277 div.markdown-block #message {
278 div.markdown-block #message {
278 font-family: @text-monospace;
279 font-family: @text-monospace;
279 font-size: 11px;
280 font-size: 11px;
280 .border-radius(@border-radius);
281 .border-radius(@border-radius);
281 background-color: white;
282 background-color: white;
282 color: @grey3;
283 color: @grey3;
283 }
284 }
284
285
285
286
286 div.markdown-block code {
287 div.markdown-block code {
287 border: @border-thickness solid @grey6;
288 border: @border-thickness solid @grey6;
288 margin: 0 2px;
289 margin: 0 2px;
289 padding: 0 5px;
290 padding: 0 5px;
290 }
291 }
291
292
292 div.markdown-block pre {
293 div.markdown-block pre {
293 border: @border-thickness solid @grey5;
294 border: @border-thickness solid @grey5;
294 overflow: auto;
295 overflow: auto;
295 padding: .5em;
296 padding: .5em;
296 background-color: @grey7;
297 background-color: @grey7;
297 }
298 }
298
299
299 div.markdown-block pre > code {
300 div.markdown-block pre > code {
300 border: 0;
301 border: 0;
301 margin: 0;
302 margin: 0;
302 padding: 0;
303 padding: 0;
303 }
304 }
304
305
305 /** RST STYLE **/
306 /** RST STYLE **/
306 div.rst-block {
307 div.rst-block {
307 clear: both;
308 clear: both;
308 overflow: hidden;
309 overflow: hidden;
309 margin: 0;
310 margin: 0;
310 padding: 3px 15px 3px;
311 padding: 3px 15px 3px;
311 }
312 }
312
313
313 div.rst-block h2 {
314 div.rst-block h2 {
314 font-weight: normal;
315 font-weight: normal;
315 }
316 }
316
317
317 div.rst-block h1,
318 div.rst-block h1,
318 div.rst-block h2,
319 div.rst-block h2,
319 div.rst-block h3,
320 div.rst-block h3,
320 div.rst-block h4,
321 div.rst-block h4,
321 div.rst-block h5,
322 div.rst-block h5,
322 div.rst-block h6 {
323 div.rst-block h6 {
323 border-bottom: 0 !important;
324 border-bottom: 0 !important;
324 margin: 0 !important;
325 margin: 0 !important;
325 padding: 0 !important;
326 padding: 0 !important;
326 line-height: 1.5em !important;
327 line-height: 1.5em !important;
327 }
328 }
328
329
329
330
330 div.rst-block h1:first-child {
331 div.rst-block h1:first-child {
331 padding-top: .25em !important;
332 padding-top: .25em !important;
332 }
333 }
333
334
334 div.rst-block h2,
335 div.rst-block h2,
335 div.rst-block h3 {
336 div.rst-block h3 {
336 margin: 1em 0 !important;
337 margin: 1em 0 !important;
337 }
338 }
338
339
339 div.rst-block h1,
340 div.rst-block h1,
340 div.rst-block h2 {
341 div.rst-block h2 {
341 border-bottom: 1px #e6e5e5 solid !important;
342 border-bottom: 1px #e6e5e5 solid !important;
342 }
343 }
343
344
344 div.rst-block h2 {
345 div.rst-block h2 {
345 margin-top: 1.5em !important;
346 margin-top: 1.5em !important;
346 padding-top: .5em !important;
347 padding-top: .5em !important;
347 }
348 }
348
349
349 div.rst-block p {
350 div.rst-block p {
350 color: black !important;
351 color: black !important;
351 margin: 1em 0 !important;
352 margin: 1em 0 !important;
352 line-height: 1.5em !important;
353 line-height: 1.5em !important;
353 }
354 }
354
355
355 div.rst-block ul {
356 div.rst-block ul {
356 list-style: disc !important;
357 list-style: disc !important;
357 margin: 1em 0 1em 2em !important;
358 margin: 1em 0 1em 2em !important;
358 clear: both;
359 clear: both;
359 }
360 }
360
361
361 div.rst-block ol {
362 div.rst-block ol {
362 list-style: decimal;
363 list-style: decimal;
363 margin: 1em 0 1em 2em !important;
364 margin: 1em 0 1em 2em !important;
364 }
365 }
365
366
366 div.rst-block pre,
367 div.rst-block pre,
367 div.rst-block code {
368 div.rst-block code {
368 font: 12px "Bitstream Vera Sans Mono","Courier",monospace;
369 font: 12px "Bitstream Vera Sans Mono","Courier",monospace;
369 }
370 }
370
371
371 div.rst-block code {
372 div.rst-block code {
372 font-size: 12px !important;
373 font-size: 12px !important;
373 background-color: ghostWhite !important;
374 background-color: ghostWhite !important;
374 color: #444 !important;
375 color: #444 !important;
375 padding: 0 .2em !important;
376 padding: 0 .2em !important;
376 border: 1px solid #dedede !important;
377 border: 1px solid #dedede !important;
377 }
378 }
378
379
379 div.rst-block pre code {
380 div.rst-block pre code {
380 padding: 0 !important;
381 padding: 0 !important;
381 font-size: 12px !important;
382 font-size: 12px !important;
382 background-color: #eee !important;
383 background-color: #eee !important;
383 border: none !important;
384 border: none !important;
384 }
385 }
385
386
386 div.rst-block pre {
387 div.rst-block pre {
387 margin: 1em 0;
388 margin: 1em 0;
388 padding: @padding;
389 padding: @padding;
389 border: 1px solid @grey6;
390 border: 1px solid @grey6;
390 .border-radius(@border-radius);
391 .border-radius(@border-radius);
391 overflow: auto;
392 overflow: auto;
392 font-size: 12px;
393 font-size: 12px;
393 color: #444;
394 color: #444;
394 background-color: @grey7;
395 background-color: @grey7;
395 }
396 }
396
397
397
398
@@ -1,510 +1,514 b''
1
1
2 // tables.less
2 // tables.less
3 // For use in RhodeCode application tables;
3 // For use in RhodeCode application tables;
4 // see style guide documentation for guidelines.
4 // see style guide documentation for guidelines.
5
5
6 // TABLES
6 // TABLES
7
7
8 .rctable,
8 .rctable,
9 table.rctable,
9 table.rctable,
10 table.dataTable {
10 table.dataTable {
11 clear:both;
11 clear:both;
12 width: 100%;
12 width: 100%;
13 margin: 0 auto @padding;
13 margin: 0 auto @padding;
14 padding: 0;
14 padding: 0;
15 vertical-align: baseline;
15 vertical-align: baseline;
16 line-height:1.5em;
16 line-height:1.5em;
17 border: none;
17 border: none;
18 outline: none;
18 outline: none;
19 border-collapse: collapse;
19 border-collapse: collapse;
20 border-spacing: 0;
20 border-spacing: 0;
21 color: @grey2;
21 color: @grey2;
22
22
23 b {
23 b {
24 font-weight: normal;
24 font-weight: normal;
25 }
25 }
26
26
27 em {
27 em {
28 font-weight: bold;
28 font-weight: bold;
29 font-style: normal;
29 font-style: normal;
30 }
30 }
31
31
32 th,
32 th,
33 td {
33 td {
34 height: auto;
34 height: auto;
35 max-width: 20%;
35 max-width: 20%;
36 padding: .65em 0 .65em 1em;
36 padding: .65em 0 .65em 1em;
37 vertical-align: middle;
37 vertical-align: middle;
38 border-bottom: @border-thickness solid @grey5;
38 border-bottom: @border-thickness solid @grey5;
39 white-space: normal;
39 white-space: normal;
40
40
41 &.td-radio,
41 &.td-radio,
42 &.td-checkbox {
42 &.td-checkbox {
43 padding-right: 0;
43 padding-right: 0;
44 text-align: center;
44 text-align: center;
45
45
46 input {
46 input {
47 margin: 0 1em;
47 margin: 0 1em;
48 }
48 }
49 }
49 }
50
50
51 &.truncate-wrap {
51 &.truncate-wrap {
52 white-space: nowrap !important;
52 white-space: nowrap !important;
53 }
53 }
54
54
55 pre {
55 pre {
56 margin: 0;
56 margin: 0;
57 }
57 }
58
58
59 .show_more {
59 .show_more {
60 height: inherit;
60 height: inherit;
61 }
61 }
62 }
62 }
63
63
64 .expired td {
64 .expired td {
65 background-color: @grey7;
65 background-color: @grey7;
66 }
66 }
67 .inactive td {
67 .inactive td {
68 background-color: @grey6;
68 background-color: @grey6;
69 }
69 }
70 th {
70 th {
71 text-align: left;
71 text-align: left;
72 font-weight: @text-semibold-weight;
72 font-weight: @text-semibold-weight;
73 font-family: @text-semibold;
73 font-family: @text-semibold;
74 }
74 }
75
75
76 .hl {
76 .hl {
77 td {
77 td {
78 background-color: lighten(@alert4,25%);
78 background-color: lighten(@alert4,25%);
79 }
79 }
80 }
80 }
81
81
82 // Special Data Cell Types
82 // Special Data Cell Types
83 // See style guide for desciptions and examples.
83 // See style guide for desciptions and examples.
84
84
85 td {
85 td {
86
86
87 &.user {
87 &.user {
88 padding-left: 1em;
88 padding-left: 1em;
89 }
89 }
90
90
91 &.td-rss {
91 &.td-rss {
92 width: 20px;
92 width: 20px;
93 min-width: 0;
93 min-width: 0;
94 margin: 0;
94 margin: 0;
95 }
95 }
96
96
97 &.quick_repo_menu {
97 &.quick_repo_menu {
98 width: 15px;
98 width: 15px;
99 text-align: center;
99 text-align: center;
100
100
101 &:hover {
101 &:hover {
102 background-color: @grey5;
102 background-color: @grey5;
103 }
103 }
104 }
104 }
105
105
106 &.td-icon {
106 &.td-icon {
107 min-width: 20px;
107 min-width: 20px;
108 width: 20px;
108 width: 20px;
109 }
109 }
110
110
111 &.td-hash {
111 &.td-hash {
112 min-width: 80px;
112 min-width: 80px;
113 width: 200px;
113 width: 200px;
114
114
115 .obsolete {
115 .obsolete {
116 text-decoration: line-through;
116 text-decoration: line-through;
117 color: lighten(@grey2,25%);
117 color: lighten(@grey2,25%);
118 }
118 }
119 }
119 }
120
120
121 &.td-sha {
122 white-space: nowrap;
123 }
124
121 &.td-graphbox {
125 &.td-graphbox {
122 width: 100px;
126 width: 100px;
123 max-width: 100px;
127 max-width: 100px;
124 min-width: 100px;
128 min-width: 100px;
125 }
129 }
126
130
127 &.td-time {
131 &.td-time {
128 width: 160px;
132 width: 160px;
129 white-space: nowrap;
133 white-space: nowrap;
130 }
134 }
131
135
132 &.annotate{
136 &.annotate{
133 padding-right: 0;
137 padding-right: 0;
134
138
135 div.annotatediv{
139 div.annotatediv{
136 margin: 0 0.7em;
140 margin: 0 0.7em;
137 }
141 }
138 }
142 }
139
143
140 &.tags-col {
144 &.tags-col {
141 padding-right: 0;
145 padding-right: 0;
142 }
146 }
143
147
144 &.td-description {
148 &.td-description {
145 min-width: 350px;
149 min-width: 350px;
146
150
147 &.truncate, .truncate-wrap {
151 &.truncate, .truncate-wrap {
148 white-space: nowrap;
152 white-space: nowrap;
149 overflow: hidden;
153 overflow: hidden;
150 text-overflow: ellipsis;
154 text-overflow: ellipsis;
151 max-width: 350px;
155 max-width: 350px;
152 }
156 }
153 }
157 }
154
158
155 &.td-grid-name {
159 &.td-grid-name {
156 white-space: nowrap;
160 white-space: nowrap;
157 min-width: 300px;
161 min-width: 300px;
158 }
162 }
159
163
160 &.td-componentname {
164 &.td-componentname {
161 white-space: nowrap;
165 white-space: nowrap;
162 }
166 }
163
167
164 &.td-name {
168 &.td-name {
165
169
166 }
170 }
167
171
168 &.td-journalaction {
172 &.td-journalaction {
169 min-width: 300px;
173 min-width: 300px;
170
174
171 .journal_action_params {
175 .journal_action_params {
172 // waiting for feedback
176 // waiting for feedback
173 }
177 }
174 }
178 }
175
179
176 &.td-active {
180 &.td-active {
177 padding-left: .65em;
181 padding-left: .65em;
178 }
182 }
179
183
180 &.td-url {
184 &.td-url {
181 white-space: nowrap;
185 white-space: nowrap;
182 }
186 }
183
187
184 &.td-comments {
188 &.td-comments {
185 min-width: 3em;
189 min-width: 3em;
186 }
190 }
187
191
188 &.td-buttons {
192 &.td-buttons {
189 padding: .3em 0;
193 padding: .3em 0;
190 }
194 }
191 &.td-align-top {
195 &.td-align-top {
192 vertical-align: text-top
196 vertical-align: text-top
193 }
197 }
194 &.td-action {
198 &.td-action {
195 // this is for the remove/delete/edit buttons
199 // this is for the remove/delete/edit buttons
196 padding-right: 0;
200 padding-right: 0;
197 min-width: 95px;
201 min-width: 95px;
198 text-transform: capitalize;
202 text-transform: capitalize;
199
203
200 i {
204 i {
201 display: none;
205 display: none;
202 }
206 }
203 }
207 }
204
208
205 // TODO: lisa: this needs to be cleaned up with the buttons
209 // TODO: lisa: this needs to be cleaned up with the buttons
206 .grid_edit,
210 .grid_edit,
207 .grid_delete {
211 .grid_delete {
208 display: inline-block;
212 display: inline-block;
209 margin: 0 @padding/3 0 0;
213 margin: 0 @padding/3 0 0;
210 font-family: @text-light;
214 font-family: @text-light;
211
215
212 i {
216 i {
213 display: none;
217 display: none;
214 }
218 }
215 }
219 }
216
220
217 .grid_edit + .grid_delete {
221 .grid_edit + .grid_delete {
218 border-left: @border-thickness solid @grey5;
222 border-left: @border-thickness solid @grey5;
219 padding-left: @padding/2;
223 padding-left: @padding/2;
220 }
224 }
221
225
222 &.td-compare {
226 &.td-compare {
223
227
224 input {
228 input {
225 margin-right: 1em;
229 margin-right: 1em;
226 }
230 }
227
231
228 .compare-radio-button {
232 .compare-radio-button {
229 margin: 0 1em 0 0;
233 margin: 0 1em 0 0;
230 }
234 }
231
235
232
236
233 }
237 }
234
238
235 &.td-tags {
239 &.td-tags {
236 padding: .5em 1em .5em 0;
240 padding: .5em 1em .5em 0;
237 width: 140px;
241 width: 140px;
238
242
239 .tag {
243 .tag {
240 margin: 1px;
244 margin: 1px;
241 float: left;
245 float: left;
242 }
246 }
243 }
247 }
244
248
245 .icon-svn, .icon-hg, .icon-git {
249 .icon-svn, .icon-hg, .icon-git {
246 font-size: 1.4em;
250 font-size: 1.4em;
247 }
251 }
248
252
249 &.collapse_commit,
253 &.collapse_commit,
250 &.expand_commit {
254 &.expand_commit {
251 padding-right: 0;
255 padding-right: 0;
252 padding-left: 1em;
256 padding-left: 1em;
253 cursor: pointer;
257 cursor: pointer;
254 width: 20px;
258 width: 20px;
255 }
259 }
256 }
260 }
257
261
258 .perm_admin_row {
262 .perm_admin_row {
259 color: @grey4;
263 color: @grey4;
260 background-color: @grey6;
264 background-color: @grey6;
261 }
265 }
262
266
263 .noborder {
267 .noborder {
264 border: none;
268 border: none;
265
269
266 td {
270 td {
267 border: none;
271 border: none;
268 }
272 }
269 }
273 }
270 }
274 }
271 .rctable.audit-log {
275 .rctable.audit-log {
272 td {
276 td {
273 vertical-align: top;
277 vertical-align: top;
274 }
278 }
275 }
279 }
276
280
277 // TRUNCATING
281 // TRUNCATING
278 // TODO: lisaq: should this possibly be moved out of tables.less?
282 // TODO: lisaq: should this possibly be moved out of tables.less?
279 // for truncated text
283 // for truncated text
280 // used inside of table cells and in code block headers
284 // used inside of table cells and in code block headers
281 .truncate-wrap {
285 .truncate-wrap {
282 white-space: nowrap !important;
286 white-space: nowrap !important;
283
287
284 //truncated text
288 //truncated text
285 .truncate {
289 .truncate {
286 max-width: 450px;
290 max-width: 450px;
287 width: 300px;
291 width: 300px;
288 overflow: hidden;
292 overflow: hidden;
289 text-overflow: ellipsis;
293 text-overflow: ellipsis;
290 -o-text-overflow: ellipsis;
294 -o-text-overflow: ellipsis;
291 -ms-text-overflow: ellipsis;
295 -ms-text-overflow: ellipsis;
292
296
293 &.autoexpand {
297 &.autoexpand {
294 width: 120px;
298 width: 120px;
295 margin-right: 200px;
299 margin-right: 200px;
296 }
300 }
297 }
301 }
298 &:hover .truncate.autoexpand {
302 &:hover .truncate.autoexpand {
299 overflow: visible;
303 overflow: visible;
300 }
304 }
301
305
302 .tags-truncate {
306 .tags-truncate {
303 width: 150px;
307 width: 150px;
304 height: 22px;
308 height: 22px;
305 overflow: hidden;
309 overflow: hidden;
306
310
307 .tag {
311 .tag {
308 display: inline-block;
312 display: inline-block;
309 }
313 }
310
314
311 &.truncate {
315 &.truncate {
312 height: 22px;
316 height: 22px;
313 max-height:2em;
317 max-height:2em;
314 width: 140px;
318 width: 140px;
315 }
319 }
316 }
320 }
317 }
321 }
318
322
319 .apikeys_wrap {
323 .apikeys_wrap {
320 margin-bottom: @padding;
324 margin-bottom: @padding;
321
325
322 table.rctable td:first-child {
326 table.rctable td:first-child {
323 width: 340px;
327 width: 340px;
324 }
328 }
325 }
329 }
326
330
327
331
328
332
329 // SPECIAL CASES
333 // SPECIAL CASES
330
334
331 // Repository Followers
335 // Repository Followers
332 table.rctable.followers_data {
336 table.rctable.followers_data {
333 width: 75%;
337 width: 75%;
334 margin: 0;
338 margin: 0;
335 }
339 }
336
340
337 // Repository List
341 // Repository List
338 // Group Members List
342 // Group Members List
339 table.rctable.group_members,
343 table.rctable.group_members,
340 table#repo_list_table {
344 table#repo_list_table {
341 min-width: 600px;
345 min-width: 600px;
342 }
346 }
343
347
344 // Keyboard mappings
348 // Keyboard mappings
345 table.keyboard-mappings {
349 table.keyboard-mappings {
346 th {
350 th {
347 text-align: left;
351 text-align: left;
348 font-weight: @text-semibold-weight;
352 font-weight: @text-semibold-weight;
349 font-family: @text-semibold;
353 font-family: @text-semibold;
350 }
354 }
351 }
355 }
352
356
353 // Branches, Tags, and Bookmarks
357 // Branches, Tags, and Bookmarks
354 #obj_list_table.dataTable {
358 #obj_list_table.dataTable {
355 td.td-time {
359 td.td-time {
356 padding-right: 1em;
360 padding-right: 1em;
357 }
361 }
358 }
362 }
359
363
360 // User Admin
364 // User Admin
361 .rctable.useremails,
365 .rctable.useremails,
362 .rctable.account_emails {
366 .rctable.account_emails {
363 .tag,
367 .tag,
364 .btn {
368 .btn {
365 float: right;
369 float: right;
366 }
370 }
367 .btn { //to line up with tags
371 .btn { //to line up with tags
368 margin-right: 1.65em;
372 margin-right: 1.65em;
369 }
373 }
370 }
374 }
371
375
372 // User List
376 // User List
373 #user_list_table {
377 #user_list_table {
374
378
375 td.td-user {
379 td.td-user {
376 min-width: 100px;
380 min-width: 100px;
377 }
381 }
378 }
382 }
379
383
380 // Pull Request List Table
384 // Pull Request List Table
381 #pull_request_list_table.dataTable {
385 #pull_request_list_table.dataTable {
382
386
383 //TODO: lisa: This needs to be removed once the description is adjusted
387 //TODO: lisa: This needs to be removed once the description is adjusted
384 // for using an expand_commit button (see issue 765)
388 // for using an expand_commit button (see issue 765)
385 td {
389 td {
386 vertical-align: middle;
390 vertical-align: middle;
387 }
391 }
388 }
392 }
389
393
390 // Settings (no border)
394 // Settings (no border)
391 table.rctable.dl-settings {
395 table.rctable.dl-settings {
392 td {
396 td {
393 border: none;
397 border: none;
394 vertical-align: baseline;
398 vertical-align: baseline;
395 }
399 }
396 }
400 }
397
401
398
402
399 // Statistics
403 // Statistics
400 table.trending_language_tbl {
404 table.trending_language_tbl {
401 width: 100%;
405 width: 100%;
402 line-height: 1em;
406 line-height: 1em;
403
407
404 td div {
408 td div {
405 overflow: visible;
409 overflow: visible;
406 }
410 }
407 }
411 }
408
412
409 .trending_language_tbl, .trending_language_tbl td {
413 .trending_language_tbl, .trending_language_tbl td {
410 border: 0;
414 border: 0;
411 margin: 0;
415 margin: 0;
412 padding: 0;
416 padding: 0;
413 background: transparent;
417 background: transparent;
414 }
418 }
415
419
416 .trending_language_tbl, .trending_language_tbl tr {
420 .trending_language_tbl, .trending_language_tbl tr {
417 border-spacing: 0 3px;
421 border-spacing: 0 3px;
418 }
422 }
419
423
420 .trending_language {
424 .trending_language {
421 position: relative;
425 position: relative;
422 overflow: hidden;
426 overflow: hidden;
423 color: @text-color;
427 color: @text-color;
424 width: 400px;
428 width: 400px;
425
429
426 .lang-bar {
430 .lang-bar {
427 z-index: 1;
431 z-index: 1;
428 overflow: hidden;
432 overflow: hidden;
429 background-color: @rcblue;
433 background-color: @rcblue;
430 color: #FFF;
434 color: #FFF;
431 text-decoration: none;
435 text-decoration: none;
432 }
436 }
433
437
434 }
438 }
435
439
436 // Changesets
440 // Changesets
437 #changesets.rctable {
441 #changesets.rctable {
438 th {
442 th {
439 padding: 0 1em 0.65em 0;
443 padding: 0 1em 0.65em 0;
440 }
444 }
441
445
442 // td must be fixed height for graph
446 // td must be fixed height for graph
443 td {
447 td {
444 height: 32px;
448 height: 32px;
445 padding: 0 1em 0 0;
449 padding: 0 1em 0 0;
446 vertical-align: middle;
450 vertical-align: middle;
447 white-space: nowrap;
451 white-space: nowrap;
448
452
449 &.td-description {
453 &.td-description {
450 white-space: normal;
454 white-space: normal;
451 }
455 }
452
456
453 &.expand_commit {
457 &.expand_commit {
454 padding-right: 0;
458 padding-right: 0;
455 cursor: pointer;
459 cursor: pointer;
456 width: 20px;
460 width: 20px;
457 }
461 }
458 }
462 }
459 }
463 }
460
464
461 // Compare
465 // Compare
462 table.compare_view_commits {
466 table.compare_view_commits {
463 margin-top: @space;
467 margin-top: @space;
464
468
465 td.td-time {
469 td.td-time {
466 padding-left: .5em;
470 padding-left: .5em;
467 }
471 }
468
472
469 // special case to not show hover actions on hidden indicator
473 // special case to not show hover actions on hidden indicator
470 tr.compare_select_hidden:hover {
474 tr.compare_select_hidden:hover {
471 cursor: inherit;
475 cursor: inherit;
472
476
473 td {
477 td {
474 background-color: inherit;
478 background-color: inherit;
475 }
479 }
476 }
480 }
477
481
478 tr:hover {
482 tr:hover {
479 cursor: pointer;
483 cursor: pointer;
480
484
481 td {
485 td {
482 background-color: lighten(@alert4,25%);
486 background-color: lighten(@alert4,25%);
483 }
487 }
484 }
488 }
485
489
486
490
487 }
491 }
488
492
489 .file_history {
493 .file_history {
490 td.td-actions {
494 td.td-actions {
491 text-align: right;
495 text-align: right;
492 }
496 }
493 }
497 }
494
498
495
499
496 // Gist List
500 // Gist List
497 #gist_list_table {
501 #gist_list_table {
498 td {
502 td {
499 vertical-align: middle;
503 vertical-align: middle;
500
504
501 div{
505 div{
502 display: inline-block;
506 display: inline-block;
503 vertical-align: middle;
507 vertical-align: middle;
504 }
508 }
505
509
506 img{
510 img{
507 vertical-align: middle;
511 vertical-align: middle;
508 }
512 }
509 }
513 }
510 }
514 }
@@ -1,380 +1,381 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('favicon', '/favicon.ico', []);
15 pyroutes.register('favicon', '/favicon.ico', []);
16 pyroutes.register('robots', '/robots.txt', []);
16 pyroutes.register('robots', '/robots.txt', []);
17 pyroutes.register('auth_home', '/_admin/auth*traverse', []);
17 pyroutes.register('auth_home', '/_admin/auth*traverse', []);
18 pyroutes.register('global_integrations_new', '/_admin/integrations/new', []);
18 pyroutes.register('global_integrations_new', '/_admin/integrations/new', []);
19 pyroutes.register('global_integrations_home', '/_admin/integrations', []);
19 pyroutes.register('global_integrations_home', '/_admin/integrations', []);
20 pyroutes.register('global_integrations_list', '/_admin/integrations/%(integration)s', ['integration']);
20 pyroutes.register('global_integrations_list', '/_admin/integrations/%(integration)s', ['integration']);
21 pyroutes.register('global_integrations_create', '/_admin/integrations/%(integration)s/new', ['integration']);
21 pyroutes.register('global_integrations_create', '/_admin/integrations/%(integration)s/new', ['integration']);
22 pyroutes.register('global_integrations_edit', '/_admin/integrations/%(integration)s/%(integration_id)s', ['integration', 'integration_id']);
22 pyroutes.register('global_integrations_edit', '/_admin/integrations/%(integration)s/%(integration_id)s', ['integration', 'integration_id']);
23 pyroutes.register('repo_group_integrations_home', '/%(repo_group_name)s/_settings/integrations', ['repo_group_name']);
23 pyroutes.register('repo_group_integrations_home', '/%(repo_group_name)s/_settings/integrations', ['repo_group_name']);
24 pyroutes.register('repo_group_integrations_new', '/%(repo_group_name)s/_settings/integrations/new', ['repo_group_name']);
24 pyroutes.register('repo_group_integrations_new', '/%(repo_group_name)s/_settings/integrations/new', ['repo_group_name']);
25 pyroutes.register('repo_group_integrations_list', '/%(repo_group_name)s/_settings/integrations/%(integration)s', ['repo_group_name', 'integration']);
25 pyroutes.register('repo_group_integrations_list', '/%(repo_group_name)s/_settings/integrations/%(integration)s', ['repo_group_name', 'integration']);
26 pyroutes.register('repo_group_integrations_create', '/%(repo_group_name)s/_settings/integrations/%(integration)s/new', ['repo_group_name', 'integration']);
26 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_edit', '/%(repo_group_name)s/_settings/integrations/%(integration)s/%(integration_id)s', ['repo_group_name', 'integration', 'integration_id']);
27 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_integrations_home', '/%(repo_name)s/settings/integrations', ['repo_name']);
28 pyroutes.register('repo_integrations_home', '/%(repo_name)s/settings/integrations', ['repo_name']);
29 pyroutes.register('repo_integrations_new', '/%(repo_name)s/settings/integrations/new', ['repo_name']);
29 pyroutes.register('repo_integrations_new', '/%(repo_name)s/settings/integrations/new', ['repo_name']);
30 pyroutes.register('repo_integrations_list', '/%(repo_name)s/settings/integrations/%(integration)s', ['repo_name', 'integration']);
30 pyroutes.register('repo_integrations_list', '/%(repo_name)s/settings/integrations/%(integration)s', ['repo_name', 'integration']);
31 pyroutes.register('repo_integrations_create', '/%(repo_name)s/settings/integrations/%(integration)s/new', ['repo_name', 'integration']);
31 pyroutes.register('repo_integrations_create', '/%(repo_name)s/settings/integrations/%(integration)s/new', ['repo_name', 'integration']);
32 pyroutes.register('repo_integrations_edit', '/%(repo_name)s/settings/integrations/%(integration)s/%(integration_id)s', ['repo_name', 'integration', 'integration_id']);
32 pyroutes.register('repo_integrations_edit', '/%(repo_name)s/settings/integrations/%(integration)s/%(integration_id)s', ['repo_name', 'integration', 'integration_id']);
33 pyroutes.register('ops_ping', '/_admin/ops/ping', []);
33 pyroutes.register('ops_ping', '/_admin/ops/ping', []);
34 pyroutes.register('ops_error_test', '/_admin/ops/error', []);
34 pyroutes.register('ops_error_test', '/_admin/ops/error', []);
35 pyroutes.register('ops_redirect_test', '/_admin/ops/redirect', []);
35 pyroutes.register('ops_redirect_test', '/_admin/ops/redirect', []);
36 pyroutes.register('ops_ping_legacy', '/_admin/ping', []);
36 pyroutes.register('ops_ping_legacy', '/_admin/ping', []);
37 pyroutes.register('ops_error_test_legacy', '/_admin/error_test', []);
37 pyroutes.register('ops_error_test_legacy', '/_admin/error_test', []);
38 pyroutes.register('admin_home', '/_admin', []);
38 pyroutes.register('admin_home', '/_admin', []);
39 pyroutes.register('admin_audit_logs', '/_admin/audit_logs', []);
39 pyroutes.register('admin_audit_logs', '/_admin/audit_logs', []);
40 pyroutes.register('admin_audit_log_entry', '/_admin/audit_logs/%(audit_log_id)s', ['audit_log_id']);
40 pyroutes.register('admin_audit_log_entry', '/_admin/audit_logs/%(audit_log_id)s', ['audit_log_id']);
41 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']);
42 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']);
43 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']);
44 pyroutes.register('admin_settings_open_source', '/_admin/settings/open_source', []);
44 pyroutes.register('admin_settings_open_source', '/_admin/settings/open_source', []);
45 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', []);
46 pyroutes.register('admin_settings_system', '/_admin/settings/system', []);
46 pyroutes.register('admin_settings_system', '/_admin/settings/system', []);
47 pyroutes.register('admin_settings_system_update', '/_admin/settings/system/updates', []);
47 pyroutes.register('admin_settings_system_update', '/_admin/settings/system/updates', []);
48 pyroutes.register('admin_settings_exception_tracker', '/_admin/settings/exceptions', []);
48 pyroutes.register('admin_settings_exception_tracker', '/_admin/settings/exceptions', []);
49 pyroutes.register('admin_settings_exception_tracker_delete_all', '/_admin/settings/exceptions/delete', []);
49 pyroutes.register('admin_settings_exception_tracker_delete_all', '/_admin/settings/exceptions/delete', []);
50 pyroutes.register('admin_settings_exception_tracker_show', '/_admin/settings/exceptions/%(exception_id)s', ['exception_id']);
50 pyroutes.register('admin_settings_exception_tracker_show', '/_admin/settings/exceptions/%(exception_id)s', ['exception_id']);
51 pyroutes.register('admin_settings_exception_tracker_delete', '/_admin/settings/exceptions/%(exception_id)s/delete', ['exception_id']);
51 pyroutes.register('admin_settings_exception_tracker_delete', '/_admin/settings/exceptions/%(exception_id)s/delete', ['exception_id']);
52 pyroutes.register('admin_settings_sessions', '/_admin/settings/sessions', []);
52 pyroutes.register('admin_settings_sessions', '/_admin/settings/sessions', []);
53 pyroutes.register('admin_settings_sessions_cleanup', '/_admin/settings/sessions/cleanup', []);
53 pyroutes.register('admin_settings_sessions_cleanup', '/_admin/settings/sessions/cleanup', []);
54 pyroutes.register('admin_settings_process_management', '/_admin/settings/process_management', []);
54 pyroutes.register('admin_settings_process_management', '/_admin/settings/process_management', []);
55 pyroutes.register('admin_settings_process_management_data', '/_admin/settings/process_management/data', []);
55 pyroutes.register('admin_settings_process_management_data', '/_admin/settings/process_management/data', []);
56 pyroutes.register('admin_settings_process_management_signal', '/_admin/settings/process_management/signal', []);
56 pyroutes.register('admin_settings_process_management_signal', '/_admin/settings/process_management/signal', []);
57 pyroutes.register('admin_settings_process_management_master_signal', '/_admin/settings/process_management/master_signal', []);
57 pyroutes.register('admin_settings_process_management_master_signal', '/_admin/settings/process_management/master_signal', []);
58 pyroutes.register('admin_defaults_repositories', '/_admin/defaults/repositories', []);
58 pyroutes.register('admin_defaults_repositories', '/_admin/defaults/repositories', []);
59 pyroutes.register('admin_defaults_repositories_update', '/_admin/defaults/repositories/update', []);
59 pyroutes.register('admin_defaults_repositories_update', '/_admin/defaults/repositories/update', []);
60 pyroutes.register('admin_settings', '/_admin/settings', []);
60 pyroutes.register('admin_settings', '/_admin/settings', []);
61 pyroutes.register('admin_settings_update', '/_admin/settings/update', []);
61 pyroutes.register('admin_settings_update', '/_admin/settings/update', []);
62 pyroutes.register('admin_settings_global', '/_admin/settings/global', []);
62 pyroutes.register('admin_settings_global', '/_admin/settings/global', []);
63 pyroutes.register('admin_settings_global_update', '/_admin/settings/global/update', []);
63 pyroutes.register('admin_settings_global_update', '/_admin/settings/global/update', []);
64 pyroutes.register('admin_settings_vcs', '/_admin/settings/vcs', []);
64 pyroutes.register('admin_settings_vcs', '/_admin/settings/vcs', []);
65 pyroutes.register('admin_settings_vcs_update', '/_admin/settings/vcs/update', []);
65 pyroutes.register('admin_settings_vcs_update', '/_admin/settings/vcs/update', []);
66 pyroutes.register('admin_settings_vcs_svn_pattern_delete', '/_admin/settings/vcs/svn_pattern_delete', []);
66 pyroutes.register('admin_settings_vcs_svn_pattern_delete', '/_admin/settings/vcs/svn_pattern_delete', []);
67 pyroutes.register('admin_settings_mapping', '/_admin/settings/mapping', []);
67 pyroutes.register('admin_settings_mapping', '/_admin/settings/mapping', []);
68 pyroutes.register('admin_settings_mapping_update', '/_admin/settings/mapping/update', []);
68 pyroutes.register('admin_settings_mapping_update', '/_admin/settings/mapping/update', []);
69 pyroutes.register('admin_settings_visual', '/_admin/settings/visual', []);
69 pyroutes.register('admin_settings_visual', '/_admin/settings/visual', []);
70 pyroutes.register('admin_settings_visual_update', '/_admin/settings/visual/update', []);
70 pyroutes.register('admin_settings_visual_update', '/_admin/settings/visual/update', []);
71 pyroutes.register('admin_settings_issuetracker', '/_admin/settings/issue-tracker', []);
71 pyroutes.register('admin_settings_issuetracker', '/_admin/settings/issue-tracker', []);
72 pyroutes.register('admin_settings_issuetracker_update', '/_admin/settings/issue-tracker/update', []);
72 pyroutes.register('admin_settings_issuetracker_update', '/_admin/settings/issue-tracker/update', []);
73 pyroutes.register('admin_settings_issuetracker_test', '/_admin/settings/issue-tracker/test', []);
73 pyroutes.register('admin_settings_issuetracker_test', '/_admin/settings/issue-tracker/test', []);
74 pyroutes.register('admin_settings_issuetracker_delete', '/_admin/settings/issue-tracker/delete', []);
74 pyroutes.register('admin_settings_issuetracker_delete', '/_admin/settings/issue-tracker/delete', []);
75 pyroutes.register('admin_settings_email', '/_admin/settings/email', []);
75 pyroutes.register('admin_settings_email', '/_admin/settings/email', []);
76 pyroutes.register('admin_settings_email_update', '/_admin/settings/email/update', []);
76 pyroutes.register('admin_settings_email_update', '/_admin/settings/email/update', []);
77 pyroutes.register('admin_settings_hooks', '/_admin/settings/hooks', []);
77 pyroutes.register('admin_settings_hooks', '/_admin/settings/hooks', []);
78 pyroutes.register('admin_settings_hooks_update', '/_admin/settings/hooks/update', []);
78 pyroutes.register('admin_settings_hooks_update', '/_admin/settings/hooks/update', []);
79 pyroutes.register('admin_settings_hooks_delete', '/_admin/settings/hooks/delete', []);
79 pyroutes.register('admin_settings_hooks_delete', '/_admin/settings/hooks/delete', []);
80 pyroutes.register('admin_settings_search', '/_admin/settings/search', []);
80 pyroutes.register('admin_settings_search', '/_admin/settings/search', []);
81 pyroutes.register('admin_settings_labs', '/_admin/settings/labs', []);
81 pyroutes.register('admin_settings_labs', '/_admin/settings/labs', []);
82 pyroutes.register('admin_settings_labs_update', '/_admin/settings/labs/update', []);
82 pyroutes.register('admin_settings_labs_update', '/_admin/settings/labs/update', []);
83 pyroutes.register('admin_permissions_application', '/_admin/permissions/application', []);
83 pyroutes.register('admin_permissions_application', '/_admin/permissions/application', []);
84 pyroutes.register('admin_permissions_application_update', '/_admin/permissions/application/update', []);
84 pyroutes.register('admin_permissions_application_update', '/_admin/permissions/application/update', []);
85 pyroutes.register('admin_permissions_global', '/_admin/permissions/global', []);
85 pyroutes.register('admin_permissions_global', '/_admin/permissions/global', []);
86 pyroutes.register('admin_permissions_global_update', '/_admin/permissions/global/update', []);
86 pyroutes.register('admin_permissions_global_update', '/_admin/permissions/global/update', []);
87 pyroutes.register('admin_permissions_object', '/_admin/permissions/object', []);
87 pyroutes.register('admin_permissions_object', '/_admin/permissions/object', []);
88 pyroutes.register('admin_permissions_object_update', '/_admin/permissions/object/update', []);
88 pyroutes.register('admin_permissions_object_update', '/_admin/permissions/object/update', []);
89 pyroutes.register('admin_permissions_ips', '/_admin/permissions/ips', []);
89 pyroutes.register('admin_permissions_ips', '/_admin/permissions/ips', []);
90 pyroutes.register('admin_permissions_overview', '/_admin/permissions/overview', []);
90 pyroutes.register('admin_permissions_overview', '/_admin/permissions/overview', []);
91 pyroutes.register('admin_permissions_auth_token_access', '/_admin/permissions/auth_token_access', []);
91 pyroutes.register('admin_permissions_auth_token_access', '/_admin/permissions/auth_token_access', []);
92 pyroutes.register('admin_permissions_ssh_keys', '/_admin/permissions/ssh_keys', []);
92 pyroutes.register('admin_permissions_ssh_keys', '/_admin/permissions/ssh_keys', []);
93 pyroutes.register('admin_permissions_ssh_keys_data', '/_admin/permissions/ssh_keys/data', []);
93 pyroutes.register('admin_permissions_ssh_keys_data', '/_admin/permissions/ssh_keys/data', []);
94 pyroutes.register('admin_permissions_ssh_keys_update', '/_admin/permissions/ssh_keys/update', []);
94 pyroutes.register('admin_permissions_ssh_keys_update', '/_admin/permissions/ssh_keys/update', []);
95 pyroutes.register('users', '/_admin/users', []);
95 pyroutes.register('users', '/_admin/users', []);
96 pyroutes.register('users_data', '/_admin/users_data', []);
96 pyroutes.register('users_data', '/_admin/users_data', []);
97 pyroutes.register('users_create', '/_admin/users/create', []);
97 pyroutes.register('users_create', '/_admin/users/create', []);
98 pyroutes.register('users_new', '/_admin/users/new', []);
98 pyroutes.register('users_new', '/_admin/users/new', []);
99 pyroutes.register('user_edit', '/_admin/users/%(user_id)s/edit', ['user_id']);
99 pyroutes.register('user_edit', '/_admin/users/%(user_id)s/edit', ['user_id']);
100 pyroutes.register('user_edit_advanced', '/_admin/users/%(user_id)s/edit/advanced', ['user_id']);
100 pyroutes.register('user_edit_advanced', '/_admin/users/%(user_id)s/edit/advanced', ['user_id']);
101 pyroutes.register('user_edit_global_perms', '/_admin/users/%(user_id)s/edit/global_permissions', ['user_id']);
101 pyroutes.register('user_edit_global_perms', '/_admin/users/%(user_id)s/edit/global_permissions', ['user_id']);
102 pyroutes.register('user_edit_global_perms_update', '/_admin/users/%(user_id)s/edit/global_permissions/update', ['user_id']);
102 pyroutes.register('user_edit_global_perms_update', '/_admin/users/%(user_id)s/edit/global_permissions/update', ['user_id']);
103 pyroutes.register('user_update', '/_admin/users/%(user_id)s/update', ['user_id']);
103 pyroutes.register('user_update', '/_admin/users/%(user_id)s/update', ['user_id']);
104 pyroutes.register('user_delete', '/_admin/users/%(user_id)s/delete', ['user_id']);
104 pyroutes.register('user_delete', '/_admin/users/%(user_id)s/delete', ['user_id']);
105 pyroutes.register('user_enable_force_password_reset', '/_admin/users/%(user_id)s/password_reset_enable', ['user_id']);
105 pyroutes.register('user_enable_force_password_reset', '/_admin/users/%(user_id)s/password_reset_enable', ['user_id']);
106 pyroutes.register('user_disable_force_password_reset', '/_admin/users/%(user_id)s/password_reset_disable', ['user_id']);
106 pyroutes.register('user_disable_force_password_reset', '/_admin/users/%(user_id)s/password_reset_disable', ['user_id']);
107 pyroutes.register('user_create_personal_repo_group', '/_admin/users/%(user_id)s/create_repo_group', ['user_id']);
107 pyroutes.register('user_create_personal_repo_group', '/_admin/users/%(user_id)s/create_repo_group', ['user_id']);
108 pyroutes.register('edit_user_auth_tokens_delete', '/_admin/users/%(user_id)s/edit/auth_tokens/delete', ['user_id']);
108 pyroutes.register('edit_user_auth_tokens_delete', '/_admin/users/%(user_id)s/edit/auth_tokens/delete', ['user_id']);
109 pyroutes.register('edit_user_ssh_keys', '/_admin/users/%(user_id)s/edit/ssh_keys', ['user_id']);
109 pyroutes.register('edit_user_ssh_keys', '/_admin/users/%(user_id)s/edit/ssh_keys', ['user_id']);
110 pyroutes.register('edit_user_ssh_keys_generate_keypair', '/_admin/users/%(user_id)s/edit/ssh_keys/generate', ['user_id']);
110 pyroutes.register('edit_user_ssh_keys_generate_keypair', '/_admin/users/%(user_id)s/edit/ssh_keys/generate', ['user_id']);
111 pyroutes.register('edit_user_ssh_keys_add', '/_admin/users/%(user_id)s/edit/ssh_keys/new', ['user_id']);
111 pyroutes.register('edit_user_ssh_keys_add', '/_admin/users/%(user_id)s/edit/ssh_keys/new', ['user_id']);
112 pyroutes.register('edit_user_ssh_keys_delete', '/_admin/users/%(user_id)s/edit/ssh_keys/delete', ['user_id']);
112 pyroutes.register('edit_user_ssh_keys_delete', '/_admin/users/%(user_id)s/edit/ssh_keys/delete', ['user_id']);
113 pyroutes.register('edit_user_emails', '/_admin/users/%(user_id)s/edit/emails', ['user_id']);
113 pyroutes.register('edit_user_emails', '/_admin/users/%(user_id)s/edit/emails', ['user_id']);
114 pyroutes.register('edit_user_emails_add', '/_admin/users/%(user_id)s/edit/emails/new', ['user_id']);
114 pyroutes.register('edit_user_emails_add', '/_admin/users/%(user_id)s/edit/emails/new', ['user_id']);
115 pyroutes.register('edit_user_emails_delete', '/_admin/users/%(user_id)s/edit/emails/delete', ['user_id']);
115 pyroutes.register('edit_user_emails_delete', '/_admin/users/%(user_id)s/edit/emails/delete', ['user_id']);
116 pyroutes.register('edit_user_ips', '/_admin/users/%(user_id)s/edit/ips', ['user_id']);
116 pyroutes.register('edit_user_ips', '/_admin/users/%(user_id)s/edit/ips', ['user_id']);
117 pyroutes.register('edit_user_ips_add', '/_admin/users/%(user_id)s/edit/ips/new', ['user_id']);
117 pyroutes.register('edit_user_ips_add', '/_admin/users/%(user_id)s/edit/ips/new', ['user_id']);
118 pyroutes.register('edit_user_ips_delete', '/_admin/users/%(user_id)s/edit/ips/delete', ['user_id']);
118 pyroutes.register('edit_user_ips_delete', '/_admin/users/%(user_id)s/edit/ips/delete', ['user_id']);
119 pyroutes.register('edit_user_perms_summary', '/_admin/users/%(user_id)s/edit/permissions_summary', ['user_id']);
119 pyroutes.register('edit_user_perms_summary', '/_admin/users/%(user_id)s/edit/permissions_summary', ['user_id']);
120 pyroutes.register('edit_user_perms_summary_json', '/_admin/users/%(user_id)s/edit/permissions_summary/json', ['user_id']);
120 pyroutes.register('edit_user_perms_summary_json', '/_admin/users/%(user_id)s/edit/permissions_summary/json', ['user_id']);
121 pyroutes.register('edit_user_groups_management', '/_admin/users/%(user_id)s/edit/groups_management', ['user_id']);
121 pyroutes.register('edit_user_groups_management', '/_admin/users/%(user_id)s/edit/groups_management', ['user_id']);
122 pyroutes.register('edit_user_groups_management_updates', '/_admin/users/%(user_id)s/edit/edit_user_groups_management/updates', ['user_id']);
122 pyroutes.register('edit_user_groups_management_updates', '/_admin/users/%(user_id)s/edit/edit_user_groups_management/updates', ['user_id']);
123 pyroutes.register('edit_user_audit_logs', '/_admin/users/%(user_id)s/edit/audit', ['user_id']);
123 pyroutes.register('edit_user_audit_logs', '/_admin/users/%(user_id)s/edit/audit', ['user_id']);
124 pyroutes.register('edit_user_audit_logs_download', '/_admin/users/%(user_id)s/edit/audit/download', ['user_id']);
124 pyroutes.register('edit_user_audit_logs_download', '/_admin/users/%(user_id)s/edit/audit/download', ['user_id']);
125 pyroutes.register('edit_user_caches', '/_admin/users/%(user_id)s/edit/caches', ['user_id']);
125 pyroutes.register('edit_user_caches', '/_admin/users/%(user_id)s/edit/caches', ['user_id']);
126 pyroutes.register('edit_user_caches_update', '/_admin/users/%(user_id)s/edit/caches/update', ['user_id']);
126 pyroutes.register('edit_user_caches_update', '/_admin/users/%(user_id)s/edit/caches/update', ['user_id']);
127 pyroutes.register('user_groups', '/_admin/user_groups', []);
127 pyroutes.register('user_groups', '/_admin/user_groups', []);
128 pyroutes.register('user_groups_data', '/_admin/user_groups_data', []);
128 pyroutes.register('user_groups_data', '/_admin/user_groups_data', []);
129 pyroutes.register('user_groups_new', '/_admin/user_groups/new', []);
129 pyroutes.register('user_groups_new', '/_admin/user_groups/new', []);
130 pyroutes.register('user_groups_create', '/_admin/user_groups/create', []);
130 pyroutes.register('user_groups_create', '/_admin/user_groups/create', []);
131 pyroutes.register('repos', '/_admin/repos', []);
131 pyroutes.register('repos', '/_admin/repos', []);
132 pyroutes.register('repo_new', '/_admin/repos/new', []);
132 pyroutes.register('repo_new', '/_admin/repos/new', []);
133 pyroutes.register('repo_create', '/_admin/repos/create', []);
133 pyroutes.register('repo_create', '/_admin/repos/create', []);
134 pyroutes.register('repo_groups', '/_admin/repo_groups', []);
134 pyroutes.register('repo_groups', '/_admin/repo_groups', []);
135 pyroutes.register('repo_groups_data', '/_admin/repo_groups_data', []);
135 pyroutes.register('repo_groups_data', '/_admin/repo_groups_data', []);
136 pyroutes.register('repo_group_new', '/_admin/repo_group/new', []);
136 pyroutes.register('repo_group_new', '/_admin/repo_group/new', []);
137 pyroutes.register('repo_group_create', '/_admin/repo_group/create', []);
137 pyroutes.register('repo_group_create', '/_admin/repo_group/create', []);
138 pyroutes.register('channelstream_connect', '/_admin/channelstream/connect', []);
138 pyroutes.register('channelstream_connect', '/_admin/channelstream/connect', []);
139 pyroutes.register('channelstream_subscribe', '/_admin/channelstream/subscribe', []);
139 pyroutes.register('channelstream_subscribe', '/_admin/channelstream/subscribe', []);
140 pyroutes.register('channelstream_proxy', '/_channelstream', []);
140 pyroutes.register('channelstream_proxy', '/_channelstream', []);
141 pyroutes.register('upload_file', '/_file_store/upload', []);
141 pyroutes.register('upload_file', '/_file_store/upload', []);
142 pyroutes.register('download_file', '/_file_store/download/%(fid)s', ['fid']);
142 pyroutes.register('download_file', '/_file_store/download/%(fid)s', ['fid']);
143 pyroutes.register('logout', '/_admin/logout', []);
143 pyroutes.register('logout', '/_admin/logout', []);
144 pyroutes.register('reset_password', '/_admin/password_reset', []);
144 pyroutes.register('reset_password', '/_admin/password_reset', []);
145 pyroutes.register('reset_password_confirmation', '/_admin/password_reset_confirmation', []);
145 pyroutes.register('reset_password_confirmation', '/_admin/password_reset_confirmation', []);
146 pyroutes.register('home', '/', []);
146 pyroutes.register('home', '/', []);
147 pyroutes.register('user_autocomplete_data', '/_users', []);
147 pyroutes.register('user_autocomplete_data', '/_users', []);
148 pyroutes.register('user_group_autocomplete_data', '/_user_groups', []);
148 pyroutes.register('user_group_autocomplete_data', '/_user_groups', []);
149 pyroutes.register('repo_list_data', '/_repos', []);
149 pyroutes.register('repo_list_data', '/_repos', []);
150 pyroutes.register('repo_group_list_data', '/_repo_groups', []);
150 pyroutes.register('repo_group_list_data', '/_repo_groups', []);
151 pyroutes.register('goto_switcher_data', '/_goto_data', []);
151 pyroutes.register('goto_switcher_data', '/_goto_data', []);
152 pyroutes.register('markup_preview', '/_markup_preview', []);
152 pyroutes.register('markup_preview', '/_markup_preview', []);
153 pyroutes.register('file_preview', '/_file_preview', []);
153 pyroutes.register('file_preview', '/_file_preview', []);
154 pyroutes.register('store_user_session_value', '/_store_session_attr', []);
154 pyroutes.register('store_user_session_value', '/_store_session_attr', []);
155 pyroutes.register('journal', '/_admin/journal', []);
155 pyroutes.register('journal', '/_admin/journal', []);
156 pyroutes.register('journal_rss', '/_admin/journal/rss', []);
156 pyroutes.register('journal_rss', '/_admin/journal/rss', []);
157 pyroutes.register('journal_atom', '/_admin/journal/atom', []);
157 pyroutes.register('journal_atom', '/_admin/journal/atom', []);
158 pyroutes.register('journal_public', '/_admin/public_journal', []);
158 pyroutes.register('journal_public', '/_admin/public_journal', []);
159 pyroutes.register('journal_public_atom', '/_admin/public_journal/atom', []);
159 pyroutes.register('journal_public_atom', '/_admin/public_journal/atom', []);
160 pyroutes.register('journal_public_atom_old', '/_admin/public_journal_atom', []);
160 pyroutes.register('journal_public_atom_old', '/_admin/public_journal_atom', []);
161 pyroutes.register('journal_public_rss', '/_admin/public_journal/rss', []);
161 pyroutes.register('journal_public_rss', '/_admin/public_journal/rss', []);
162 pyroutes.register('journal_public_rss_old', '/_admin/public_journal_rss', []);
162 pyroutes.register('journal_public_rss_old', '/_admin/public_journal_rss', []);
163 pyroutes.register('toggle_following', '/_admin/toggle_following', []);
163 pyroutes.register('toggle_following', '/_admin/toggle_following', []);
164 pyroutes.register('repo_creating', '/%(repo_name)s/repo_creating', ['repo_name']);
164 pyroutes.register('repo_creating', '/%(repo_name)s/repo_creating', ['repo_name']);
165 pyroutes.register('repo_creating_check', '/%(repo_name)s/repo_creating_check', ['repo_name']);
165 pyroutes.register('repo_creating_check', '/%(repo_name)s/repo_creating_check', ['repo_name']);
166 pyroutes.register('repo_summary_explicit', '/%(repo_name)s/summary', ['repo_name']);
166 pyroutes.register('repo_summary_explicit', '/%(repo_name)s/summary', ['repo_name']);
167 pyroutes.register('repo_summary_commits', '/%(repo_name)s/summary-commits', ['repo_name']);
167 pyroutes.register('repo_summary_commits', '/%(repo_name)s/summary-commits', ['repo_name']);
168 pyroutes.register('repo_commit', '/%(repo_name)s/changeset/%(commit_id)s', ['repo_name', 'commit_id']);
168 pyroutes.register('repo_commit', '/%(repo_name)s/changeset/%(commit_id)s', ['repo_name', 'commit_id']);
169 pyroutes.register('repo_commit_children', '/%(repo_name)s/changeset_children/%(commit_id)s', ['repo_name', 'commit_id']);
169 pyroutes.register('repo_commit_children', '/%(repo_name)s/changeset_children/%(commit_id)s', ['repo_name', 'commit_id']);
170 pyroutes.register('repo_commit_parents', '/%(repo_name)s/changeset_parents/%(commit_id)s', ['repo_name', 'commit_id']);
170 pyroutes.register('repo_commit_parents', '/%(repo_name)s/changeset_parents/%(commit_id)s', ['repo_name', 'commit_id']);
171 pyroutes.register('repo_commit_raw', '/%(repo_name)s/changeset-diff/%(commit_id)s', ['repo_name', 'commit_id']);
171 pyroutes.register('repo_commit_raw', '/%(repo_name)s/changeset-diff/%(commit_id)s', ['repo_name', 'commit_id']);
172 pyroutes.register('repo_commit_patch', '/%(repo_name)s/changeset-patch/%(commit_id)s', ['repo_name', 'commit_id']);
172 pyroutes.register('repo_commit_patch', '/%(repo_name)s/changeset-patch/%(commit_id)s', ['repo_name', 'commit_id']);
173 pyroutes.register('repo_commit_download', '/%(repo_name)s/changeset-download/%(commit_id)s', ['repo_name', 'commit_id']);
173 pyroutes.register('repo_commit_download', '/%(repo_name)s/changeset-download/%(commit_id)s', ['repo_name', 'commit_id']);
174 pyroutes.register('repo_commit_data', '/%(repo_name)s/changeset-data/%(commit_id)s', ['repo_name', 'commit_id']);
174 pyroutes.register('repo_commit_data', '/%(repo_name)s/changeset-data/%(commit_id)s', ['repo_name', 'commit_id']);
175 pyroutes.register('repo_commit_comment_create', '/%(repo_name)s/changeset/%(commit_id)s/comment/create', ['repo_name', 'commit_id']);
175 pyroutes.register('repo_commit_comment_create', '/%(repo_name)s/changeset/%(commit_id)s/comment/create', ['repo_name', 'commit_id']);
176 pyroutes.register('repo_commit_comment_preview', '/%(repo_name)s/changeset/%(commit_id)s/comment/preview', ['repo_name', 'commit_id']);
176 pyroutes.register('repo_commit_comment_preview', '/%(repo_name)s/changeset/%(commit_id)s/comment/preview', ['repo_name', 'commit_id']);
177 pyroutes.register('repo_commit_comment_attachment_upload', '/%(repo_name)s/changeset/%(commit_id)s/comment/attachment_upload', ['repo_name', 'commit_id']);
177 pyroutes.register('repo_commit_comment_delete', '/%(repo_name)s/changeset/%(commit_id)s/comment/%(comment_id)s/delete', ['repo_name', 'commit_id', 'comment_id']);
178 pyroutes.register('repo_commit_comment_delete', '/%(repo_name)s/changeset/%(commit_id)s/comment/%(comment_id)s/delete', ['repo_name', 'commit_id', 'comment_id']);
178 pyroutes.register('repo_commit_raw_deprecated', '/%(repo_name)s/raw-changeset/%(commit_id)s', ['repo_name', 'commit_id']);
179 pyroutes.register('repo_commit_raw_deprecated', '/%(repo_name)s/raw-changeset/%(commit_id)s', ['repo_name', 'commit_id']);
179 pyroutes.register('repo_archivefile', '/%(repo_name)s/archive/%(fname)s', ['repo_name', 'fname']);
180 pyroutes.register('repo_archivefile', '/%(repo_name)s/archive/%(fname)s', ['repo_name', 'fname']);
180 pyroutes.register('repo_files_diff', '/%(repo_name)s/diff/%(f_path)s', ['repo_name', 'f_path']);
181 pyroutes.register('repo_files_diff', '/%(repo_name)s/diff/%(f_path)s', ['repo_name', 'f_path']);
181 pyroutes.register('repo_files_diff_2way_redirect', '/%(repo_name)s/diff-2way/%(f_path)s', ['repo_name', 'f_path']);
182 pyroutes.register('repo_files_diff_2way_redirect', '/%(repo_name)s/diff-2way/%(f_path)s', ['repo_name', 'f_path']);
182 pyroutes.register('repo_files', '/%(repo_name)s/files/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
183 pyroutes.register('repo_files', '/%(repo_name)s/files/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
183 pyroutes.register('repo_files:default_path', '/%(repo_name)s/files/%(commit_id)s/', ['repo_name', 'commit_id']);
184 pyroutes.register('repo_files:default_path', '/%(repo_name)s/files/%(commit_id)s/', ['repo_name', 'commit_id']);
184 pyroutes.register('repo_files:default_commit', '/%(repo_name)s/files', ['repo_name']);
185 pyroutes.register('repo_files:default_commit', '/%(repo_name)s/files', ['repo_name']);
185 pyroutes.register('repo_files:rendered', '/%(repo_name)s/render/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
186 pyroutes.register('repo_files:rendered', '/%(repo_name)s/render/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
186 pyroutes.register('repo_files:annotated', '/%(repo_name)s/annotate/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
187 pyroutes.register('repo_files:annotated', '/%(repo_name)s/annotate/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
187 pyroutes.register('repo_files:annotated_previous', '/%(repo_name)s/annotate-previous/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
188 pyroutes.register('repo_files:annotated_previous', '/%(repo_name)s/annotate-previous/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
188 pyroutes.register('repo_nodetree_full', '/%(repo_name)s/nodetree_full/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
189 pyroutes.register('repo_nodetree_full', '/%(repo_name)s/nodetree_full/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
189 pyroutes.register('repo_nodetree_full:default_path', '/%(repo_name)s/nodetree_full/%(commit_id)s/', ['repo_name', 'commit_id']);
190 pyroutes.register('repo_nodetree_full:default_path', '/%(repo_name)s/nodetree_full/%(commit_id)s/', ['repo_name', 'commit_id']);
190 pyroutes.register('repo_files_nodelist', '/%(repo_name)s/nodelist/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
191 pyroutes.register('repo_files_nodelist', '/%(repo_name)s/nodelist/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
191 pyroutes.register('repo_file_raw', '/%(repo_name)s/raw/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
192 pyroutes.register('repo_file_raw', '/%(repo_name)s/raw/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
192 pyroutes.register('repo_file_download', '/%(repo_name)s/download/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
193 pyroutes.register('repo_file_download', '/%(repo_name)s/download/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
193 pyroutes.register('repo_file_download:legacy', '/%(repo_name)s/rawfile/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
194 pyroutes.register('repo_file_download:legacy', '/%(repo_name)s/rawfile/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
194 pyroutes.register('repo_file_history', '/%(repo_name)s/history/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
195 pyroutes.register('repo_file_history', '/%(repo_name)s/history/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
195 pyroutes.register('repo_file_authors', '/%(repo_name)s/authors/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
196 pyroutes.register('repo_file_authors', '/%(repo_name)s/authors/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
196 pyroutes.register('repo_files_remove_file', '/%(repo_name)s/remove_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
197 pyroutes.register('repo_files_remove_file', '/%(repo_name)s/remove_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
197 pyroutes.register('repo_files_delete_file', '/%(repo_name)s/delete_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
198 pyroutes.register('repo_files_delete_file', '/%(repo_name)s/delete_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
198 pyroutes.register('repo_files_edit_file', '/%(repo_name)s/edit_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
199 pyroutes.register('repo_files_edit_file', '/%(repo_name)s/edit_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
199 pyroutes.register('repo_files_update_file', '/%(repo_name)s/update_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
200 pyroutes.register('repo_files_update_file', '/%(repo_name)s/update_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
200 pyroutes.register('repo_files_add_file', '/%(repo_name)s/add_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
201 pyroutes.register('repo_files_add_file', '/%(repo_name)s/add_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
201 pyroutes.register('repo_files_upload_file', '/%(repo_name)s/upload_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
202 pyroutes.register('repo_files_upload_file', '/%(repo_name)s/upload_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
202 pyroutes.register('repo_files_create_file', '/%(repo_name)s/create_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
203 pyroutes.register('repo_files_create_file', '/%(repo_name)s/create_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
203 pyroutes.register('repo_refs_data', '/%(repo_name)s/refs-data', ['repo_name']);
204 pyroutes.register('repo_refs_data', '/%(repo_name)s/refs-data', ['repo_name']);
204 pyroutes.register('repo_refs_changelog_data', '/%(repo_name)s/refs-data-changelog', ['repo_name']);
205 pyroutes.register('repo_refs_changelog_data', '/%(repo_name)s/refs-data-changelog', ['repo_name']);
205 pyroutes.register('repo_stats', '/%(repo_name)s/repo_stats/%(commit_id)s', ['repo_name', 'commit_id']);
206 pyroutes.register('repo_stats', '/%(repo_name)s/repo_stats/%(commit_id)s', ['repo_name', 'commit_id']);
206 pyroutes.register('repo_commits', '/%(repo_name)s/commits', ['repo_name']);
207 pyroutes.register('repo_commits', '/%(repo_name)s/commits', ['repo_name']);
207 pyroutes.register('repo_commits_file', '/%(repo_name)s/commits/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
208 pyroutes.register('repo_commits_file', '/%(repo_name)s/commits/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
208 pyroutes.register('repo_commits_elements', '/%(repo_name)s/commits_elements', ['repo_name']);
209 pyroutes.register('repo_commits_elements', '/%(repo_name)s/commits_elements', ['repo_name']);
209 pyroutes.register('repo_commits_elements_file', '/%(repo_name)s/commits_elements/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
210 pyroutes.register('repo_commits_elements_file', '/%(repo_name)s/commits_elements/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
210 pyroutes.register('repo_changelog', '/%(repo_name)s/changelog', ['repo_name']);
211 pyroutes.register('repo_changelog', '/%(repo_name)s/changelog', ['repo_name']);
211 pyroutes.register('repo_changelog_file', '/%(repo_name)s/changelog/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
212 pyroutes.register('repo_changelog_file', '/%(repo_name)s/changelog/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
212 pyroutes.register('repo_compare_select', '/%(repo_name)s/compare', ['repo_name']);
213 pyroutes.register('repo_compare_select', '/%(repo_name)s/compare', ['repo_name']);
213 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']);
214 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']);
214 pyroutes.register('tags_home', '/%(repo_name)s/tags', ['repo_name']);
215 pyroutes.register('tags_home', '/%(repo_name)s/tags', ['repo_name']);
215 pyroutes.register('branches_home', '/%(repo_name)s/branches', ['repo_name']);
216 pyroutes.register('branches_home', '/%(repo_name)s/branches', ['repo_name']);
216 pyroutes.register('bookmarks_home', '/%(repo_name)s/bookmarks', ['repo_name']);
217 pyroutes.register('bookmarks_home', '/%(repo_name)s/bookmarks', ['repo_name']);
217 pyroutes.register('repo_fork_new', '/%(repo_name)s/fork', ['repo_name']);
218 pyroutes.register('repo_fork_new', '/%(repo_name)s/fork', ['repo_name']);
218 pyroutes.register('repo_fork_create', '/%(repo_name)s/fork/create', ['repo_name']);
219 pyroutes.register('repo_fork_create', '/%(repo_name)s/fork/create', ['repo_name']);
219 pyroutes.register('repo_forks_show_all', '/%(repo_name)s/forks', ['repo_name']);
220 pyroutes.register('repo_forks_show_all', '/%(repo_name)s/forks', ['repo_name']);
220 pyroutes.register('repo_forks_data', '/%(repo_name)s/forks/data', ['repo_name']);
221 pyroutes.register('repo_forks_data', '/%(repo_name)s/forks/data', ['repo_name']);
221 pyroutes.register('pullrequest_show', '/%(repo_name)s/pull-request/%(pull_request_id)s', ['repo_name', 'pull_request_id']);
222 pyroutes.register('pullrequest_show', '/%(repo_name)s/pull-request/%(pull_request_id)s', ['repo_name', 'pull_request_id']);
222 pyroutes.register('pullrequest_show_all', '/%(repo_name)s/pull-request', ['repo_name']);
223 pyroutes.register('pullrequest_show_all', '/%(repo_name)s/pull-request', ['repo_name']);
223 pyroutes.register('pullrequest_show_all_data', '/%(repo_name)s/pull-request-data', ['repo_name']);
224 pyroutes.register('pullrequest_show_all_data', '/%(repo_name)s/pull-request-data', ['repo_name']);
224 pyroutes.register('pullrequest_repo_refs', '/%(repo_name)s/pull-request/refs/%(target_repo_name)s', ['repo_name', 'target_repo_name']);
225 pyroutes.register('pullrequest_repo_refs', '/%(repo_name)s/pull-request/refs/%(target_repo_name)s', ['repo_name', 'target_repo_name']);
225 pyroutes.register('pullrequest_repo_targets', '/%(repo_name)s/pull-request/repo-targets', ['repo_name']);
226 pyroutes.register('pullrequest_repo_targets', '/%(repo_name)s/pull-request/repo-targets', ['repo_name']);
226 pyroutes.register('pullrequest_new', '/%(repo_name)s/pull-request/new', ['repo_name']);
227 pyroutes.register('pullrequest_new', '/%(repo_name)s/pull-request/new', ['repo_name']);
227 pyroutes.register('pullrequest_create', '/%(repo_name)s/pull-request/create', ['repo_name']);
228 pyroutes.register('pullrequest_create', '/%(repo_name)s/pull-request/create', ['repo_name']);
228 pyroutes.register('pullrequest_update', '/%(repo_name)s/pull-request/%(pull_request_id)s/update', ['repo_name', 'pull_request_id']);
229 pyroutes.register('pullrequest_update', '/%(repo_name)s/pull-request/%(pull_request_id)s/update', ['repo_name', 'pull_request_id']);
229 pyroutes.register('pullrequest_merge', '/%(repo_name)s/pull-request/%(pull_request_id)s/merge', ['repo_name', 'pull_request_id']);
230 pyroutes.register('pullrequest_merge', '/%(repo_name)s/pull-request/%(pull_request_id)s/merge', ['repo_name', 'pull_request_id']);
230 pyroutes.register('pullrequest_delete', '/%(repo_name)s/pull-request/%(pull_request_id)s/delete', ['repo_name', 'pull_request_id']);
231 pyroutes.register('pullrequest_delete', '/%(repo_name)s/pull-request/%(pull_request_id)s/delete', ['repo_name', 'pull_request_id']);
231 pyroutes.register('pullrequest_comment_create', '/%(repo_name)s/pull-request/%(pull_request_id)s/comment', ['repo_name', 'pull_request_id']);
232 pyroutes.register('pullrequest_comment_create', '/%(repo_name)s/pull-request/%(pull_request_id)s/comment', ['repo_name', 'pull_request_id']);
232 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']);
233 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']);
233 pyroutes.register('edit_repo', '/%(repo_name)s/settings', ['repo_name']);
234 pyroutes.register('edit_repo', '/%(repo_name)s/settings', ['repo_name']);
234 pyroutes.register('edit_repo_advanced', '/%(repo_name)s/settings/advanced', ['repo_name']);
235 pyroutes.register('edit_repo_advanced', '/%(repo_name)s/settings/advanced', ['repo_name']);
235 pyroutes.register('edit_repo_advanced_archive', '/%(repo_name)s/settings/advanced/archive', ['repo_name']);
236 pyroutes.register('edit_repo_advanced_archive', '/%(repo_name)s/settings/advanced/archive', ['repo_name']);
236 pyroutes.register('edit_repo_advanced_delete', '/%(repo_name)s/settings/advanced/delete', ['repo_name']);
237 pyroutes.register('edit_repo_advanced_delete', '/%(repo_name)s/settings/advanced/delete', ['repo_name']);
237 pyroutes.register('edit_repo_advanced_locking', '/%(repo_name)s/settings/advanced/locking', ['repo_name']);
238 pyroutes.register('edit_repo_advanced_locking', '/%(repo_name)s/settings/advanced/locking', ['repo_name']);
238 pyroutes.register('edit_repo_advanced_journal', '/%(repo_name)s/settings/advanced/journal', ['repo_name']);
239 pyroutes.register('edit_repo_advanced_journal', '/%(repo_name)s/settings/advanced/journal', ['repo_name']);
239 pyroutes.register('edit_repo_advanced_fork', '/%(repo_name)s/settings/advanced/fork', ['repo_name']);
240 pyroutes.register('edit_repo_advanced_fork', '/%(repo_name)s/settings/advanced/fork', ['repo_name']);
240 pyroutes.register('edit_repo_advanced_hooks', '/%(repo_name)s/settings/advanced/hooks', ['repo_name']);
241 pyroutes.register('edit_repo_advanced_hooks', '/%(repo_name)s/settings/advanced/hooks', ['repo_name']);
241 pyroutes.register('edit_repo_caches', '/%(repo_name)s/settings/caches', ['repo_name']);
242 pyroutes.register('edit_repo_caches', '/%(repo_name)s/settings/caches', ['repo_name']);
242 pyroutes.register('edit_repo_perms', '/%(repo_name)s/settings/permissions', ['repo_name']);
243 pyroutes.register('edit_repo_perms', '/%(repo_name)s/settings/permissions', ['repo_name']);
243 pyroutes.register('edit_repo_perms_set_private', '/%(repo_name)s/settings/permissions/set_private', ['repo_name']);
244 pyroutes.register('edit_repo_perms_set_private', '/%(repo_name)s/settings/permissions/set_private', ['repo_name']);
244 pyroutes.register('edit_repo_maintenance', '/%(repo_name)s/settings/maintenance', ['repo_name']);
245 pyroutes.register('edit_repo_maintenance', '/%(repo_name)s/settings/maintenance', ['repo_name']);
245 pyroutes.register('edit_repo_maintenance_execute', '/%(repo_name)s/settings/maintenance/execute', ['repo_name']);
246 pyroutes.register('edit_repo_maintenance_execute', '/%(repo_name)s/settings/maintenance/execute', ['repo_name']);
246 pyroutes.register('edit_repo_fields', '/%(repo_name)s/settings/fields', ['repo_name']);
247 pyroutes.register('edit_repo_fields', '/%(repo_name)s/settings/fields', ['repo_name']);
247 pyroutes.register('edit_repo_fields_create', '/%(repo_name)s/settings/fields/create', ['repo_name']);
248 pyroutes.register('edit_repo_fields_create', '/%(repo_name)s/settings/fields/create', ['repo_name']);
248 pyroutes.register('edit_repo_fields_delete', '/%(repo_name)s/settings/fields/%(field_id)s/delete', ['repo_name', 'field_id']);
249 pyroutes.register('edit_repo_fields_delete', '/%(repo_name)s/settings/fields/%(field_id)s/delete', ['repo_name', 'field_id']);
249 pyroutes.register('repo_edit_toggle_locking', '/%(repo_name)s/settings/toggle_locking', ['repo_name']);
250 pyroutes.register('repo_edit_toggle_locking', '/%(repo_name)s/settings/toggle_locking', ['repo_name']);
250 pyroutes.register('edit_repo_remote', '/%(repo_name)s/settings/remote', ['repo_name']);
251 pyroutes.register('edit_repo_remote', '/%(repo_name)s/settings/remote', ['repo_name']);
251 pyroutes.register('edit_repo_remote_pull', '/%(repo_name)s/settings/remote/pull', ['repo_name']);
252 pyroutes.register('edit_repo_remote_pull', '/%(repo_name)s/settings/remote/pull', ['repo_name']);
252 pyroutes.register('edit_repo_statistics', '/%(repo_name)s/settings/statistics', ['repo_name']);
253 pyroutes.register('edit_repo_statistics', '/%(repo_name)s/settings/statistics', ['repo_name']);
253 pyroutes.register('edit_repo_statistics_reset', '/%(repo_name)s/settings/statistics/update', ['repo_name']);
254 pyroutes.register('edit_repo_statistics_reset', '/%(repo_name)s/settings/statistics/update', ['repo_name']);
254 pyroutes.register('edit_repo_issuetracker', '/%(repo_name)s/settings/issue_trackers', ['repo_name']);
255 pyroutes.register('edit_repo_issuetracker', '/%(repo_name)s/settings/issue_trackers', ['repo_name']);
255 pyroutes.register('edit_repo_issuetracker_test', '/%(repo_name)s/settings/issue_trackers/test', ['repo_name']);
256 pyroutes.register('edit_repo_issuetracker_test', '/%(repo_name)s/settings/issue_trackers/test', ['repo_name']);
256 pyroutes.register('edit_repo_issuetracker_delete', '/%(repo_name)s/settings/issue_trackers/delete', ['repo_name']);
257 pyroutes.register('edit_repo_issuetracker_delete', '/%(repo_name)s/settings/issue_trackers/delete', ['repo_name']);
257 pyroutes.register('edit_repo_issuetracker_update', '/%(repo_name)s/settings/issue_trackers/update', ['repo_name']);
258 pyroutes.register('edit_repo_issuetracker_update', '/%(repo_name)s/settings/issue_trackers/update', ['repo_name']);
258 pyroutes.register('edit_repo_vcs', '/%(repo_name)s/settings/vcs', ['repo_name']);
259 pyroutes.register('edit_repo_vcs', '/%(repo_name)s/settings/vcs', ['repo_name']);
259 pyroutes.register('edit_repo_vcs_update', '/%(repo_name)s/settings/vcs/update', ['repo_name']);
260 pyroutes.register('edit_repo_vcs_update', '/%(repo_name)s/settings/vcs/update', ['repo_name']);
260 pyroutes.register('edit_repo_vcs_svn_pattern_delete', '/%(repo_name)s/settings/vcs/svn_pattern/delete', ['repo_name']);
261 pyroutes.register('edit_repo_vcs_svn_pattern_delete', '/%(repo_name)s/settings/vcs/svn_pattern/delete', ['repo_name']);
261 pyroutes.register('repo_reviewers', '/%(repo_name)s/settings/review/rules', ['repo_name']);
262 pyroutes.register('repo_reviewers', '/%(repo_name)s/settings/review/rules', ['repo_name']);
262 pyroutes.register('repo_default_reviewers_data', '/%(repo_name)s/settings/review/default-reviewers', ['repo_name']);
263 pyroutes.register('repo_default_reviewers_data', '/%(repo_name)s/settings/review/default-reviewers', ['repo_name']);
263 pyroutes.register('edit_repo_strip', '/%(repo_name)s/settings/strip', ['repo_name']);
264 pyroutes.register('edit_repo_strip', '/%(repo_name)s/settings/strip', ['repo_name']);
264 pyroutes.register('strip_check', '/%(repo_name)s/settings/strip_check', ['repo_name']);
265 pyroutes.register('strip_check', '/%(repo_name)s/settings/strip_check', ['repo_name']);
265 pyroutes.register('strip_execute', '/%(repo_name)s/settings/strip_execute', ['repo_name']);
266 pyroutes.register('strip_execute', '/%(repo_name)s/settings/strip_execute', ['repo_name']);
266 pyroutes.register('edit_repo_audit_logs', '/%(repo_name)s/settings/audit_logs', ['repo_name']);
267 pyroutes.register('edit_repo_audit_logs', '/%(repo_name)s/settings/audit_logs', ['repo_name']);
267 pyroutes.register('rss_feed_home', '/%(repo_name)s/feed-rss', ['repo_name']);
268 pyroutes.register('rss_feed_home', '/%(repo_name)s/feed-rss', ['repo_name']);
268 pyroutes.register('atom_feed_home', '/%(repo_name)s/feed-atom', ['repo_name']);
269 pyroutes.register('atom_feed_home', '/%(repo_name)s/feed-atom', ['repo_name']);
269 pyroutes.register('rss_feed_home_old', '/%(repo_name)s/feed/rss', ['repo_name']);
270 pyroutes.register('rss_feed_home_old', '/%(repo_name)s/feed/rss', ['repo_name']);
270 pyroutes.register('atom_feed_home_old', '/%(repo_name)s/feed/atom', ['repo_name']);
271 pyroutes.register('atom_feed_home_old', '/%(repo_name)s/feed/atom', ['repo_name']);
271 pyroutes.register('repo_summary', '/%(repo_name)s', ['repo_name']);
272 pyroutes.register('repo_summary', '/%(repo_name)s', ['repo_name']);
272 pyroutes.register('repo_summary_slash', '/%(repo_name)s/', ['repo_name']);
273 pyroutes.register('repo_summary_slash', '/%(repo_name)s/', ['repo_name']);
273 pyroutes.register('edit_repo_group', '/%(repo_group_name)s/_edit', ['repo_group_name']);
274 pyroutes.register('edit_repo_group', '/%(repo_group_name)s/_edit', ['repo_group_name']);
274 pyroutes.register('edit_repo_group_advanced', '/%(repo_group_name)s/_settings/advanced', ['repo_group_name']);
275 pyroutes.register('edit_repo_group_advanced', '/%(repo_group_name)s/_settings/advanced', ['repo_group_name']);
275 pyroutes.register('edit_repo_group_advanced_delete', '/%(repo_group_name)s/_settings/advanced/delete', ['repo_group_name']);
276 pyroutes.register('edit_repo_group_advanced_delete', '/%(repo_group_name)s/_settings/advanced/delete', ['repo_group_name']);
276 pyroutes.register('edit_repo_group_perms', '/%(repo_group_name)s/_settings/permissions', ['repo_group_name']);
277 pyroutes.register('edit_repo_group_perms', '/%(repo_group_name)s/_settings/permissions', ['repo_group_name']);
277 pyroutes.register('edit_repo_group_perms_update', '/%(repo_group_name)s/_settings/permissions/update', ['repo_group_name']);
278 pyroutes.register('edit_repo_group_perms_update', '/%(repo_group_name)s/_settings/permissions/update', ['repo_group_name']);
278 pyroutes.register('repo_group_home', '/%(repo_group_name)s', ['repo_group_name']);
279 pyroutes.register('repo_group_home', '/%(repo_group_name)s', ['repo_group_name']);
279 pyroutes.register('repo_group_home_slash', '/%(repo_group_name)s/', ['repo_group_name']);
280 pyroutes.register('repo_group_home_slash', '/%(repo_group_name)s/', ['repo_group_name']);
280 pyroutes.register('user_group_members_data', '/_admin/user_groups/%(user_group_id)s/members', ['user_group_id']);
281 pyroutes.register('user_group_members_data', '/_admin/user_groups/%(user_group_id)s/members', ['user_group_id']);
281 pyroutes.register('edit_user_group_perms_summary', '/_admin/user_groups/%(user_group_id)s/edit/permissions_summary', ['user_group_id']);
282 pyroutes.register('edit_user_group_perms_summary', '/_admin/user_groups/%(user_group_id)s/edit/permissions_summary', ['user_group_id']);
282 pyroutes.register('edit_user_group_perms_summary_json', '/_admin/user_groups/%(user_group_id)s/edit/permissions_summary/json', ['user_group_id']);
283 pyroutes.register('edit_user_group_perms_summary_json', '/_admin/user_groups/%(user_group_id)s/edit/permissions_summary/json', ['user_group_id']);
283 pyroutes.register('edit_user_group', '/_admin/user_groups/%(user_group_id)s/edit', ['user_group_id']);
284 pyroutes.register('edit_user_group', '/_admin/user_groups/%(user_group_id)s/edit', ['user_group_id']);
284 pyroutes.register('user_groups_update', '/_admin/user_groups/%(user_group_id)s/update', ['user_group_id']);
285 pyroutes.register('user_groups_update', '/_admin/user_groups/%(user_group_id)s/update', ['user_group_id']);
285 pyroutes.register('edit_user_group_global_perms', '/_admin/user_groups/%(user_group_id)s/edit/global_permissions', ['user_group_id']);
286 pyroutes.register('edit_user_group_global_perms', '/_admin/user_groups/%(user_group_id)s/edit/global_permissions', ['user_group_id']);
286 pyroutes.register('edit_user_group_global_perms_update', '/_admin/user_groups/%(user_group_id)s/edit/global_permissions/update', ['user_group_id']);
287 pyroutes.register('edit_user_group_global_perms_update', '/_admin/user_groups/%(user_group_id)s/edit/global_permissions/update', ['user_group_id']);
287 pyroutes.register('edit_user_group_perms', '/_admin/user_groups/%(user_group_id)s/edit/permissions', ['user_group_id']);
288 pyroutes.register('edit_user_group_perms', '/_admin/user_groups/%(user_group_id)s/edit/permissions', ['user_group_id']);
288 pyroutes.register('edit_user_group_perms_update', '/_admin/user_groups/%(user_group_id)s/edit/permissions/update', ['user_group_id']);
289 pyroutes.register('edit_user_group_perms_update', '/_admin/user_groups/%(user_group_id)s/edit/permissions/update', ['user_group_id']);
289 pyroutes.register('edit_user_group_advanced', '/_admin/user_groups/%(user_group_id)s/edit/advanced', ['user_group_id']);
290 pyroutes.register('edit_user_group_advanced', '/_admin/user_groups/%(user_group_id)s/edit/advanced', ['user_group_id']);
290 pyroutes.register('edit_user_group_advanced_sync', '/_admin/user_groups/%(user_group_id)s/edit/advanced/sync', ['user_group_id']);
291 pyroutes.register('edit_user_group_advanced_sync', '/_admin/user_groups/%(user_group_id)s/edit/advanced/sync', ['user_group_id']);
291 pyroutes.register('user_groups_delete', '/_admin/user_groups/%(user_group_id)s/delete', ['user_group_id']);
292 pyroutes.register('user_groups_delete', '/_admin/user_groups/%(user_group_id)s/delete', ['user_group_id']);
292 pyroutes.register('search', '/_admin/search', []);
293 pyroutes.register('search', '/_admin/search', []);
293 pyroutes.register('search_repo', '/%(repo_name)s/_search', ['repo_name']);
294 pyroutes.register('search_repo', '/%(repo_name)s/_search', ['repo_name']);
294 pyroutes.register('search_repo_alt', '/%(repo_name)s/search', ['repo_name']);
295 pyroutes.register('search_repo_alt', '/%(repo_name)s/search', ['repo_name']);
295 pyroutes.register('search_repo_group', '/%(repo_group_name)s/_search', ['repo_group_name']);
296 pyroutes.register('search_repo_group', '/%(repo_group_name)s/_search', ['repo_group_name']);
296 pyroutes.register('user_profile', '/_profiles/%(username)s', ['username']);
297 pyroutes.register('user_profile', '/_profiles/%(username)s', ['username']);
297 pyroutes.register('user_group_profile', '/_profile_user_group/%(user_group_name)s', ['user_group_name']);
298 pyroutes.register('user_group_profile', '/_profile_user_group/%(user_group_name)s', ['user_group_name']);
298 pyroutes.register('my_account_profile', '/_admin/my_account/profile', []);
299 pyroutes.register('my_account_profile', '/_admin/my_account/profile', []);
299 pyroutes.register('my_account_edit', '/_admin/my_account/edit', []);
300 pyroutes.register('my_account_edit', '/_admin/my_account/edit', []);
300 pyroutes.register('my_account_update', '/_admin/my_account/update', []);
301 pyroutes.register('my_account_update', '/_admin/my_account/update', []);
301 pyroutes.register('my_account_password', '/_admin/my_account/password', []);
302 pyroutes.register('my_account_password', '/_admin/my_account/password', []);
302 pyroutes.register('my_account_password_update', '/_admin/my_account/password/update', []);
303 pyroutes.register('my_account_password_update', '/_admin/my_account/password/update', []);
303 pyroutes.register('my_account_auth_tokens_delete', '/_admin/my_account/auth_tokens/delete', []);
304 pyroutes.register('my_account_auth_tokens_delete', '/_admin/my_account/auth_tokens/delete', []);
304 pyroutes.register('my_account_ssh_keys', '/_admin/my_account/ssh_keys', []);
305 pyroutes.register('my_account_ssh_keys', '/_admin/my_account/ssh_keys', []);
305 pyroutes.register('my_account_ssh_keys_generate', '/_admin/my_account/ssh_keys/generate', []);
306 pyroutes.register('my_account_ssh_keys_generate', '/_admin/my_account/ssh_keys/generate', []);
306 pyroutes.register('my_account_ssh_keys_add', '/_admin/my_account/ssh_keys/new', []);
307 pyroutes.register('my_account_ssh_keys_add', '/_admin/my_account/ssh_keys/new', []);
307 pyroutes.register('my_account_ssh_keys_delete', '/_admin/my_account/ssh_keys/delete', []);
308 pyroutes.register('my_account_ssh_keys_delete', '/_admin/my_account/ssh_keys/delete', []);
308 pyroutes.register('my_account_user_group_membership', '/_admin/my_account/user_group_membership', []);
309 pyroutes.register('my_account_user_group_membership', '/_admin/my_account/user_group_membership', []);
309 pyroutes.register('my_account_emails', '/_admin/my_account/emails', []);
310 pyroutes.register('my_account_emails', '/_admin/my_account/emails', []);
310 pyroutes.register('my_account_emails_add', '/_admin/my_account/emails/new', []);
311 pyroutes.register('my_account_emails_add', '/_admin/my_account/emails/new', []);
311 pyroutes.register('my_account_emails_delete', '/_admin/my_account/emails/delete', []);
312 pyroutes.register('my_account_emails_delete', '/_admin/my_account/emails/delete', []);
312 pyroutes.register('my_account_repos', '/_admin/my_account/repos', []);
313 pyroutes.register('my_account_repos', '/_admin/my_account/repos', []);
313 pyroutes.register('my_account_watched', '/_admin/my_account/watched', []);
314 pyroutes.register('my_account_watched', '/_admin/my_account/watched', []);
314 pyroutes.register('my_account_bookmarks', '/_admin/my_account/bookmarks', []);
315 pyroutes.register('my_account_bookmarks', '/_admin/my_account/bookmarks', []);
315 pyroutes.register('my_account_bookmarks_update', '/_admin/my_account/bookmarks/update', []);
316 pyroutes.register('my_account_bookmarks_update', '/_admin/my_account/bookmarks/update', []);
316 pyroutes.register('my_account_goto_bookmark', '/_admin/my_account/bookmark/%(bookmark_id)s', ['bookmark_id']);
317 pyroutes.register('my_account_goto_bookmark', '/_admin/my_account/bookmark/%(bookmark_id)s', ['bookmark_id']);
317 pyroutes.register('my_account_perms', '/_admin/my_account/perms', []);
318 pyroutes.register('my_account_perms', '/_admin/my_account/perms', []);
318 pyroutes.register('my_account_notifications', '/_admin/my_account/notifications', []);
319 pyroutes.register('my_account_notifications', '/_admin/my_account/notifications', []);
319 pyroutes.register('my_account_notifications_toggle_visibility', '/_admin/my_account/toggle_visibility', []);
320 pyroutes.register('my_account_notifications_toggle_visibility', '/_admin/my_account/toggle_visibility', []);
320 pyroutes.register('my_account_pullrequests', '/_admin/my_account/pull_requests', []);
321 pyroutes.register('my_account_pullrequests', '/_admin/my_account/pull_requests', []);
321 pyroutes.register('my_account_pullrequests_data', '/_admin/my_account/pull_requests/data', []);
322 pyroutes.register('my_account_pullrequests_data', '/_admin/my_account/pull_requests/data', []);
322 pyroutes.register('notifications_show_all', '/_admin/notifications', []);
323 pyroutes.register('notifications_show_all', '/_admin/notifications', []);
323 pyroutes.register('notifications_mark_all_read', '/_admin/notifications/mark_all_read', []);
324 pyroutes.register('notifications_mark_all_read', '/_admin/notifications/mark_all_read', []);
324 pyroutes.register('notifications_show', '/_admin/notifications/%(notification_id)s', ['notification_id']);
325 pyroutes.register('notifications_show', '/_admin/notifications/%(notification_id)s', ['notification_id']);
325 pyroutes.register('notifications_update', '/_admin/notifications/%(notification_id)s/update', ['notification_id']);
326 pyroutes.register('notifications_update', '/_admin/notifications/%(notification_id)s/update', ['notification_id']);
326 pyroutes.register('notifications_delete', '/_admin/notifications/%(notification_id)s/delete', ['notification_id']);
327 pyroutes.register('notifications_delete', '/_admin/notifications/%(notification_id)s/delete', ['notification_id']);
327 pyroutes.register('my_account_notifications_test_channelstream', '/_admin/my_account/test_channelstream', []);
328 pyroutes.register('my_account_notifications_test_channelstream', '/_admin/my_account/test_channelstream', []);
328 pyroutes.register('gists_show', '/_admin/gists', []);
329 pyroutes.register('gists_show', '/_admin/gists', []);
329 pyroutes.register('gists_new', '/_admin/gists/new', []);
330 pyroutes.register('gists_new', '/_admin/gists/new', []);
330 pyroutes.register('gists_create', '/_admin/gists/create', []);
331 pyroutes.register('gists_create', '/_admin/gists/create', []);
331 pyroutes.register('gist_show', '/_admin/gists/%(gist_id)s', ['gist_id']);
332 pyroutes.register('gist_show', '/_admin/gists/%(gist_id)s', ['gist_id']);
332 pyroutes.register('gist_delete', '/_admin/gists/%(gist_id)s/delete', ['gist_id']);
333 pyroutes.register('gist_delete', '/_admin/gists/%(gist_id)s/delete', ['gist_id']);
333 pyroutes.register('gist_edit', '/_admin/gists/%(gist_id)s/edit', ['gist_id']);
334 pyroutes.register('gist_edit', '/_admin/gists/%(gist_id)s/edit', ['gist_id']);
334 pyroutes.register('gist_edit_check_revision', '/_admin/gists/%(gist_id)s/edit/check_revision', ['gist_id']);
335 pyroutes.register('gist_edit_check_revision', '/_admin/gists/%(gist_id)s/edit/check_revision', ['gist_id']);
335 pyroutes.register('gist_update', '/_admin/gists/%(gist_id)s/update', ['gist_id']);
336 pyroutes.register('gist_update', '/_admin/gists/%(gist_id)s/update', ['gist_id']);
336 pyroutes.register('gist_show_rev', '/_admin/gists/%(gist_id)s/%(revision)s', ['gist_id', 'revision']);
337 pyroutes.register('gist_show_rev', '/_admin/gists/%(gist_id)s/%(revision)s', ['gist_id', 'revision']);
337 pyroutes.register('gist_show_formatted', '/_admin/gists/%(gist_id)s/%(revision)s/%(format)s', ['gist_id', 'revision', 'format']);
338 pyroutes.register('gist_show_formatted', '/_admin/gists/%(gist_id)s/%(revision)s/%(format)s', ['gist_id', 'revision', 'format']);
338 pyroutes.register('gist_show_formatted_path', '/_admin/gists/%(gist_id)s/%(revision)s/%(format)s/%(f_path)s', ['gist_id', 'revision', 'format', 'f_path']);
339 pyroutes.register('gist_show_formatted_path', '/_admin/gists/%(gist_id)s/%(revision)s/%(format)s/%(f_path)s', ['gist_id', 'revision', 'format', 'f_path']);
339 pyroutes.register('debug_style_home', '/_admin/debug_style', []);
340 pyroutes.register('debug_style_home', '/_admin/debug_style', []);
340 pyroutes.register('debug_style_template', '/_admin/debug_style/t/%(t_path)s', ['t_path']);
341 pyroutes.register('debug_style_template', '/_admin/debug_style/t/%(t_path)s', ['t_path']);
341 pyroutes.register('apiv2', '/_admin/api', []);
342 pyroutes.register('apiv2', '/_admin/api', []);
342 pyroutes.register('admin_settings_license', '/_admin/settings/license', []);
343 pyroutes.register('admin_settings_license', '/_admin/settings/license', []);
343 pyroutes.register('admin_settings_license_unlock', '/_admin/settings/license_unlock', []);
344 pyroutes.register('admin_settings_license_unlock', '/_admin/settings/license_unlock', []);
344 pyroutes.register('login', '/_admin/login', []);
345 pyroutes.register('login', '/_admin/login', []);
345 pyroutes.register('register', '/_admin/register', []);
346 pyroutes.register('register', '/_admin/register', []);
346 pyroutes.register('repo_reviewers_review_rule_new', '/%(repo_name)s/settings/review/rules/new', ['repo_name']);
347 pyroutes.register('repo_reviewers_review_rule_new', '/%(repo_name)s/settings/review/rules/new', ['repo_name']);
347 pyroutes.register('repo_reviewers_review_rule_edit', '/%(repo_name)s/settings/review/rules/%(rule_id)s', ['repo_name', 'rule_id']);
348 pyroutes.register('repo_reviewers_review_rule_edit', '/%(repo_name)s/settings/review/rules/%(rule_id)s', ['repo_name', 'rule_id']);
348 pyroutes.register('repo_reviewers_review_rule_delete', '/%(repo_name)s/settings/review/rules/%(rule_id)s/delete', ['repo_name', 'rule_id']);
349 pyroutes.register('repo_reviewers_review_rule_delete', '/%(repo_name)s/settings/review/rules/%(rule_id)s/delete', ['repo_name', 'rule_id']);
349 pyroutes.register('plugin_admin_chat', '/_admin/plugin_admin_chat/%(action)s', ['action']);
350 pyroutes.register('plugin_admin_chat', '/_admin/plugin_admin_chat/%(action)s', ['action']);
350 pyroutes.register('edit_user_auth_tokens', '/_admin/users/%(user_id)s/edit/auth_tokens', ['user_id']);
351 pyroutes.register('edit_user_auth_tokens', '/_admin/users/%(user_id)s/edit/auth_tokens', ['user_id']);
351 pyroutes.register('edit_user_auth_tokens_add', '/_admin/users/%(user_id)s/edit/auth_tokens/new', ['user_id']);
352 pyroutes.register('edit_user_auth_tokens_add', '/_admin/users/%(user_id)s/edit/auth_tokens/new', ['user_id']);
352 pyroutes.register('admin_settings_scheduler_show_tasks', '/_admin/settings/scheduler/_tasks', []);
353 pyroutes.register('admin_settings_scheduler_show_tasks', '/_admin/settings/scheduler/_tasks', []);
353 pyroutes.register('admin_settings_scheduler_show_all', '/_admin/settings/scheduler', []);
354 pyroutes.register('admin_settings_scheduler_show_all', '/_admin/settings/scheduler', []);
354 pyroutes.register('admin_settings_scheduler_new', '/_admin/settings/scheduler/new', []);
355 pyroutes.register('admin_settings_scheduler_new', '/_admin/settings/scheduler/new', []);
355 pyroutes.register('admin_settings_scheduler_create', '/_admin/settings/scheduler/create', []);
356 pyroutes.register('admin_settings_scheduler_create', '/_admin/settings/scheduler/create', []);
356 pyroutes.register('admin_settings_scheduler_edit', '/_admin/settings/scheduler/%(schedule_id)s', ['schedule_id']);
357 pyroutes.register('admin_settings_scheduler_edit', '/_admin/settings/scheduler/%(schedule_id)s', ['schedule_id']);
357 pyroutes.register('admin_settings_scheduler_update', '/_admin/settings/scheduler/%(schedule_id)s/update', ['schedule_id']);
358 pyroutes.register('admin_settings_scheduler_update', '/_admin/settings/scheduler/%(schedule_id)s/update', ['schedule_id']);
358 pyroutes.register('admin_settings_scheduler_delete', '/_admin/settings/scheduler/%(schedule_id)s/delete', ['schedule_id']);
359 pyroutes.register('admin_settings_scheduler_delete', '/_admin/settings/scheduler/%(schedule_id)s/delete', ['schedule_id']);
359 pyroutes.register('admin_settings_scheduler_execute', '/_admin/settings/scheduler/%(schedule_id)s/execute', ['schedule_id']);
360 pyroutes.register('admin_settings_scheduler_execute', '/_admin/settings/scheduler/%(schedule_id)s/execute', ['schedule_id']);
360 pyroutes.register('admin_settings_automation', '/_admin/settings/automation', []);
361 pyroutes.register('admin_settings_automation', '/_admin/settings/automation', []);
361 pyroutes.register('admin_settings_automation_update', '/_admin/settings/automation/%(entry_id)s/update', ['entry_id']);
362 pyroutes.register('admin_settings_automation_update', '/_admin/settings/automation/%(entry_id)s/update', ['entry_id']);
362 pyroutes.register('admin_permissions_branch', '/_admin/permissions/branch', []);
363 pyroutes.register('admin_permissions_branch', '/_admin/permissions/branch', []);
363 pyroutes.register('admin_permissions_branch_update', '/_admin/permissions/branch/update', []);
364 pyroutes.register('admin_permissions_branch_update', '/_admin/permissions/branch/update', []);
364 pyroutes.register('my_account_auth_tokens', '/_admin/my_account/auth_tokens', []);
365 pyroutes.register('my_account_auth_tokens', '/_admin/my_account/auth_tokens', []);
365 pyroutes.register('my_account_auth_tokens_add', '/_admin/my_account/auth_tokens/new', []);
366 pyroutes.register('my_account_auth_tokens_add', '/_admin/my_account/auth_tokens/new', []);
366 pyroutes.register('my_account_external_identity', '/_admin/my_account/external-identity', []);
367 pyroutes.register('my_account_external_identity', '/_admin/my_account/external-identity', []);
367 pyroutes.register('my_account_external_identity_delete', '/_admin/my_account/external-identity/delete', []);
368 pyroutes.register('my_account_external_identity_delete', '/_admin/my_account/external-identity/delete', []);
368 pyroutes.register('repo_artifacts_list', '/%(repo_name)s/artifacts', ['repo_name']);
369 pyroutes.register('repo_artifacts_list', '/%(repo_name)s/artifacts', ['repo_name']);
369 pyroutes.register('repo_artifacts_data', '/%(repo_name)s/artifacts_data', ['repo_name']);
370 pyroutes.register('repo_artifacts_data', '/%(repo_name)s/artifacts_data', ['repo_name']);
370 pyroutes.register('repo_artifacts_new', '/%(repo_name)s/artifacts/new', ['repo_name']);
371 pyroutes.register('repo_artifacts_new', '/%(repo_name)s/artifacts/new', ['repo_name']);
371 pyroutes.register('repo_artifacts_get', '/%(repo_name)s/artifacts/download/%(uid)s', ['repo_name', 'uid']);
372 pyroutes.register('repo_artifacts_get', '/%(repo_name)s/artifacts/download/%(uid)s', ['repo_name', 'uid']);
372 pyroutes.register('repo_artifacts_store', '/%(repo_name)s/artifacts/store', ['repo_name']);
373 pyroutes.register('repo_artifacts_store', '/%(repo_name)s/artifacts/store', ['repo_name']);
373 pyroutes.register('repo_artifacts_info', '/%(repo_name)s/artifacts/info/%(uid)s', ['repo_name', 'uid']);
374 pyroutes.register('repo_artifacts_info', '/%(repo_name)s/artifacts/info/%(uid)s', ['repo_name', 'uid']);
374 pyroutes.register('repo_artifacts_delete', '/%(repo_name)s/artifacts/delete/%(uid)s', ['repo_name', 'uid']);
375 pyroutes.register('repo_artifacts_delete', '/%(repo_name)s/artifacts/delete/%(uid)s', ['repo_name', 'uid']);
375 pyroutes.register('repo_automation', '/%(repo_name)s/settings/automation', ['repo_name']);
376 pyroutes.register('repo_automation', '/%(repo_name)s/settings/automation', ['repo_name']);
376 pyroutes.register('repo_automation_update', '/%(repo_name)s/settings/automation/%(entry_id)s/update', ['repo_name', 'entry_id']);
377 pyroutes.register('repo_automation_update', '/%(repo_name)s/settings/automation/%(entry_id)s/update', ['repo_name', 'entry_id']);
377 pyroutes.register('edit_repo_remote_push', '/%(repo_name)s/settings/remote/push', ['repo_name']);
378 pyroutes.register('edit_repo_remote_push', '/%(repo_name)s/settings/remote/push', ['repo_name']);
378 pyroutes.register('edit_repo_perms_branch', '/%(repo_name)s/settings/branch_permissions', ['repo_name']);
379 pyroutes.register('edit_repo_perms_branch', '/%(repo_name)s/settings/branch_permissions', ['repo_name']);
379 pyroutes.register('edit_repo_perms_branch_delete', '/%(repo_name)s/settings/branch_permissions/%(rule_id)s/delete', ['repo_name', 'rule_id']);
380 pyroutes.register('edit_repo_perms_branch_delete', '/%(repo_name)s/settings/branch_permissions/%(rule_id)s/delete', ['repo_name', 'rule_id']);
380 }
381 }
@@ -1,838 +1,927 b''
1 // # Copyright (C) 2010-2019 RhodeCode GmbH
1 // # Copyright (C) 2010-2019 RhodeCode GmbH
2 // #
2 // #
3 // # This program is free software: you can redistribute it and/or modify
3 // # This program is free software: you can redistribute it and/or modify
4 // # it under the terms of the GNU Affero General Public License, version 3
4 // # it under the terms of the GNU Affero General Public License, version 3
5 // # (only), as published by the Free Software Foundation.
5 // # (only), as published by the Free Software Foundation.
6 // #
6 // #
7 // # This program is distributed in the hope that it will be useful,
7 // # This program is distributed in the hope that it will be useful,
8 // # but WITHOUT ANY WARRANTY; without even the implied warranty of
8 // # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 // # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9 // # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 // # GNU General Public License for more details.
10 // # GNU General Public License for more details.
11 // #
11 // #
12 // # You should have received a copy of the GNU Affero General Public License
12 // # You should have received a copy of the GNU Affero General Public License
13 // # along with this program. If not, see <http://www.gnu.org/licenses/>.
13 // # along with this program. If not, see <http://www.gnu.org/licenses/>.
14 // #
14 // #
15 // # This program is dual-licensed. If you wish to learn more about the
15 // # This program is dual-licensed. If you wish to learn more about the
16 // # RhodeCode Enterprise Edition, including its added features, Support services,
16 // # RhodeCode Enterprise Edition, including its added features, Support services,
17 // # and proprietary license terms, please see https://rhodecode.com/licenses/
17 // # and proprietary license terms, please see https://rhodecode.com/licenses/
18
18
19 var firefoxAnchorFix = function() {
19 var firefoxAnchorFix = function() {
20 // hack to make anchor links behave properly on firefox, in our inline
20 // hack to make anchor links behave properly on firefox, in our inline
21 // comments generation when comments are injected firefox is misbehaving
21 // comments generation when comments are injected firefox is misbehaving
22 // when jumping to anchor links
22 // when jumping to anchor links
23 if (location.href.indexOf('#') > -1) {
23 if (location.href.indexOf('#') > -1) {
24 location.href += '';
24 location.href += '';
25 }
25 }
26 };
26 };
27
27
28 var linkifyComments = function(comments) {
28 var linkifyComments = function(comments) {
29 var firstCommentId = null;
29 var firstCommentId = null;
30 if (comments) {
30 if (comments) {
31 firstCommentId = $(comments[0]).data('comment-id');
31 firstCommentId = $(comments[0]).data('comment-id');
32 }
32 }
33
33
34 if (firstCommentId){
34 if (firstCommentId){
35 $('#inline-comments-counter').attr('href', '#comment-' + firstCommentId);
35 $('#inline-comments-counter').attr('href', '#comment-' + firstCommentId);
36 }
36 }
37 };
37 };
38
38
39 var bindToggleButtons = function() {
39 var bindToggleButtons = function() {
40 $('.comment-toggle').on('click', function() {
40 $('.comment-toggle').on('click', function() {
41 $(this).parent().nextUntil('tr.line').toggle('inline-comments');
41 $(this).parent().nextUntil('tr.line').toggle('inline-comments');
42 });
42 });
43 };
43 };
44
44
45
45
46
46
47 var _submitAjaxPOST = function(url, postData, successHandler, failHandler) {
47 var _submitAjaxPOST = function(url, postData, successHandler, failHandler) {
48 failHandler = failHandler || function() {};
48 failHandler = failHandler || function() {};
49 postData = toQueryString(postData);
49 postData = toQueryString(postData);
50 var request = $.ajax({
50 var request = $.ajax({
51 url: url,
51 url: url,
52 type: 'POST',
52 type: 'POST',
53 data: postData,
53 data: postData,
54 headers: {'X-PARTIAL-XHR': true}
54 headers: {'X-PARTIAL-XHR': true}
55 })
55 })
56 .done(function (data) {
56 .done(function (data) {
57 successHandler(data);
57 successHandler(data);
58 })
58 })
59 .fail(function (data, textStatus, errorThrown) {
59 .fail(function (data, textStatus, errorThrown) {
60 failHandler(data, textStatus, errorThrown)
60 failHandler(data, textStatus, errorThrown)
61 });
61 });
62 return request;
62 return request;
63 };
63 };
64
64
65
65
66
66
67
67
68 /* Comment form for main and inline comments */
68 /* Comment form for main and inline comments */
69 (function(mod) {
69 (function(mod) {
70
70
71 if (typeof exports == "object" && typeof module == "object") {
71 if (typeof exports == "object" && typeof module == "object") {
72 // CommonJS
72 // CommonJS
73 module.exports = mod();
73 module.exports = mod();
74 }
74 }
75 else {
75 else {
76 // Plain browser env
76 // Plain browser env
77 (this || window).CommentForm = mod();
77 (this || window).CommentForm = mod();
78 }
78 }
79
79
80 })(function() {
80 })(function() {
81 "use strict";
81 "use strict";
82
82
83 function CommentForm(formElement, commitId, pullRequestId, lineNo, initAutocompleteActions, resolvesCommentId) {
83 function CommentForm(formElement, commitId, pullRequestId, lineNo, initAutocompleteActions, resolvesCommentId) {
84 if (!(this instanceof CommentForm)) {
84 if (!(this instanceof CommentForm)) {
85 return new CommentForm(formElement, commitId, pullRequestId, lineNo, initAutocompleteActions, resolvesCommentId);
85 return new CommentForm(formElement, commitId, pullRequestId, lineNo, initAutocompleteActions, resolvesCommentId);
86 }
86 }
87
87
88 // bind the element instance to our Form
88 // bind the element instance to our Form
89 $(formElement).get(0).CommentForm = this;
89 $(formElement).get(0).CommentForm = this;
90
90
91 this.withLineNo = function(selector) {
91 this.withLineNo = function(selector) {
92 var lineNo = this.lineNo;
92 var lineNo = this.lineNo;
93 if (lineNo === undefined) {
93 if (lineNo === undefined) {
94 return selector
94 return selector
95 } else {
95 } else {
96 return selector + '_' + lineNo;
96 return selector + '_' + lineNo;
97 }
97 }
98 };
98 };
99
99
100 this.commitId = commitId;
100 this.commitId = commitId;
101 this.pullRequestId = pullRequestId;
101 this.pullRequestId = pullRequestId;
102 this.lineNo = lineNo;
102 this.lineNo = lineNo;
103 this.initAutocompleteActions = initAutocompleteActions;
103 this.initAutocompleteActions = initAutocompleteActions;
104
104
105 this.previewButton = this.withLineNo('#preview-btn');
105 this.previewButton = this.withLineNo('#preview-btn');
106 this.previewContainer = this.withLineNo('#preview-container');
106 this.previewContainer = this.withLineNo('#preview-container');
107
107
108 this.previewBoxSelector = this.withLineNo('#preview-box');
108 this.previewBoxSelector = this.withLineNo('#preview-box');
109
109
110 this.editButton = this.withLineNo('#edit-btn');
110 this.editButton = this.withLineNo('#edit-btn');
111 this.editContainer = this.withLineNo('#edit-container');
111 this.editContainer = this.withLineNo('#edit-container');
112 this.cancelButton = this.withLineNo('#cancel-btn');
112 this.cancelButton = this.withLineNo('#cancel-btn');
113 this.commentType = this.withLineNo('#comment_type');
113 this.commentType = this.withLineNo('#comment_type');
114
114
115 this.resolvesId = null;
115 this.resolvesId = null;
116 this.resolvesActionId = null;
116 this.resolvesActionId = null;
117
117
118 this.closesPr = '#close_pull_request';
118 this.closesPr = '#close_pull_request';
119
119
120 this.cmBox = this.withLineNo('#text');
120 this.cmBox = this.withLineNo('#text');
121 this.cm = initCommentBoxCodeMirror(this, this.cmBox, this.initAutocompleteActions);
121 this.cm = initCommentBoxCodeMirror(this, this.cmBox, this.initAutocompleteActions);
122
122
123 this.statusChange = this.withLineNo('#change_status');
123 this.statusChange = this.withLineNo('#change_status');
124
124
125 this.submitForm = formElement;
125 this.submitForm = formElement;
126 this.submitButton = $(this.submitForm).find('input[type="submit"]');
126 this.submitButton = $(this.submitForm).find('input[type="submit"]');
127 this.submitButtonText = this.submitButton.val();
127 this.submitButtonText = this.submitButton.val();
128
128
129 this.previewUrl = pyroutes.url('repo_commit_comment_preview',
129 this.previewUrl = pyroutes.url('repo_commit_comment_preview',
130 {'repo_name': templateContext.repo_name,
130 {'repo_name': templateContext.repo_name,
131 'commit_id': templateContext.commit_data.commit_id});
131 'commit_id': templateContext.commit_data.commit_id});
132
132
133 if (resolvesCommentId){
133 if (resolvesCommentId){
134 this.resolvesId = '#resolve_comment_{0}'.format(resolvesCommentId);
134 this.resolvesId = '#resolve_comment_{0}'.format(resolvesCommentId);
135 this.resolvesActionId = '#resolve_comment_action_{0}'.format(resolvesCommentId);
135 this.resolvesActionId = '#resolve_comment_action_{0}'.format(resolvesCommentId);
136 $(this.commentType).prop('disabled', true);
136 $(this.commentType).prop('disabled', true);
137 $(this.commentType).addClass('disabled');
137 $(this.commentType).addClass('disabled');
138
138
139 // disable select
139 // disable select
140 setTimeout(function() {
140 setTimeout(function() {
141 $(self.statusChange).select2('readonly', true);
141 $(self.statusChange).select2('readonly', true);
142 }, 10);
142 }, 10);
143
143
144 var resolvedInfo = (
144 var resolvedInfo = (
145 '<li class="resolve-action">' +
145 '<li class="resolve-action">' +
146 '<input type="hidden" id="resolve_comment_{0}" name="resolve_comment_{0}" value="{0}">' +
146 '<input type="hidden" id="resolve_comment_{0}" name="resolve_comment_{0}" value="{0}">' +
147 '<button id="resolve_comment_action_{0}" class="resolve-text btn btn-sm" onclick="return Rhodecode.comments.submitResolution({0})">{1} #{0}</button>' +
147 '<button id="resolve_comment_action_{0}" class="resolve-text btn btn-sm" onclick="return Rhodecode.comments.submitResolution({0})">{1} #{0}</button>' +
148 '</li>'
148 '</li>'
149 ).format(resolvesCommentId, _gettext('resolve comment'));
149 ).format(resolvesCommentId, _gettext('resolve comment'));
150 $(resolvedInfo).insertAfter($(this.commentType).parent());
150 $(resolvedInfo).insertAfter($(this.commentType).parent());
151 }
151 }
152
152
153 // based on commitId, or pullRequestId decide where do we submit
153 // based on commitId, or pullRequestId decide where do we submit
154 // out data
154 // out data
155 if (this.commitId){
155 if (this.commitId){
156 this.submitUrl = pyroutes.url('repo_commit_comment_create',
156 this.submitUrl = pyroutes.url('repo_commit_comment_create',
157 {'repo_name': templateContext.repo_name,
157 {'repo_name': templateContext.repo_name,
158 'commit_id': this.commitId});
158 'commit_id': this.commitId});
159 this.selfUrl = pyroutes.url('repo_commit',
159 this.selfUrl = pyroutes.url('repo_commit',
160 {'repo_name': templateContext.repo_name,
160 {'repo_name': templateContext.repo_name,
161 'commit_id': this.commitId});
161 'commit_id': this.commitId});
162
162
163 } else if (this.pullRequestId) {
163 } else if (this.pullRequestId) {
164 this.submitUrl = pyroutes.url('pullrequest_comment_create',
164 this.submitUrl = pyroutes.url('pullrequest_comment_create',
165 {'repo_name': templateContext.repo_name,
165 {'repo_name': templateContext.repo_name,
166 'pull_request_id': this.pullRequestId});
166 'pull_request_id': this.pullRequestId});
167 this.selfUrl = pyroutes.url('pullrequest_show',
167 this.selfUrl = pyroutes.url('pullrequest_show',
168 {'repo_name': templateContext.repo_name,
168 {'repo_name': templateContext.repo_name,
169 'pull_request_id': this.pullRequestId});
169 'pull_request_id': this.pullRequestId});
170
170
171 } else {
171 } else {
172 throw new Error(
172 throw new Error(
173 'CommentForm requires pullRequestId, or commitId to be specified.')
173 'CommentForm requires pullRequestId, or commitId to be specified.')
174 }
174 }
175
175
176 // FUNCTIONS and helpers
176 // FUNCTIONS and helpers
177 var self = this;
177 var self = this;
178
178
179 this.isInline = function(){
179 this.isInline = function(){
180 return this.lineNo && this.lineNo != 'general';
180 return this.lineNo && this.lineNo != 'general';
181 };
181 };
182
182
183 this.getCmInstance = function(){
183 this.getCmInstance = function(){
184 return this.cm
184 return this.cm
185 };
185 };
186
186
187 this.setPlaceholder = function(placeholder) {
187 this.setPlaceholder = function(placeholder) {
188 var cm = this.getCmInstance();
188 var cm = this.getCmInstance();
189 if (cm){
189 if (cm){
190 cm.setOption('placeholder', placeholder);
190 cm.setOption('placeholder', placeholder);
191 }
191 }
192 };
192 };
193
193
194 this.getCommentStatus = function() {
194 this.getCommentStatus = function() {
195 return $(this.submitForm).find(this.statusChange).val();
195 return $(this.submitForm).find(this.statusChange).val();
196 };
196 };
197 this.getCommentType = function() {
197 this.getCommentType = function() {
198 return $(this.submitForm).find(this.commentType).val();
198 return $(this.submitForm).find(this.commentType).val();
199 };
199 };
200
200
201 this.getResolvesId = function() {
201 this.getResolvesId = function() {
202 return $(this.submitForm).find(this.resolvesId).val() || null;
202 return $(this.submitForm).find(this.resolvesId).val() || null;
203 };
203 };
204
204
205 this.getClosePr = function() {
205 this.getClosePr = function() {
206 return $(this.submitForm).find(this.closesPr).val() || null;
206 return $(this.submitForm).find(this.closesPr).val() || null;
207 };
207 };
208
208
209 this.markCommentResolved = function(resolvedCommentId){
209 this.markCommentResolved = function(resolvedCommentId){
210 $('#comment-label-{0}'.format(resolvedCommentId)).find('.resolved').show();
210 $('#comment-label-{0}'.format(resolvedCommentId)).find('.resolved').show();
211 $('#comment-label-{0}'.format(resolvedCommentId)).find('.resolve').hide();
211 $('#comment-label-{0}'.format(resolvedCommentId)).find('.resolve').hide();
212 };
212 };
213
213
214 this.isAllowedToSubmit = function() {
214 this.isAllowedToSubmit = function() {
215 return !$(this.submitButton).prop('disabled');
215 return !$(this.submitButton).prop('disabled');
216 };
216 };
217
217
218 this.initStatusChangeSelector = function(){
218 this.initStatusChangeSelector = function(){
219 var formatChangeStatus = function(state, escapeMarkup) {
219 var formatChangeStatus = function(state, escapeMarkup) {
220 var originalOption = state.element;
220 var originalOption = state.element;
221 var tmpl = '<i class="icon-circle review-status-{0}"></i><span>{1}</span>'.format($(originalOption).data('status'), escapeMarkup(state.text));
221 var tmpl = '<i class="icon-circle review-status-{0}"></i><span>{1}</span>'.format($(originalOption).data('status'), escapeMarkup(state.text));
222 return tmpl
222 return tmpl
223 };
223 };
224 var formatResult = function(result, container, query, escapeMarkup) {
224 var formatResult = function(result, container, query, escapeMarkup) {
225 return formatChangeStatus(result, escapeMarkup);
225 return formatChangeStatus(result, escapeMarkup);
226 };
226 };
227
227
228 var formatSelection = function(data, container, escapeMarkup) {
228 var formatSelection = function(data, container, escapeMarkup) {
229 return formatChangeStatus(data, escapeMarkup);
229 return formatChangeStatus(data, escapeMarkup);
230 };
230 };
231
231
232 $(this.submitForm).find(this.statusChange).select2({
232 $(this.submitForm).find(this.statusChange).select2({
233 placeholder: _gettext('Status Review'),
233 placeholder: _gettext('Status Review'),
234 formatResult: formatResult,
234 formatResult: formatResult,
235 formatSelection: formatSelection,
235 formatSelection: formatSelection,
236 containerCssClass: "drop-menu status_box_menu",
236 containerCssClass: "drop-menu status_box_menu",
237 dropdownCssClass: "drop-menu-dropdown",
237 dropdownCssClass: "drop-menu-dropdown",
238 dropdownAutoWidth: true,
238 dropdownAutoWidth: true,
239 minimumResultsForSearch: -1
239 minimumResultsForSearch: -1
240 });
240 });
241 $(this.submitForm).find(this.statusChange).on('change', function() {
241 $(this.submitForm).find(this.statusChange).on('change', function() {
242 var status = self.getCommentStatus();
242 var status = self.getCommentStatus();
243
243
244 if (status && !self.isInline()) {
244 if (status && !self.isInline()) {
245 $(self.submitButton).prop('disabled', false);
245 $(self.submitButton).prop('disabled', false);
246 }
246 }
247
247
248 var placeholderText = _gettext('Comment text will be set automatically based on currently selected status ({0}) ...').format(status);
248 var placeholderText = _gettext('Comment text will be set automatically based on currently selected status ({0}) ...').format(status);
249 self.setPlaceholder(placeholderText)
249 self.setPlaceholder(placeholderText)
250 })
250 })
251 };
251 };
252
252
253 // reset the comment form into it's original state
253 // reset the comment form into it's original state
254 this.resetCommentFormState = function(content) {
254 this.resetCommentFormState = function(content) {
255 content = content || '';
255 content = content || '';
256
256
257 $(this.editContainer).show();
257 $(this.editContainer).show();
258 $(this.editButton).parent().addClass('active');
258 $(this.editButton).parent().addClass('active');
259
259
260 $(this.previewContainer).hide();
260 $(this.previewContainer).hide();
261 $(this.previewButton).parent().removeClass('active');
261 $(this.previewButton).parent().removeClass('active');
262
262
263 this.setActionButtonsDisabled(true);
263 this.setActionButtonsDisabled(true);
264 self.cm.setValue(content);
264 self.cm.setValue(content);
265 self.cm.setOption("readOnly", false);
265 self.cm.setOption("readOnly", false);
266
266
267 if (this.resolvesId) {
267 if (this.resolvesId) {
268 // destroy the resolve action
268 // destroy the resolve action
269 $(this.resolvesId).parent().remove();
269 $(this.resolvesId).parent().remove();
270 }
270 }
271 // reset closingPR flag
271 // reset closingPR flag
272 $('.close-pr-input').remove();
272 $('.close-pr-input').remove();
273
273
274 $(this.statusChange).select2('readonly', false);
274 $(this.statusChange).select2('readonly', false);
275 };
275 };
276
276
277 this.globalSubmitSuccessCallback = function(){
277 this.globalSubmitSuccessCallback = function(){
278 // default behaviour is to call GLOBAL hook, if it's registered.
278 // default behaviour is to call GLOBAL hook, if it's registered.
279 if (window.commentFormGlobalSubmitSuccessCallback !== undefined){
279 if (window.commentFormGlobalSubmitSuccessCallback !== undefined){
280 commentFormGlobalSubmitSuccessCallback()
280 commentFormGlobalSubmitSuccessCallback()
281 }
281 }
282 };
282 };
283
283
284 this.submitAjaxPOST = function(url, postData, successHandler, failHandler) {
284 this.submitAjaxPOST = function(url, postData, successHandler, failHandler) {
285 return _submitAjaxPOST(url, postData, successHandler, failHandler);
285 return _submitAjaxPOST(url, postData, successHandler, failHandler);
286 };
286 };
287
287
288 // overwrite a submitHandler, we need to do it for inline comments
288 // overwrite a submitHandler, we need to do it for inline comments
289 this.setHandleFormSubmit = function(callback) {
289 this.setHandleFormSubmit = function(callback) {
290 this.handleFormSubmit = callback;
290 this.handleFormSubmit = callback;
291 };
291 };
292
292
293 // overwrite a submitSuccessHandler
293 // overwrite a submitSuccessHandler
294 this.setGlobalSubmitSuccessCallback = function(callback) {
294 this.setGlobalSubmitSuccessCallback = function(callback) {
295 this.globalSubmitSuccessCallback = callback;
295 this.globalSubmitSuccessCallback = callback;
296 };
296 };
297
297
298 // default handler for for submit for main comments
298 // default handler for for submit for main comments
299 this.handleFormSubmit = function() {
299 this.handleFormSubmit = function() {
300 var text = self.cm.getValue();
300 var text = self.cm.getValue();
301 var status = self.getCommentStatus();
301 var status = self.getCommentStatus();
302 var commentType = self.getCommentType();
302 var commentType = self.getCommentType();
303 var resolvesCommentId = self.getResolvesId();
303 var resolvesCommentId = self.getResolvesId();
304 var closePullRequest = self.getClosePr();
304 var closePullRequest = self.getClosePr();
305
305
306 if (text === "" && !status) {
306 if (text === "" && !status) {
307 return;
307 return;
308 }
308 }
309
309
310 var excludeCancelBtn = false;
310 var excludeCancelBtn = false;
311 var submitEvent = true;
311 var submitEvent = true;
312 self.setActionButtonsDisabled(true, excludeCancelBtn, submitEvent);
312 self.setActionButtonsDisabled(true, excludeCancelBtn, submitEvent);
313 self.cm.setOption("readOnly", true);
313 self.cm.setOption("readOnly", true);
314
314
315 var postData = {
315 var postData = {
316 'text': text,
316 'text': text,
317 'changeset_status': status,
317 'changeset_status': status,
318 'comment_type': commentType,
318 'comment_type': commentType,
319 'csrf_token': CSRF_TOKEN
319 'csrf_token': CSRF_TOKEN
320 };
320 };
321
321
322 if (resolvesCommentId) {
322 if (resolvesCommentId) {
323 postData['resolves_comment_id'] = resolvesCommentId;
323 postData['resolves_comment_id'] = resolvesCommentId;
324 }
324 }
325
325
326 if (closePullRequest) {
326 if (closePullRequest) {
327 postData['close_pull_request'] = true;
327 postData['close_pull_request'] = true;
328 }
328 }
329
329
330 var submitSuccessCallback = function(o) {
330 var submitSuccessCallback = function(o) {
331 // reload page if we change status for single commit.
331 // reload page if we change status for single commit.
332 if (status && self.commitId) {
332 if (status && self.commitId) {
333 location.reload(true);
333 location.reload(true);
334 } else {
334 } else {
335 $('#injected_page_comments').append(o.rendered_text);
335 $('#injected_page_comments').append(o.rendered_text);
336 self.resetCommentFormState();
336 self.resetCommentFormState();
337 timeagoActivate();
337 timeagoActivate();
338
338
339 // mark visually which comment was resolved
339 // mark visually which comment was resolved
340 if (resolvesCommentId) {
340 if (resolvesCommentId) {
341 self.markCommentResolved(resolvesCommentId);
341 self.markCommentResolved(resolvesCommentId);
342 }
342 }
343 }
343 }
344
344
345 // run global callback on submit
345 // run global callback on submit
346 self.globalSubmitSuccessCallback();
346 self.globalSubmitSuccessCallback();
347
347
348 };
348 };
349 var submitFailCallback = function(data) {
349 var submitFailCallback = function(data) {
350 alert(
350 alert(
351 "Error while submitting comment.\n" +
351 "Error while submitting comment.\n" +
352 "Error code {0} ({1}).".format(data.status, data.statusText)
352 "Error code {0} ({1}).".format(data.status, data.statusText)
353 );
353 );
354 self.resetCommentFormState(text);
354 self.resetCommentFormState(text);
355 };
355 };
356 self.submitAjaxPOST(
356 self.submitAjaxPOST(
357 self.submitUrl, postData, submitSuccessCallback, submitFailCallback);
357 self.submitUrl, postData, submitSuccessCallback, submitFailCallback);
358 };
358 };
359
359
360 this.previewSuccessCallback = function(o) {
360 this.previewSuccessCallback = function(o) {
361 $(self.previewBoxSelector).html(o);
361 $(self.previewBoxSelector).html(o);
362 $(self.previewBoxSelector).removeClass('unloaded');
362 $(self.previewBoxSelector).removeClass('unloaded');
363
363
364 // swap buttons, making preview active
364 // swap buttons, making preview active
365 $(self.previewButton).parent().addClass('active');
365 $(self.previewButton).parent().addClass('active');
366 $(self.editButton).parent().removeClass('active');
366 $(self.editButton).parent().removeClass('active');
367
367
368 // unlock buttons
368 // unlock buttons
369 self.setActionButtonsDisabled(false);
369 self.setActionButtonsDisabled(false);
370 };
370 };
371
371
372 this.setActionButtonsDisabled = function(state, excludeCancelBtn, submitEvent) {
372 this.setActionButtonsDisabled = function(state, excludeCancelBtn, submitEvent) {
373 excludeCancelBtn = excludeCancelBtn || false;
373 excludeCancelBtn = excludeCancelBtn || false;
374 submitEvent = submitEvent || false;
374 submitEvent = submitEvent || false;
375
375
376 $(this.editButton).prop('disabled', state);
376 $(this.editButton).prop('disabled', state);
377 $(this.previewButton).prop('disabled', state);
377 $(this.previewButton).prop('disabled', state);
378
378
379 if (!excludeCancelBtn) {
379 if (!excludeCancelBtn) {
380 $(this.cancelButton).prop('disabled', state);
380 $(this.cancelButton).prop('disabled', state);
381 }
381 }
382
382
383 var submitState = state;
383 var submitState = state;
384 if (!submitEvent && this.getCommentStatus() && !self.isInline()) {
384 if (!submitEvent && this.getCommentStatus() && !self.isInline()) {
385 // if the value of commit review status is set, we allow
385 // if the value of commit review status is set, we allow
386 // submit button, but only on Main form, isInline means inline
386 // submit button, but only on Main form, isInline means inline
387 submitState = false
387 submitState = false
388 }
388 }
389
389
390 $(this.submitButton).prop('disabled', submitState);
390 $(this.submitButton).prop('disabled', submitState);
391 if (submitEvent) {
391 if (submitEvent) {
392 $(this.submitButton).val(_gettext('Submitting...'));
392 $(this.submitButton).val(_gettext('Submitting...'));
393 } else {
393 } else {
394 $(this.submitButton).val(this.submitButtonText);
394 $(this.submitButton).val(this.submitButtonText);
395 }
395 }
396
396
397 };
397 };
398
398
399 // lock preview/edit/submit buttons on load, but exclude cancel button
399 // lock preview/edit/submit buttons on load, but exclude cancel button
400 var excludeCancelBtn = true;
400 var excludeCancelBtn = true;
401 this.setActionButtonsDisabled(true, excludeCancelBtn);
401 this.setActionButtonsDisabled(true, excludeCancelBtn);
402
402
403 // anonymous users don't have access to initialized CM instance
403 // anonymous users don't have access to initialized CM instance
404 if (this.cm !== undefined){
404 if (this.cm !== undefined){
405 this.cm.on('change', function(cMirror) {
405 this.cm.on('change', function(cMirror) {
406 if (cMirror.getValue() === "") {
406 if (cMirror.getValue() === "") {
407 self.setActionButtonsDisabled(true, excludeCancelBtn)
407 self.setActionButtonsDisabled(true, excludeCancelBtn)
408 } else {
408 } else {
409 self.setActionButtonsDisabled(false, excludeCancelBtn)
409 self.setActionButtonsDisabled(false, excludeCancelBtn)
410 }
410 }
411 });
411 });
412 }
412 }
413
413
414 $(this.editButton).on('click', function(e) {
414 $(this.editButton).on('click', function(e) {
415 e.preventDefault();
415 e.preventDefault();
416
416
417 $(self.previewButton).parent().removeClass('active');
417 $(self.previewButton).parent().removeClass('active');
418 $(self.previewContainer).hide();
418 $(self.previewContainer).hide();
419
419
420 $(self.editButton).parent().addClass('active');
420 $(self.editButton).parent().addClass('active');
421 $(self.editContainer).show();
421 $(self.editContainer).show();
422
422
423 });
423 });
424
424
425 $(this.previewButton).on('click', function(e) {
425 $(this.previewButton).on('click', function(e) {
426 e.preventDefault();
426 e.preventDefault();
427 var text = self.cm.getValue();
427 var text = self.cm.getValue();
428
428
429 if (text === "") {
429 if (text === "") {
430 return;
430 return;
431 }
431 }
432
432
433 var postData = {
433 var postData = {
434 'text': text,
434 'text': text,
435 'renderer': templateContext.visual.default_renderer,
435 'renderer': templateContext.visual.default_renderer,
436 'csrf_token': CSRF_TOKEN
436 'csrf_token': CSRF_TOKEN
437 };
437 };
438
438
439 // lock ALL buttons on preview
439 // lock ALL buttons on preview
440 self.setActionButtonsDisabled(true);
440 self.setActionButtonsDisabled(true);
441
441
442 $(self.previewBoxSelector).addClass('unloaded');
442 $(self.previewBoxSelector).addClass('unloaded');
443 $(self.previewBoxSelector).html(_gettext('Loading ...'));
443 $(self.previewBoxSelector).html(_gettext('Loading ...'));
444
444
445 $(self.editContainer).hide();
445 $(self.editContainer).hide();
446 $(self.previewContainer).show();
446 $(self.previewContainer).show();
447
447
448 // by default we reset state of comment preserving the text
448 // by default we reset state of comment preserving the text
449 var previewFailCallback = function(data){
449 var previewFailCallback = function(data){
450 alert(
450 alert(
451 "Error while preview of comment.\n" +
451 "Error while preview of comment.\n" +
452 "Error code {0} ({1}).".format(data.status, data.statusText)
452 "Error code {0} ({1}).".format(data.status, data.statusText)
453 );
453 );
454 self.resetCommentFormState(text)
454 self.resetCommentFormState(text)
455 };
455 };
456 self.submitAjaxPOST(
456 self.submitAjaxPOST(
457 self.previewUrl, postData, self.previewSuccessCallback,
457 self.previewUrl, postData, self.previewSuccessCallback,
458 previewFailCallback);
458 previewFailCallback);
459
459
460 $(self.previewButton).parent().addClass('active');
460 $(self.previewButton).parent().addClass('active');
461 $(self.editButton).parent().removeClass('active');
461 $(self.editButton).parent().removeClass('active');
462 });
462 });
463
463
464 $(this.submitForm).submit(function(e) {
464 $(this.submitForm).submit(function(e) {
465 e.preventDefault();
465 e.preventDefault();
466 var allowedToSubmit = self.isAllowedToSubmit();
466 var allowedToSubmit = self.isAllowedToSubmit();
467 if (!allowedToSubmit){
467 if (!allowedToSubmit){
468 return false;
468 return false;
469 }
469 }
470 self.handleFormSubmit();
470 self.handleFormSubmit();
471 });
471 });
472
472
473 }
473 }
474
474
475 return CommentForm;
475 return CommentForm;
476 });
476 });
477
477
478 /* comments controller */
478 /* comments controller */
479 var CommentsController = function() {
479 var CommentsController = function() {
480 var mainComment = '#text';
480 var mainComment = '#text';
481 var self = this;
481 var self = this;
482
482
483 this.cancelComment = function(node) {
483 this.cancelComment = function(node) {
484 var $node = $(node);
484 var $node = $(node);
485 var $td = $node.closest('td');
485 var $td = $node.closest('td');
486 $node.closest('.comment-inline-form').remove();
486 $node.closest('.comment-inline-form').remove();
487 return false;
487 return false;
488 };
488 };
489
489
490 this.getLineNumber = function(node) {
490 this.getLineNumber = function(node) {
491 var $node = $(node);
491 var $node = $(node);
492 var lineNo = $node.closest('td').attr('data-line-no');
492 var lineNo = $node.closest('td').attr('data-line-no');
493 if (lineNo === undefined && $node.data('commentInline')){
493 if (lineNo === undefined && $node.data('commentInline')){
494 lineNo = $node.data('commentLineNo')
494 lineNo = $node.data('commentLineNo')
495 }
495 }
496
496
497 return lineNo
497 return lineNo
498 };
498 };
499
499
500 this.scrollToComment = function(node, offset, outdated) {
500 this.scrollToComment = function(node, offset, outdated) {
501 if (offset === undefined) {
501 if (offset === undefined) {
502 offset = 0;
502 offset = 0;
503 }
503 }
504 var outdated = outdated || false;
504 var outdated = outdated || false;
505 var klass = outdated ? 'div.comment-outdated' : 'div.comment-current';
505 var klass = outdated ? 'div.comment-outdated' : 'div.comment-current';
506
506
507 if (!node) {
507 if (!node) {
508 node = $('.comment-selected');
508 node = $('.comment-selected');
509 if (!node.length) {
509 if (!node.length) {
510 node = $('comment-current')
510 node = $('comment-current')
511 }
511 }
512 }
512 }
513 $wrapper = $(node).closest('div.comment');
513 $wrapper = $(node).closest('div.comment');
514 $comment = $(node).closest(klass);
514 $comment = $(node).closest(klass);
515 $comments = $(klass);
515 $comments = $(klass);
516
516
517 // show hidden comment when referenced.
517 // show hidden comment when referenced.
518 if (!$wrapper.is(':visible')){
518 if (!$wrapper.is(':visible')){
519 $wrapper.show();
519 $wrapper.show();
520 }
520 }
521
521
522 $('.comment-selected').removeClass('comment-selected');
522 $('.comment-selected').removeClass('comment-selected');
523
523
524 var nextIdx = $(klass).index($comment) + offset;
524 var nextIdx = $(klass).index($comment) + offset;
525 if (nextIdx >= $comments.length) {
525 if (nextIdx >= $comments.length) {
526 nextIdx = 0;
526 nextIdx = 0;
527 }
527 }
528 var $next = $(klass).eq(nextIdx);
528 var $next = $(klass).eq(nextIdx);
529
529
530 var $cb = $next.closest('.cb');
530 var $cb = $next.closest('.cb');
531 $cb.removeClass('cb-collapsed');
531 $cb.removeClass('cb-collapsed');
532
532
533 var $filediffCollapseState = $cb.closest('.filediff').prev();
533 var $filediffCollapseState = $cb.closest('.filediff').prev();
534 $filediffCollapseState.prop('checked', false);
534 $filediffCollapseState.prop('checked', false);
535 $next.addClass('comment-selected');
535 $next.addClass('comment-selected');
536 scrollToElement($next);
536 scrollToElement($next);
537 return false;
537 return false;
538 };
538 };
539
539
540 this.nextComment = function(node) {
540 this.nextComment = function(node) {
541 return self.scrollToComment(node, 1);
541 return self.scrollToComment(node, 1);
542 };
542 };
543
543
544 this.prevComment = function(node) {
544 this.prevComment = function(node) {
545 return self.scrollToComment(node, -1);
545 return self.scrollToComment(node, -1);
546 };
546 };
547
547
548 this.nextOutdatedComment = function(node) {
548 this.nextOutdatedComment = function(node) {
549 return self.scrollToComment(node, 1, true);
549 return self.scrollToComment(node, 1, true);
550 };
550 };
551
551
552 this.prevOutdatedComment = function(node) {
552 this.prevOutdatedComment = function(node) {
553 return self.scrollToComment(node, -1, true);
553 return self.scrollToComment(node, -1, true);
554 };
554 };
555
555
556 this.deleteComment = function(node) {
556 this.deleteComment = function(node) {
557 if (!confirm(_gettext('Delete this comment?'))) {
557 if (!confirm(_gettext('Delete this comment?'))) {
558 return false;
558 return false;
559 }
559 }
560 var $node = $(node);
560 var $node = $(node);
561 var $td = $node.closest('td');
561 var $td = $node.closest('td');
562 var $comment = $node.closest('.comment');
562 var $comment = $node.closest('.comment');
563 var comment_id = $comment.attr('data-comment-id');
563 var comment_id = $comment.attr('data-comment-id');
564 var url = AJAX_COMMENT_DELETE_URL.replace('__COMMENT_ID__', comment_id);
564 var url = AJAX_COMMENT_DELETE_URL.replace('__COMMENT_ID__', comment_id);
565 var postData = {
565 var postData = {
566 'csrf_token': CSRF_TOKEN
566 'csrf_token': CSRF_TOKEN
567 };
567 };
568
568
569 $comment.addClass('comment-deleting');
569 $comment.addClass('comment-deleting');
570 $comment.hide('fast');
570 $comment.hide('fast');
571
571
572 var success = function(response) {
572 var success = function(response) {
573 $comment.remove();
573 $comment.remove();
574 return false;
574 return false;
575 };
575 };
576 var failure = function(data, textStatus, xhr) {
576 var failure = function(data, textStatus, xhr) {
577 alert("error processing request: " + textStatus);
577 alert("error processing request: " + textStatus);
578 $comment.show('fast');
578 $comment.show('fast');
579 $comment.removeClass('comment-deleting');
579 $comment.removeClass('comment-deleting');
580 return false;
580 return false;
581 };
581 };
582 ajaxPOST(url, postData, success, failure);
582 ajaxPOST(url, postData, success, failure);
583 };
583 };
584
584
585 this.toggleWideMode = function (node) {
585 this.toggleWideMode = function (node) {
586 if ($('#content').hasClass('wrapper')) {
586 if ($('#content').hasClass('wrapper')) {
587 $('#content').removeClass("wrapper");
587 $('#content').removeClass("wrapper");
588 $('#content').addClass("wide-mode-wrapper");
588 $('#content').addClass("wide-mode-wrapper");
589 $(node).addClass('btn-success');
589 $(node).addClass('btn-success');
590 return true
590 return true
591 } else {
591 } else {
592 $('#content').removeClass("wide-mode-wrapper");
592 $('#content').removeClass("wide-mode-wrapper");
593 $('#content').addClass("wrapper");
593 $('#content').addClass("wrapper");
594 $(node).removeClass('btn-success');
594 $(node).removeClass('btn-success');
595 return false
595 return false
596 }
596 }
597
597
598 };
598 };
599
599
600 this.toggleComments = function(node, show) {
600 this.toggleComments = function(node, show) {
601 var $filediff = $(node).closest('.filediff');
601 var $filediff = $(node).closest('.filediff');
602 if (show === true) {
602 if (show === true) {
603 $filediff.removeClass('hide-comments');
603 $filediff.removeClass('hide-comments');
604 } else if (show === false) {
604 } else if (show === false) {
605 $filediff.find('.hide-line-comments').removeClass('hide-line-comments');
605 $filediff.find('.hide-line-comments').removeClass('hide-line-comments');
606 $filediff.addClass('hide-comments');
606 $filediff.addClass('hide-comments');
607 } else {
607 } else {
608 $filediff.find('.hide-line-comments').removeClass('hide-line-comments');
608 $filediff.find('.hide-line-comments').removeClass('hide-line-comments');
609 $filediff.toggleClass('hide-comments');
609 $filediff.toggleClass('hide-comments');
610 }
610 }
611 return false;
611 return false;
612 };
612 };
613
613
614 this.toggleLineComments = function(node) {
614 this.toggleLineComments = function(node) {
615 self.toggleComments(node, true);
615 self.toggleComments(node, true);
616 var $node = $(node);
616 var $node = $(node);
617 // mark outdated comments as visible before the toggle;
617 // mark outdated comments as visible before the toggle;
618 $(node.closest('tr')).find('.comment-outdated').show();
618 $(node.closest('tr')).find('.comment-outdated').show();
619 $node.closest('tr').toggleClass('hide-line-comments');
619 $node.closest('tr').toggleClass('hide-line-comments');
620 };
620 };
621
621
622 this.createCommentForm = function(formElement, lineno, placeholderText, initAutocompleteActions, resolvesCommentId){
622 this.createCommentForm = function(formElement, lineno, placeholderText, initAutocompleteActions, resolvesCommentId){
623 var pullRequestId = templateContext.pull_request_data.pull_request_id;
623 var pullRequestId = templateContext.pull_request_data.pull_request_id;
624 var commitId = templateContext.commit_data.commit_id;
624 var commitId = templateContext.commit_data.commit_id;
625
625
626 var commentForm = new CommentForm(
626 var commentForm = new CommentForm(
627 formElement, commitId, pullRequestId, lineno, initAutocompleteActions, resolvesCommentId);
627 formElement, commitId, pullRequestId, lineno, initAutocompleteActions, resolvesCommentId);
628 var cm = commentForm.getCmInstance();
628 var cm = commentForm.getCmInstance();
629
629
630 if (resolvesCommentId){
630 if (resolvesCommentId){
631 var placeholderText = _gettext('Leave a comment, or click resolve button to resolve TODO comment #{0}').format(resolvesCommentId);
631 var placeholderText = _gettext('Leave a comment, or click resolve button to resolve TODO comment #{0}').format(resolvesCommentId);
632 }
632 }
633
633
634 setTimeout(function() {
634 setTimeout(function() {
635 // callbacks
635 // callbacks
636 if (cm !== undefined) {
636 if (cm !== undefined) {
637 commentForm.setPlaceholder(placeholderText);
637 commentForm.setPlaceholder(placeholderText);
638 if (commentForm.isInline()) {
638 if (commentForm.isInline()) {
639 cm.focus();
639 cm.focus();
640 cm.refresh();
640 cm.refresh();
641 }
641 }
642 }
642 }
643 }, 10);
643 }, 10);
644
644
645 // trigger scrolldown to the resolve comment, since it might be away
645 // trigger scrolldown to the resolve comment, since it might be away
646 // from the clicked
646 // from the clicked
647 if (resolvesCommentId){
647 if (resolvesCommentId){
648 var actionNode = $(commentForm.resolvesActionId).offset();
648 var actionNode = $(commentForm.resolvesActionId).offset();
649
649
650 setTimeout(function() {
650 setTimeout(function() {
651 if (actionNode) {
651 if (actionNode) {
652 $('body, html').animate({scrollTop: actionNode.top}, 10);
652 $('body, html').animate({scrollTop: actionNode.top}, 10);
653 }
653 }
654 }, 100);
654 }, 100);
655 }
655 }
656
656
657 // add dropzone support
658 var insertAttachmentText = function (cm, attachmentName, attachmentStoreUrl, isRendered) {
659 var renderer = templateContext.visual.default_renderer;
660 if (renderer == 'rst') {
661 var attachmentUrl = '`#{0} <{1}>`_'.format(attachmentName, attachmentStoreUrl);
662 if (isRendered){
663 attachmentUrl = '\n.. image:: {0}'.format(attachmentStoreUrl);
664 }
665 } else if (renderer == 'markdown') {
666 var attachmentUrl = '[{0}]({1})'.format(attachmentName, attachmentStoreUrl);
667 if (isRendered){
668 attachmentUrl = '!' + attachmentUrl;
669 }
670 } else {
671 var attachmentUrl = '{}'.format(attachmentStoreUrl);
672 }
673 cm.replaceRange(attachmentUrl+'\n', CodeMirror.Pos(cm.lastLine()));
674
675 return false;
676 };
677
678 //see: https://www.dropzonejs.com/#configuration
679 var storeUrl = pyroutes.url('repo_commit_comment_attachment_upload',
680 {'repo_name': templateContext.repo_name,
681 'commit_id': templateContext.commit_data.commit_id})
682
683 var previewTmpl = $(formElement).find('.comment-attachment-uploader-template').get(0).innerHTML;
684 var selectLink = $(formElement).find('.pick-attachment').get(0);
685 $(formElement).find('.comment-attachment-uploader').dropzone({
686 url: storeUrl,
687 headers: {"X-CSRF-Token": CSRF_TOKEN},
688 paramName: function () {
689 return "attachment"
690 }, // The name that will be used to transfer the file
691 clickable: selectLink,
692 parallelUploads: 1,
693 maxFiles: 10,
694 maxFilesize: templateContext.attachment_store.max_file_size_mb,
695 uploadMultiple: false,
696 autoProcessQueue: true, // if false queue will not be processed automatically.
697 createImageThumbnails: false,
698 previewTemplate: previewTmpl,
699
700 accept: function (file, done) {
701 done();
702 },
703 init: function () {
704
705 this.on("sending", function (file, xhr, formData) {
706 $(formElement).find('.comment-attachment-uploader').find('.dropzone-text').hide();
707 $(formElement).find('.comment-attachment-uploader').find('.dropzone-upload').show();
708 });
709
710 this.on("success", function (file, response) {
711 $(formElement).find('.comment-attachment-uploader').find('.dropzone-text').show();
712 $(formElement).find('.comment-attachment-uploader').find('.dropzone-upload').hide();
713
714 var isRendered = false;
715 var ext = file.name.split('.').pop();
716 var imageExts = templateContext.attachment_store.image_ext;
717 if (imageExts.indexOf(ext) !== -1){
718 isRendered = true;
719 }
720
721 insertAttachmentText(cm, file.name, response.repo_fqn_access_path, isRendered)
722 });
723
724 this.on("error", function (file, errorMessage, xhr) {
725 $(formElement).find('.comment-attachment-uploader').find('.dropzone-upload').hide();
726
727 var error = null;
728
729 if (xhr !== undefined){
730 var httpStatus = xhr.status + " " + xhr.statusText;
731 if (xhr.status >= 500) {
732 error = httpStatus;
733 }
734 }
735
736 if (error === null) {
737 error = errorMessage.error || errorMessage || httpStatus;
738 }
739 $(file.previewElement).find('.dz-error-message').html('ERROR: {0}'.format(error));
740
741 });
742 }
743 });
744
745
657 return commentForm;
746 return commentForm;
658 };
747 };
659
748
660 this.createGeneralComment = function (lineNo, placeholderText, resolvesCommentId) {
749 this.createGeneralComment = function (lineNo, placeholderText, resolvesCommentId) {
661
750
662 var tmpl = $('#cb-comment-general-form-template').html();
751 var tmpl = $('#cb-comment-general-form-template').html();
663 tmpl = tmpl.format(null, 'general');
752 tmpl = tmpl.format(null, 'general');
664 var $form = $(tmpl);
753 var $form = $(tmpl);
665
754
666 var $formPlaceholder = $('#cb-comment-general-form-placeholder');
755 var $formPlaceholder = $('#cb-comment-general-form-placeholder');
667 var curForm = $formPlaceholder.find('form');
756 var curForm = $formPlaceholder.find('form');
668 if (curForm){
757 if (curForm){
669 curForm.remove();
758 curForm.remove();
670 }
759 }
671 $formPlaceholder.append($form);
760 $formPlaceholder.append($form);
672
761
673 var _form = $($form[0]);
762 var _form = $($form[0]);
674 var autocompleteActions = ['approve', 'reject', 'as_note', 'as_todo'];
763 var autocompleteActions = ['approve', 'reject', 'as_note', 'as_todo'];
675 var commentForm = this.createCommentForm(
764 var commentForm = this.createCommentForm(
676 _form, lineNo, placeholderText, autocompleteActions, resolvesCommentId);
765 _form, lineNo, placeholderText, autocompleteActions, resolvesCommentId);
677 commentForm.initStatusChangeSelector();
766 commentForm.initStatusChangeSelector();
678
767
679 return commentForm;
768 return commentForm;
680 };
769 };
681
770
682 this.createComment = function(node, resolutionComment) {
771 this.createComment = function(node, resolutionComment) {
683 var resolvesCommentId = resolutionComment || null;
772 var resolvesCommentId = resolutionComment || null;
684 var $node = $(node);
773 var $node = $(node);
685 var $td = $node.closest('td');
774 var $td = $node.closest('td');
686 var $form = $td.find('.comment-inline-form');
775 var $form = $td.find('.comment-inline-form');
687
776
688 if (!$form.length) {
777 if (!$form.length) {
689
778
690 var $filediff = $node.closest('.filediff');
779 var $filediff = $node.closest('.filediff');
691 $filediff.removeClass('hide-comments');
780 $filediff.removeClass('hide-comments');
692 var f_path = $filediff.attr('data-f-path');
781 var f_path = $filediff.attr('data-f-path');
693 var lineno = self.getLineNumber(node);
782 var lineno = self.getLineNumber(node);
694 // create a new HTML from template
783 // create a new HTML from template
695 var tmpl = $('#cb-comment-inline-form-template').html();
784 var tmpl = $('#cb-comment-inline-form-template').html();
696 tmpl = tmpl.format(escapeHtml(f_path), lineno);
785 tmpl = tmpl.format(escapeHtml(f_path), lineno);
697 $form = $(tmpl);
786 $form = $(tmpl);
698
787
699 var $comments = $td.find('.inline-comments');
788 var $comments = $td.find('.inline-comments');
700 if (!$comments.length) {
789 if (!$comments.length) {
701 $comments = $(
790 $comments = $(
702 $('#cb-comments-inline-container-template').html());
791 $('#cb-comments-inline-container-template').html());
703 $td.append($comments);
792 $td.append($comments);
704 }
793 }
705
794
706 $td.find('.cb-comment-add-button').before($form);
795 $td.find('.cb-comment-add-button').before($form);
707
796
708 var placeholderText = _gettext('Leave a comment on line {0}.').format(lineno);
797 var placeholderText = _gettext('Leave a comment on line {0}.').format(lineno);
709 var _form = $($form[0]).find('form');
798 var _form = $($form[0]).find('form');
710 var autocompleteActions = ['as_note', 'as_todo'];
799 var autocompleteActions = ['as_note', 'as_todo'];
711 var commentForm = this.createCommentForm(
800 var commentForm = this.createCommentForm(
712 _form, lineno, placeholderText, autocompleteActions, resolvesCommentId);
801 _form, lineno, placeholderText, autocompleteActions, resolvesCommentId);
713
802
714 $.Topic('/ui/plugins/code/comment_form_built').prepareOrPublish({
803 $.Topic('/ui/plugins/code/comment_form_built').prepareOrPublish({
715 form: _form,
804 form: _form,
716 parent: $td[0],
805 parent: $td[0],
717 lineno: lineno,
806 lineno: lineno,
718 f_path: f_path}
807 f_path: f_path}
719 );
808 );
720
809
721 // set a CUSTOM submit handler for inline comments.
810 // set a CUSTOM submit handler for inline comments.
722 commentForm.setHandleFormSubmit(function(o) {
811 commentForm.setHandleFormSubmit(function(o) {
723 var text = commentForm.cm.getValue();
812 var text = commentForm.cm.getValue();
724 var commentType = commentForm.getCommentType();
813 var commentType = commentForm.getCommentType();
725 var resolvesCommentId = commentForm.getResolvesId();
814 var resolvesCommentId = commentForm.getResolvesId();
726
815
727 if (text === "") {
816 if (text === "") {
728 return;
817 return;
729 }
818 }
730
819
731 if (lineno === undefined) {
820 if (lineno === undefined) {
732 alert('missing line !');
821 alert('missing line !');
733 return;
822 return;
734 }
823 }
735 if (f_path === undefined) {
824 if (f_path === undefined) {
736 alert('missing file path !');
825 alert('missing file path !');
737 return;
826 return;
738 }
827 }
739
828
740 var excludeCancelBtn = false;
829 var excludeCancelBtn = false;
741 var submitEvent = true;
830 var submitEvent = true;
742 commentForm.setActionButtonsDisabled(true, excludeCancelBtn, submitEvent);
831 commentForm.setActionButtonsDisabled(true, excludeCancelBtn, submitEvent);
743 commentForm.cm.setOption("readOnly", true);
832 commentForm.cm.setOption("readOnly", true);
744 var postData = {
833 var postData = {
745 'text': text,
834 'text': text,
746 'f_path': f_path,
835 'f_path': f_path,
747 'line': lineno,
836 'line': lineno,
748 'comment_type': commentType,
837 'comment_type': commentType,
749 'csrf_token': CSRF_TOKEN
838 'csrf_token': CSRF_TOKEN
750 };
839 };
751 if (resolvesCommentId){
840 if (resolvesCommentId){
752 postData['resolves_comment_id'] = resolvesCommentId;
841 postData['resolves_comment_id'] = resolvesCommentId;
753 }
842 }
754
843
755 var submitSuccessCallback = function(json_data) {
844 var submitSuccessCallback = function(json_data) {
756 $form.remove();
845 $form.remove();
757 try {
846 try {
758 var html = json_data.rendered_text;
847 var html = json_data.rendered_text;
759 var lineno = json_data.line_no;
848 var lineno = json_data.line_no;
760 var target_id = json_data.target_id;
849 var target_id = json_data.target_id;
761
850
762 $comments.find('.cb-comment-add-button').before(html);
851 $comments.find('.cb-comment-add-button').before(html);
763
852
764 //mark visually which comment was resolved
853 //mark visually which comment was resolved
765 if (resolvesCommentId) {
854 if (resolvesCommentId) {
766 commentForm.markCommentResolved(resolvesCommentId);
855 commentForm.markCommentResolved(resolvesCommentId);
767 }
856 }
768
857
769 // run global callback on submit
858 // run global callback on submit
770 commentForm.globalSubmitSuccessCallback();
859 commentForm.globalSubmitSuccessCallback();
771
860
772 } catch (e) {
861 } catch (e) {
773 console.error(e);
862 console.error(e);
774 }
863 }
775
864
776 // re trigger the linkification of next/prev navigation
865 // re trigger the linkification of next/prev navigation
777 linkifyComments($('.inline-comment-injected'));
866 linkifyComments($('.inline-comment-injected'));
778 timeagoActivate();
867 timeagoActivate();
779
868
780 if (window.updateSticky !== undefined) {
869 if (window.updateSticky !== undefined) {
781 // potentially our comments change the active window size, so we
870 // potentially our comments change the active window size, so we
782 // notify sticky elements
871 // notify sticky elements
783 updateSticky()
872 updateSticky()
784 }
873 }
785
874
786 commentForm.setActionButtonsDisabled(false);
875 commentForm.setActionButtonsDisabled(false);
787
876
788 };
877 };
789 var submitFailCallback = function(data){
878 var submitFailCallback = function(data){
790 alert(
879 alert(
791 "Error while submitting comment.\n" +
880 "Error while submitting comment.\n" +
792 "Error code {0} ({1}).".format(data.status, data.statusText)
881 "Error code {0} ({1}).".format(data.status, data.statusText)
793 );
882 );
794 commentForm.resetCommentFormState(text)
883 commentForm.resetCommentFormState(text)
795 };
884 };
796 commentForm.submitAjaxPOST(
885 commentForm.submitAjaxPOST(
797 commentForm.submitUrl, postData, submitSuccessCallback, submitFailCallback);
886 commentForm.submitUrl, postData, submitSuccessCallback, submitFailCallback);
798 });
887 });
799 }
888 }
800
889
801 $form.addClass('comment-inline-form-open');
890 $form.addClass('comment-inline-form-open');
802 };
891 };
803
892
804 this.createResolutionComment = function(commentId){
893 this.createResolutionComment = function(commentId){
805 // hide the trigger text
894 // hide the trigger text
806 $('#resolve-comment-{0}'.format(commentId)).hide();
895 $('#resolve-comment-{0}'.format(commentId)).hide();
807
896
808 var comment = $('#comment-'+commentId);
897 var comment = $('#comment-'+commentId);
809 var commentData = comment.data();
898 var commentData = comment.data();
810 if (commentData.commentInline) {
899 if (commentData.commentInline) {
811 this.createComment(comment, commentId)
900 this.createComment(comment, commentId)
812 } else {
901 } else {
813 Rhodecode.comments.createGeneralComment('general', "$placeholder", commentId)
902 Rhodecode.comments.createGeneralComment('general', "$placeholder", commentId)
814 }
903 }
815
904
816 return false;
905 return false;
817 };
906 };
818
907
819 this.submitResolution = function(commentId){
908 this.submitResolution = function(commentId){
820 var form = $('#resolve_comment_{0}'.format(commentId)).closest('form');
909 var form = $('#resolve_comment_{0}'.format(commentId)).closest('form');
821 var commentForm = form.get(0).CommentForm;
910 var commentForm = form.get(0).CommentForm;
822
911
823 var cm = commentForm.getCmInstance();
912 var cm = commentForm.getCmInstance();
824 var renderer = templateContext.visual.default_renderer;
913 var renderer = templateContext.visual.default_renderer;
825 if (renderer == 'rst'){
914 if (renderer == 'rst'){
826 var commentUrl = '`#{0} <{1}#comment-{0}>`_'.format(commentId, commentForm.selfUrl);
915 var commentUrl = '`#{0} <{1}#comment-{0}>`_'.format(commentId, commentForm.selfUrl);
827 } else if (renderer == 'markdown') {
916 } else if (renderer == 'markdown') {
828 var commentUrl = '[#{0}]({1}#comment-{0})'.format(commentId, commentForm.selfUrl);
917 var commentUrl = '[#{0}]({1}#comment-{0})'.format(commentId, commentForm.selfUrl);
829 } else {
918 } else {
830 var commentUrl = '{1}#comment-{0}'.format(commentId, commentForm.selfUrl);
919 var commentUrl = '{1}#comment-{0}'.format(commentId, commentForm.selfUrl);
831 }
920 }
832
921
833 cm.setValue(_gettext('TODO from comment {0} was fixed.').format(commentUrl));
922 cm.setValue(_gettext('TODO from comment {0} was fixed.').format(commentUrl));
834 form.submit();
923 form.submit();
835 return false;
924 return false;
836 };
925 };
837
926
838 };
927 };
@@ -1,159 +1,164 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2 <!DOCTYPE html>
2 <!DOCTYPE html>
3
3
4 <%
4 <%
5 c.template_context['repo_name'] = getattr(c, 'repo_name', '')
5 c.template_context['repo_name'] = getattr(c, 'repo_name', '')
6 go_import_header = ''
6 go_import_header = ''
7 if hasattr(c, 'rhodecode_db_repo'):
7 if hasattr(c, 'rhodecode_db_repo'):
8 c.template_context['repo_type'] = c.rhodecode_db_repo.repo_type
8 c.template_context['repo_type'] = c.rhodecode_db_repo.repo_type
9 c.template_context['repo_landing_commit'] = c.rhodecode_db_repo.landing_rev[1]
9 c.template_context['repo_landing_commit'] = c.rhodecode_db_repo.landing_rev[1]
10 c.template_context['repo_id'] = c.rhodecode_db_repo.repo_id
10 c.template_context['repo_id'] = c.rhodecode_db_repo.repo_id
11 c.template_context['repo_view_type'] = h.get_repo_view_type(request)
11 c.template_context['repo_view_type'] = h.get_repo_view_type(request)
12
12
13 if getattr(c, 'repo_group', None):
13 if getattr(c, 'repo_group', None):
14 c.template_context['repo_group_id'] = c.repo_group.group_id
14 c.template_context['repo_group_id'] = c.repo_group.group_id
15 c.template_context['repo_group_name'] = c.repo_group.group_name
15 c.template_context['repo_group_name'] = c.repo_group.group_name
16
16
17 if getattr(c, 'rhodecode_user', None) and c.rhodecode_user.user_id:
17 if getattr(c, 'rhodecode_user', None) and c.rhodecode_user.user_id:
18 c.template_context['rhodecode_user']['username'] = c.rhodecode_user.username
18 c.template_context['rhodecode_user']['username'] = c.rhodecode_user.username
19 c.template_context['rhodecode_user']['email'] = c.rhodecode_user.email
19 c.template_context['rhodecode_user']['email'] = c.rhodecode_user.email
20 c.template_context['rhodecode_user']['notification_status'] = c.rhodecode_user.get_instance().user_data.get('notification_status', True)
20 c.template_context['rhodecode_user']['notification_status'] = c.rhodecode_user.get_instance().user_data.get('notification_status', True)
21 c.template_context['rhodecode_user']['first_name'] = c.rhodecode_user.first_name
21 c.template_context['rhodecode_user']['first_name'] = c.rhodecode_user.first_name
22 c.template_context['rhodecode_user']['last_name'] = c.rhodecode_user.last_name
22 c.template_context['rhodecode_user']['last_name'] = c.rhodecode_user.last_name
23
23
24 c.template_context['visual']['default_renderer'] = h.get_visual_attr(c, 'default_renderer')
24 c.template_context['visual']['default_renderer'] = h.get_visual_attr(c, 'default_renderer')
25 c.template_context['default_user'] = {
25 c.template_context['default_user'] = {
26 'username': h.DEFAULT_USER,
26 'username': h.DEFAULT_USER,
27 'user_id': 1
27 'user_id': 1
28 }
28 }
29 c.template_context['search_context'] = {
29 c.template_context['search_context'] = {
30 'repo_group_id': c.template_context.get('repo_group_id'),
30 'repo_group_id': c.template_context.get('repo_group_id'),
31 'repo_group_name': c.template_context.get('repo_group_name'),
31 'repo_group_name': c.template_context.get('repo_group_name'),
32 'repo_id': c.template_context.get('repo_id'),
32 'repo_id': c.template_context.get('repo_id'),
33 'repo_name': c.template_context.get('repo_name'),
33 'repo_name': c.template_context.get('repo_name'),
34 'repo_view_type': c.template_context.get('repo_view_type'),
34 'repo_view_type': c.template_context.get('repo_view_type'),
35 }
35 }
36
36
37 c.template_context['attachment_store'] = {
38 'max_file_size_mb': 10,
39 'image_ext': ["png", "jpg", "gif", "jpeg"]
40 }
41
37 %>
42 %>
38 <html xmlns="http://www.w3.org/1999/xhtml">
43 <html xmlns="http://www.w3.org/1999/xhtml">
39 <head>
44 <head>
40 <title>${self.title()}</title>
45 <title>${self.title()}</title>
41 <meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
46 <meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
42
47
43 ${h.go_import_header(request, getattr(c, 'rhodecode_db_repo', None))}
48 ${h.go_import_header(request, getattr(c, 'rhodecode_db_repo', None))}
44
49
45 % if 'safari' in (request.user_agent or '').lower():
50 % if 'safari' in (request.user_agent or '').lower():
46 <meta name="referrer" content="origin">
51 <meta name="referrer" content="origin">
47 % else:
52 % else:
48 <meta name="referrer" content="origin-when-cross-origin">
53 <meta name="referrer" content="origin-when-cross-origin">
49 % endif
54 % endif
50
55
51 <%def name="robots()">
56 <%def name="robots()">
52 <meta name="robots" content="index, nofollow"/>
57 <meta name="robots" content="index, nofollow"/>
53 </%def>
58 </%def>
54 ${self.robots()}
59 ${self.robots()}
55 <link rel="icon" href="${h.asset('images/favicon.ico', ver=c.rhodecode_version_hash)}" sizes="16x16 32x32" type="image/png" />
60 <link rel="icon" href="${h.asset('images/favicon.ico', ver=c.rhodecode_version_hash)}" sizes="16x16 32x32" type="image/png" />
56 <script src="${h.asset('js/vendors/webcomponentsjs/custom-elements-es5-adapter.js', ver=c.rhodecode_version_hash)}"></script>
61 <script src="${h.asset('js/vendors/webcomponentsjs/custom-elements-es5-adapter.js', ver=c.rhodecode_version_hash)}"></script>
57 <script src="${h.asset('js/vendors/webcomponentsjs/webcomponents-bundle.js', ver=c.rhodecode_version_hash)}"></script>
62 <script src="${h.asset('js/vendors/webcomponentsjs/webcomponents-bundle.js', ver=c.rhodecode_version_hash)}"></script>
58
63
59 ## CSS definitions
64 ## CSS definitions
60 <%def name="css()">
65 <%def name="css()">
61 <link rel="stylesheet" type="text/css" href="${h.asset('css/style.css', ver=c.rhodecode_version_hash)}" media="screen"/>
66 <link rel="stylesheet" type="text/css" href="${h.asset('css/style.css', ver=c.rhodecode_version_hash)}" media="screen"/>
62 ## EXTRA FOR CSS
67 ## EXTRA FOR CSS
63 ${self.css_extra()}
68 ${self.css_extra()}
64 </%def>
69 </%def>
65 ## CSS EXTRA - optionally inject some extra CSS stuff needed for specific websites
70 ## CSS EXTRA - optionally inject some extra CSS stuff needed for specific websites
66 <%def name="css_extra()">
71 <%def name="css_extra()">
67 </%def>
72 </%def>
68
73
69 ${self.css()}
74 ${self.css()}
70
75
71 ## JAVASCRIPT
76 ## JAVASCRIPT
72 <%def name="js()">
77 <%def name="js()">
73
78
74 <script src="${h.asset('js/rhodecode/i18n/%s.js' % c.language, ver=c.rhodecode_version_hash)}"></script>
79 <script src="${h.asset('js/rhodecode/i18n/%s.js' % c.language, ver=c.rhodecode_version_hash)}"></script>
75 <script type="text/javascript">
80 <script type="text/javascript">
76 // register templateContext to pass template variables to JS
81 // register templateContext to pass template variables to JS
77 var templateContext = ${h.json.dumps(c.template_context)|n};
82 var templateContext = ${h.json.dumps(c.template_context)|n};
78
83
79 var APPLICATION_URL = "${h.route_path('home').rstrip('/')}";
84 var APPLICATION_URL = "${h.route_path('home').rstrip('/')}";
80 var APPLICATION_PLUGINS = [];
85 var APPLICATION_PLUGINS = [];
81 var ASSET_URL = "${h.asset('')}";
86 var ASSET_URL = "${h.asset('')}";
82 var DEFAULT_RENDERER = "${h.get_visual_attr(c, 'default_renderer')}";
87 var DEFAULT_RENDERER = "${h.get_visual_attr(c, 'default_renderer')}";
83 var CSRF_TOKEN = "${getattr(c, 'csrf_token', '')}";
88 var CSRF_TOKEN = "${getattr(c, 'csrf_token', '')}";
84
89
85 var APPENLIGHT = {
90 var APPENLIGHT = {
86 enabled: ${'true' if getattr(c, 'appenlight_enabled', False) else 'false'},
91 enabled: ${'true' if getattr(c, 'appenlight_enabled', False) else 'false'},
87 key: '${getattr(c, "appenlight_api_public_key", "")}',
92 key: '${getattr(c, "appenlight_api_public_key", "")}',
88 % if getattr(c, 'appenlight_server_url', None):
93 % if getattr(c, 'appenlight_server_url', None):
89 serverUrl: '${getattr(c, "appenlight_server_url", "")}',
94 serverUrl: '${getattr(c, "appenlight_server_url", "")}',
90 % endif
95 % endif
91 requestInfo: {
96 requestInfo: {
92 % if getattr(c, 'rhodecode_user', None):
97 % if getattr(c, 'rhodecode_user', None):
93 ip: '${c.rhodecode_user.ip_addr}',
98 ip: '${c.rhodecode_user.ip_addr}',
94 username: '${c.rhodecode_user.username}'
99 username: '${c.rhodecode_user.username}'
95 % endif
100 % endif
96 },
101 },
97 tags: {
102 tags: {
98 rhodecode_version: '${c.rhodecode_version}',
103 rhodecode_version: '${c.rhodecode_version}',
99 rhodecode_edition: '${c.rhodecode_edition}'
104 rhodecode_edition: '${c.rhodecode_edition}'
100 }
105 }
101 };
106 };
102
107
103 </script>
108 </script>
104 <%include file="/base/plugins_base.mako"/>
109 <%include file="/base/plugins_base.mako"/>
105 <!--[if lt IE 9]>
110 <!--[if lt IE 9]>
106 <script language="javascript" type="text/javascript" src="${h.asset('js/src/excanvas.min.js')}"></script>
111 <script language="javascript" type="text/javascript" src="${h.asset('js/src/excanvas.min.js')}"></script>
107 <![endif]-->
112 <![endif]-->
108 <script language="javascript" type="text/javascript" src="${h.asset('js/rhodecode/routes.js', ver=c.rhodecode_version_hash)}"></script>
113 <script language="javascript" type="text/javascript" src="${h.asset('js/rhodecode/routes.js', ver=c.rhodecode_version_hash)}"></script>
109 <script> var alertMessagePayloads = ${h.flash.json_alerts(request=request)|n}; </script>
114 <script> var alertMessagePayloads = ${h.flash.json_alerts(request=request)|n}; </script>
110 ## avoide escaping the %N
115 ## avoide escaping the %N
111 <script language="javascript" type="text/javascript" src="${h.asset('js/scripts.js', ver=c.rhodecode_version_hash)}"></script>
116 <script language="javascript" type="text/javascript" src="${h.asset('js/scripts.js', ver=c.rhodecode_version_hash)}"></script>
112 <script>CodeMirror.modeURL = "${h.asset('') + 'js/mode/%N/%N.js?ver='+c.rhodecode_version_hash}";</script>
117 <script>CodeMirror.modeURL = "${h.asset('') + 'js/mode/%N/%N.js?ver='+c.rhodecode_version_hash}";</script>
113
118
114
119
115 ## JAVASCRIPT EXTRA - optionally inject some extra JS for specificed templates
120 ## JAVASCRIPT EXTRA - optionally inject some extra JS for specificed templates
116 ${self.js_extra()}
121 ${self.js_extra()}
117
122
118 <script type="text/javascript">
123 <script type="text/javascript">
119 Rhodecode = (function() {
124 Rhodecode = (function() {
120 function _Rhodecode() {
125 function _Rhodecode() {
121 this.comments = new CommentsController();
126 this.comments = new CommentsController();
122 }
127 }
123 return new _Rhodecode();
128 return new _Rhodecode();
124 })();
129 })();
125
130
126 $(document).ready(function(){
131 $(document).ready(function(){
127 show_more_event();
132 show_more_event();
128 timeagoActivate();
133 timeagoActivate();
129 clipboardActivate();
134 clipboardActivate();
130 })
135 })
131 </script>
136 </script>
132
137
133 </%def>
138 </%def>
134
139
135 ## JAVASCRIPT EXTRA - optionally inject some extra JS for specificed templates
140 ## JAVASCRIPT EXTRA - optionally inject some extra JS for specificed templates
136 <%def name="js_extra()"></%def>
141 <%def name="js_extra()"></%def>
137 ${self.js()}
142 ${self.js()}
138
143
139 <%def name="head_extra()"></%def>
144 <%def name="head_extra()"></%def>
140 ${self.head_extra()}
145 ${self.head_extra()}
141 ## extra stuff
146 ## extra stuff
142 %if c.pre_code:
147 %if c.pre_code:
143 ${c.pre_code|n}
148 ${c.pre_code|n}
144 %endif
149 %endif
145 </head>
150 </head>
146 <body id="body">
151 <body id="body">
147 <noscript>
152 <noscript>
148 <div class="noscript-error">
153 <div class="noscript-error">
149 ${_('Please enable JavaScript to use RhodeCode Enterprise')}
154 ${_('Please enable JavaScript to use RhodeCode Enterprise')}
150 </div>
155 </div>
151 </noscript>
156 </noscript>
152
157
153 ${next.body()}
158 ${next.body()}
154 %if c.post_code:
159 %if c.post_code:
155 ${c.post_code|n}
160 ${c.post_code|n}
156 %endif
161 %endif
157 <rhodecode-app></rhodecode-app>
162 <rhodecode-app></rhodecode-app>
158 </body>
163 </body>
159 </html>
164 </html>
@@ -1,404 +1,420 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2 ## usage:
2 ## usage:
3 ## <%namespace name="comment" file="/changeset/changeset_file_comment.mako"/>
3 ## <%namespace name="comment" file="/changeset/changeset_file_comment.mako"/>
4 ## ${comment.comment_block(comment)}
4 ## ${comment.comment_block(comment)}
5 ##
5 ##
6 <%namespace name="base" file="/base/base.mako"/>
6 <%namespace name="base" file="/base/base.mako"/>
7
7
8 <%def name="comment_block(comment, inline=False)">
8 <%def name="comment_block(comment, inline=False)">
9 <% pr_index_ver = comment.get_index_version(getattr(c, 'versions', [])) %>
9 <% pr_index_ver = comment.get_index_version(getattr(c, 'versions', [])) %>
10 <% latest_ver = len(getattr(c, 'versions', [])) %>
10 <% latest_ver = len(getattr(c, 'versions', [])) %>
11 % if inline:
11 % if inline:
12 <% outdated_at_ver = comment.outdated_at_version(getattr(c, 'at_version_num', None)) %>
12 <% outdated_at_ver = comment.outdated_at_version(getattr(c, 'at_version_num', None)) %>
13 % else:
13 % else:
14 <% outdated_at_ver = comment.older_than_version(getattr(c, 'at_version_num', None)) %>
14 <% outdated_at_ver = comment.older_than_version(getattr(c, 'at_version_num', None)) %>
15 % endif
15 % endif
16
16
17
17
18 <div class="comment
18 <div class="comment
19 ${'comment-inline' if inline else 'comment-general'}
19 ${'comment-inline' if inline else 'comment-general'}
20 ${'comment-outdated' if outdated_at_ver else 'comment-current'}"
20 ${'comment-outdated' if outdated_at_ver else 'comment-current'}"
21 id="comment-${comment.comment_id}"
21 id="comment-${comment.comment_id}"
22 line="${comment.line_no}"
22 line="${comment.line_no}"
23 data-comment-id="${comment.comment_id}"
23 data-comment-id="${comment.comment_id}"
24 data-comment-type="${comment.comment_type}"
24 data-comment-type="${comment.comment_type}"
25 data-comment-line-no="${comment.line_no}"
25 data-comment-line-no="${comment.line_no}"
26 data-comment-inline=${h.json.dumps(inline)}
26 data-comment-inline=${h.json.dumps(inline)}
27 style="${'display: none;' if outdated_at_ver else ''}">
27 style="${'display: none;' if outdated_at_ver else ''}">
28
28
29 <div class="meta">
29 <div class="meta">
30 <div class="comment-type-label">
30 <div class="comment-type-label">
31 <div class="comment-label ${comment.comment_type or 'note'}" id="comment-label-${comment.comment_id}" title="line: ${comment.line_no}">
31 <div class="comment-label ${comment.comment_type or 'note'}" id="comment-label-${comment.comment_id}" title="line: ${comment.line_no}">
32 % if comment.comment_type == 'todo':
32 % if comment.comment_type == 'todo':
33 % if comment.resolved:
33 % if comment.resolved:
34 <div class="resolved tooltip" title="${_('Resolved by comment #{}').format(comment.resolved.comment_id)}">
34 <div class="resolved tooltip" title="${_('Resolved by comment #{}').format(comment.resolved.comment_id)}">
35 <a href="#comment-${comment.resolved.comment_id}">${comment.comment_type}</a>
35 <a href="#comment-${comment.resolved.comment_id}">${comment.comment_type}</a>
36 </div>
36 </div>
37 % else:
37 % else:
38 <div class="resolved tooltip" style="display: none">
38 <div class="resolved tooltip" style="display: none">
39 <span>${comment.comment_type}</span>
39 <span>${comment.comment_type}</span>
40 </div>
40 </div>
41 <div class="resolve tooltip" onclick="return Rhodecode.comments.createResolutionComment(${comment.comment_id});" title="${_('Click to resolve this comment')}">
41 <div class="resolve tooltip" onclick="return Rhodecode.comments.createResolutionComment(${comment.comment_id});" title="${_('Click to resolve this comment')}">
42 ${comment.comment_type}
42 ${comment.comment_type}
43 </div>
43 </div>
44 % endif
44 % endif
45 % else:
45 % else:
46 % if comment.resolved_comment:
46 % if comment.resolved_comment:
47 fix
47 fix
48 <a href="#comment-${comment.resolved_comment.comment_id}" onclick="Rhodecode.comments.scrollToComment($('#comment-${comment.resolved_comment.comment_id}'), 0, ${h.json.dumps(comment.resolved_comment.outdated)})">
48 <a href="#comment-${comment.resolved_comment.comment_id}" onclick="Rhodecode.comments.scrollToComment($('#comment-${comment.resolved_comment.comment_id}'), 0, ${h.json.dumps(comment.resolved_comment.outdated)})">
49 <span style="text-decoration: line-through">#${comment.resolved_comment.comment_id}</span>
49 <span style="text-decoration: line-through">#${comment.resolved_comment.comment_id}</span>
50 </a>
50 </a>
51 % else:
51 % else:
52 ${comment.comment_type or 'note'}
52 ${comment.comment_type or 'note'}
53 % endif
53 % endif
54 % endif
54 % endif
55 </div>
55 </div>
56 </div>
56 </div>
57
57
58 <div class="author ${'author-inline' if inline else 'author-general'}">
58 <div class="author ${'author-inline' if inline else 'author-general'}">
59 ${base.gravatar_with_user(comment.author.email, 16)}
59 ${base.gravatar_with_user(comment.author.email, 16)}
60 </div>
60 </div>
61 <div class="date">
61 <div class="date">
62 ${h.age_component(comment.modified_at, time_is_local=True)}
62 ${h.age_component(comment.modified_at, time_is_local=True)}
63 </div>
63 </div>
64 % if inline:
64 % if inline:
65 <span></span>
65 <span></span>
66 % else:
66 % else:
67 <div class="status-change">
67 <div class="status-change">
68 % if comment.pull_request:
68 % if comment.pull_request:
69 <a href="${h.route_path('pullrequest_show',repo_name=comment.pull_request.target_repo.repo_name,pull_request_id=comment.pull_request.pull_request_id)}">
69 <a href="${h.route_path('pullrequest_show',repo_name=comment.pull_request.target_repo.repo_name,pull_request_id=comment.pull_request.pull_request_id)}">
70 % if comment.status_change:
70 % if comment.status_change:
71 ${_('pull request #%s') % comment.pull_request.pull_request_id}:
71 ${_('pull request #%s') % comment.pull_request.pull_request_id}:
72 % else:
72 % else:
73 ${_('pull request #%s') % comment.pull_request.pull_request_id}
73 ${_('pull request #%s') % comment.pull_request.pull_request_id}
74 % endif
74 % endif
75 </a>
75 </a>
76 % else:
76 % else:
77 % if comment.status_change:
77 % if comment.status_change:
78 ${_('Status change on commit')}:
78 ${_('Status change on commit')}:
79 % endif
79 % endif
80 % endif
80 % endif
81 </div>
81 </div>
82 % endif
82 % endif
83
83
84 % if comment.status_change:
84 % if comment.status_change:
85 <i class="icon-circle review-status-${comment.status_change[0].status}"></i>
85 <i class="icon-circle review-status-${comment.status_change[0].status}"></i>
86 <div title="${_('Commit status')}" class="changeset-status-lbl">
86 <div title="${_('Commit status')}" class="changeset-status-lbl">
87 ${comment.status_change[0].status_lbl}
87 ${comment.status_change[0].status_lbl}
88 </div>
88 </div>
89 % endif
89 % endif
90
90
91 <a class="permalink" href="#comment-${comment.comment_id}"> &para;</a>
91 <a class="permalink" href="#comment-${comment.comment_id}"> &para;</a>
92
92
93 <div class="comment-links-block">
93 <div class="comment-links-block">
94 % if comment.pull_request and comment.pull_request.author.user_id == comment.author.user_id:
94 % if comment.pull_request and comment.pull_request.author.user_id == comment.author.user_id:
95 <span class="tag authortag tooltip" title="${_('Pull request author')}">
95 <span class="tag authortag tooltip" title="${_('Pull request author')}">
96 ${_('author')}
96 ${_('author')}
97 </span>
97 </span>
98 |
98 |
99 % endif
99 % endif
100 % if inline:
100 % if inline:
101 <div class="pr-version-inline">
101 <div class="pr-version-inline">
102 <a href="${request.current_route_path(_query=dict(version=comment.pull_request_version_id), _anchor='comment-{}'.format(comment.comment_id))}">
102 <a href="${request.current_route_path(_query=dict(version=comment.pull_request_version_id), _anchor='comment-{}'.format(comment.comment_id))}">
103 % if outdated_at_ver:
103 % if outdated_at_ver:
104 <code class="pr-version-num" title="${_('Outdated comment from pull request version v{0}, latest v{1}').format(pr_index_ver, latest_ver)}">
104 <code class="pr-version-num" title="${_('Outdated comment from pull request version v{0}, latest v{1}').format(pr_index_ver, latest_ver)}">
105 outdated ${'v{}'.format(pr_index_ver)} |
105 outdated ${'v{}'.format(pr_index_ver)} |
106 </code>
106 </code>
107 % elif pr_index_ver:
107 % elif pr_index_ver:
108 <code class="pr-version-num" title="${_('Comment from pull request version v{0}, latest v{1}').format(pr_index_ver, latest_ver)}">
108 <code class="pr-version-num" title="${_('Comment from pull request version v{0}, latest v{1}').format(pr_index_ver, latest_ver)}">
109 ${'v{}'.format(pr_index_ver)} |
109 ${'v{}'.format(pr_index_ver)} |
110 </code>
110 </code>
111 % endif
111 % endif
112 </a>
112 </a>
113 </div>
113 </div>
114 % else:
114 % else:
115 % if comment.pull_request_version_id and pr_index_ver:
115 % if comment.pull_request_version_id and pr_index_ver:
116 |
116 |
117 <div class="pr-version">
117 <div class="pr-version">
118 % if comment.outdated:
118 % if comment.outdated:
119 <a href="?version=${comment.pull_request_version_id}#comment-${comment.comment_id}">
119 <a href="?version=${comment.pull_request_version_id}#comment-${comment.comment_id}">
120 ${_('Outdated comment from pull request version v{0}, latest v{1}').format(pr_index_ver, latest_ver)}
120 ${_('Outdated comment from pull request version v{0}, latest v{1}').format(pr_index_ver, latest_ver)}
121 </a>
121 </a>
122 % else:
122 % else:
123 <div title="${_('Comment from pull request version v{0}, latest v{1}').format(pr_index_ver, latest_ver)}">
123 <div title="${_('Comment from pull request version v{0}, latest v{1}').format(pr_index_ver, latest_ver)}">
124 <a href="${h.route_path('pullrequest_show',repo_name=comment.pull_request.target_repo.repo_name,pull_request_id=comment.pull_request.pull_request_id, version=comment.pull_request_version_id)}">
124 <a href="${h.route_path('pullrequest_show',repo_name=comment.pull_request.target_repo.repo_name,pull_request_id=comment.pull_request.pull_request_id, version=comment.pull_request_version_id)}">
125 <code class="pr-version-num">
125 <code class="pr-version-num">
126 ${'v{}'.format(pr_index_ver)}
126 ${'v{}'.format(pr_index_ver)}
127 </code>
127 </code>
128 </a>
128 </a>
129 </div>
129 </div>
130 % endif
130 % endif
131 </div>
131 </div>
132 % endif
132 % endif
133 % endif
133 % endif
134
134
135 ## show delete comment if it's not a PR (regular comments) or it's PR that is not closed
135 ## show delete comment if it's not a PR (regular comments) or it's PR that is not closed
136 ## only super-admin, repo admin OR comment owner can delete, also hide delete if currently viewed comment is outdated
136 ## only super-admin, repo admin OR comment owner can delete, also hide delete if currently viewed comment is outdated
137 %if not outdated_at_ver and (not comment.pull_request or (comment.pull_request and not comment.pull_request.is_closed())):
137 %if not outdated_at_ver and (not comment.pull_request or (comment.pull_request and not comment.pull_request.is_closed())):
138 ## permissions to delete
138 ## permissions to delete
139 %if c.is_super_admin or h.HasRepoPermissionAny('repository.admin')(c.repo_name) or comment.author.user_id == c.rhodecode_user.user_id:
139 %if c.is_super_admin or h.HasRepoPermissionAny('repository.admin')(c.repo_name) or comment.author.user_id == c.rhodecode_user.user_id:
140 ## TODO: dan: add edit comment here
140 ## TODO: dan: add edit comment here
141 <a onclick="return Rhodecode.comments.deleteComment(this);" class="delete-comment"> ${_('Delete')}</a>
141 <a onclick="return Rhodecode.comments.deleteComment(this);" class="delete-comment"> ${_('Delete')}</a>
142 %else:
142 %else:
143 <button class="btn-link" disabled="disabled"> ${_('Delete')}</button>
143 <button class="btn-link" disabled="disabled"> ${_('Delete')}</button>
144 %endif
144 %endif
145 %else:
145 %else:
146 <button class="btn-link" disabled="disabled"> ${_('Delete')}</button>
146 <button class="btn-link" disabled="disabled"> ${_('Delete')}</button>
147 %endif
147 %endif
148
148
149 % if outdated_at_ver:
149 % if outdated_at_ver:
150 | <a onclick="return Rhodecode.comments.prevOutdatedComment(this);" class="prev-comment"> ${_('Prev')}</a>
150 | <a onclick="return Rhodecode.comments.prevOutdatedComment(this);" class="prev-comment"> ${_('Prev')}</a>
151 | <a onclick="return Rhodecode.comments.nextOutdatedComment(this);" class="next-comment"> ${_('Next')}</a>
151 | <a onclick="return Rhodecode.comments.nextOutdatedComment(this);" class="next-comment"> ${_('Next')}</a>
152 % else:
152 % else:
153 | <a onclick="return Rhodecode.comments.prevComment(this);" class="prev-comment"> ${_('Prev')}</a>
153 | <a onclick="return Rhodecode.comments.prevComment(this);" class="prev-comment"> ${_('Prev')}</a>
154 | <a onclick="return Rhodecode.comments.nextComment(this);" class="next-comment"> ${_('Next')}</a>
154 | <a onclick="return Rhodecode.comments.nextComment(this);" class="next-comment"> ${_('Next')}</a>
155 % endif
155 % endif
156
156
157 </div>
157 </div>
158 </div>
158 </div>
159 <div class="text">
159 <div class="text">
160 ${h.render(comment.text, renderer=comment.renderer, mentions=True)}
160 ${h.render(comment.text, renderer=comment.renderer, mentions=True)}
161 </div>
161 </div>
162
162
163 </div>
163 </div>
164 </%def>
164 </%def>
165
165
166 ## generate main comments
166 ## generate main comments
167 <%def name="generate_comments(comments, include_pull_request=False, is_pull_request=False)">
167 <%def name="generate_comments(comments, include_pull_request=False, is_pull_request=False)">
168 <div class="general-comments" id="comments">
168 <div class="general-comments" id="comments">
169 %for comment in comments:
169 %for comment in comments:
170 <div id="comment-tr-${comment.comment_id}">
170 <div id="comment-tr-${comment.comment_id}">
171 ## only render comments that are not from pull request, or from
171 ## only render comments that are not from pull request, or from
172 ## pull request and a status change
172 ## pull request and a status change
173 %if not comment.pull_request or (comment.pull_request and comment.status_change) or include_pull_request:
173 %if not comment.pull_request or (comment.pull_request and comment.status_change) or include_pull_request:
174 ${comment_block(comment)}
174 ${comment_block(comment)}
175 %endif
175 %endif
176 </div>
176 </div>
177 %endfor
177 %endfor
178 ## to anchor ajax comments
178 ## to anchor ajax comments
179 <div id="injected_page_comments"></div>
179 <div id="injected_page_comments"></div>
180 </div>
180 </div>
181 </%def>
181 </%def>
182
182
183
183
184 <%def name="comments(post_url, cur_status, is_pull_request=False, is_compare=False, change_status=True, form_extras=None)">
184 <%def name="comments(post_url, cur_status, is_pull_request=False, is_compare=False, change_status=True, form_extras=None)">
185
185
186 <div class="comments">
186 <div class="comments">
187 <%
187 <%
188 if is_pull_request:
188 if is_pull_request:
189 placeholder = _('Leave a comment on this Pull Request.')
189 placeholder = _('Leave a comment on this Pull Request.')
190 elif is_compare:
190 elif is_compare:
191 placeholder = _('Leave a comment on {} commits in this range.').format(len(form_extras))
191 placeholder = _('Leave a comment on {} commits in this range.').format(len(form_extras))
192 else:
192 else:
193 placeholder = _('Leave a comment on this Commit.')
193 placeholder = _('Leave a comment on this Commit.')
194 %>
194 %>
195
195
196 % if c.rhodecode_user.username != h.DEFAULT_USER:
196 % if c.rhodecode_user.username != h.DEFAULT_USER:
197 <div class="js-template" id="cb-comment-general-form-template">
197 <div class="js-template" id="cb-comment-general-form-template">
198 ## template generated for injection
198 ## template generated for injection
199 ${comment_form(form_type='general', review_statuses=c.commit_statuses, form_extras=form_extras)}
199 ${comment_form(form_type='general', review_statuses=c.commit_statuses, form_extras=form_extras)}
200 </div>
200 </div>
201
201
202 <div id="cb-comment-general-form-placeholder" class="comment-form ac">
202 <div id="cb-comment-general-form-placeholder" class="comment-form ac">
203 ## inject form here
203 ## inject form here
204 </div>
204 </div>
205 <script type="text/javascript">
205 <script type="text/javascript">
206 var lineNo = 'general';
206 var lineNo = 'general';
207 var resolvesCommentId = null;
207 var resolvesCommentId = null;
208 var generalCommentForm = Rhodecode.comments.createGeneralComment(
208 var generalCommentForm = Rhodecode.comments.createGeneralComment(
209 lineNo, "${placeholder}", resolvesCommentId);
209 lineNo, "${placeholder}", resolvesCommentId);
210
210
211 // set custom success callback on rangeCommit
211 // set custom success callback on rangeCommit
212 % if is_compare:
212 % if is_compare:
213 generalCommentForm.setHandleFormSubmit(function(o) {
213 generalCommentForm.setHandleFormSubmit(function(o) {
214 var self = generalCommentForm;
214 var self = generalCommentForm;
215
215
216 var text = self.cm.getValue();
216 var text = self.cm.getValue();
217 var status = self.getCommentStatus();
217 var status = self.getCommentStatus();
218 var commentType = self.getCommentType();
218 var commentType = self.getCommentType();
219
219
220 if (text === "" && !status) {
220 if (text === "" && !status) {
221 return;
221 return;
222 }
222 }
223
223
224 // we can pick which commits we want to make the comment by
224 // we can pick which commits we want to make the comment by
225 // selecting them via click on preview pane, this will alter the hidden inputs
225 // selecting them via click on preview pane, this will alter the hidden inputs
226 var cherryPicked = $('#changeset_compare_view_content .compare_select.hl').length > 0;
226 var cherryPicked = $('#changeset_compare_view_content .compare_select.hl').length > 0;
227
227
228 var commitIds = [];
228 var commitIds = [];
229 $('#changeset_compare_view_content .compare_select').each(function(el) {
229 $('#changeset_compare_view_content .compare_select').each(function(el) {
230 var commitId = this.id.replace('row-', '');
230 var commitId = this.id.replace('row-', '');
231 if ($(this).hasClass('hl') || !cherryPicked) {
231 if ($(this).hasClass('hl') || !cherryPicked) {
232 $("input[data-commit-id='{0}']".format(commitId)).val(commitId);
232 $("input[data-commit-id='{0}']".format(commitId)).val(commitId);
233 commitIds.push(commitId);
233 commitIds.push(commitId);
234 } else {
234 } else {
235 $("input[data-commit-id='{0}']".format(commitId)).val('')
235 $("input[data-commit-id='{0}']".format(commitId)).val('')
236 }
236 }
237 });
237 });
238
238
239 self.setActionButtonsDisabled(true);
239 self.setActionButtonsDisabled(true);
240 self.cm.setOption("readOnly", true);
240 self.cm.setOption("readOnly", true);
241 var postData = {
241 var postData = {
242 'text': text,
242 'text': text,
243 'changeset_status': status,
243 'changeset_status': status,
244 'comment_type': commentType,
244 'comment_type': commentType,
245 'commit_ids': commitIds,
245 'commit_ids': commitIds,
246 'csrf_token': CSRF_TOKEN
246 'csrf_token': CSRF_TOKEN
247 };
247 };
248
248
249 var submitSuccessCallback = function(o) {
249 var submitSuccessCallback = function(o) {
250 location.reload(true);
250 location.reload(true);
251 };
251 };
252 var submitFailCallback = function(){
252 var submitFailCallback = function(){
253 self.resetCommentFormState(text)
253 self.resetCommentFormState(text)
254 };
254 };
255 self.submitAjaxPOST(
255 self.submitAjaxPOST(
256 self.submitUrl, postData, submitSuccessCallback, submitFailCallback);
256 self.submitUrl, postData, submitSuccessCallback, submitFailCallback);
257 });
257 });
258 % endif
258 % endif
259
259
260
261 </script>
260 </script>
262 % else:
261 % else:
263 ## form state when not logged in
262 ## form state when not logged in
264 <div class="comment-form ac">
263 <div class="comment-form ac">
265
264
266 <div class="comment-area">
265 <div class="comment-area">
267 <div class="comment-area-header">
266 <div class="comment-area-header">
268 <ul class="nav-links clearfix">
267 <ul class="nav-links clearfix">
269 <li class="active">
268 <li class="active">
270 <a class="disabled" href="#edit-btn" disabled="disabled" onclick="return false">${_('Write')}</a>
269 <a class="disabled" href="#edit-btn" disabled="disabled" onclick="return false">${_('Write')}</a>
271 </li>
270 </li>
272 <li class="">
271 <li class="">
273 <a class="disabled" href="#preview-btn" disabled="disabled" onclick="return false">${_('Preview')}</a>
272 <a class="disabled" href="#preview-btn" disabled="disabled" onclick="return false">${_('Preview')}</a>
274 </li>
273 </li>
275 </ul>
274 </ul>
276 </div>
275 </div>
277
276
278 <div class="comment-area-write" style="display: block;">
277 <div class="comment-area-write" style="display: block;">
279 <div id="edit-container">
278 <div id="edit-container">
280 <div style="padding: 40px 0">
279 <div style="padding: 40px 0">
281 ${_('You need to be logged in to leave comments.')}
280 ${_('You need to be logged in to leave comments.')}
282 <a href="${h.route_path('login', _query={'came_from': h.current_route_path(request)})}">${_('Login now')}</a>
281 <a href="${h.route_path('login', _query={'came_from': h.current_route_path(request)})}">${_('Login now')}</a>
283 </div>
282 </div>
284 </div>
283 </div>
285 <div id="preview-container" class="clearfix" style="display: none;">
284 <div id="preview-container" class="clearfix" style="display: none;">
286 <div id="preview-box" class="preview-box"></div>
285 <div id="preview-box" class="preview-box"></div>
287 </div>
286 </div>
288 </div>
287 </div>
289
288
290 <div class="comment-area-footer">
289 <div class="comment-area-footer">
291 <div class="toolbar">
290 <div class="toolbar">
292 <div class="toolbar-text">
291 <div class="toolbar-text">
293 </div>
292 </div>
294 </div>
293 </div>
295 </div>
294 </div>
296 </div>
295 </div>
297
296
298 <div class="comment-footer">
297 <div class="comment-footer">
299 </div>
298 </div>
300
299
301 </div>
300 </div>
302 % endif
301 % endif
303
302
304 <script type="text/javascript">
303 <script type="text/javascript">
305 bindToggleButtons();
304 bindToggleButtons();
306 </script>
305 </script>
307 </div>
306 </div>
308 </%def>
307 </%def>
309
308
310
309
311 <%def name="comment_form(form_type, form_id='', lineno_id='{1}', review_statuses=None, form_extras=None)">
310 <%def name="comment_form(form_type, form_id='', lineno_id='{1}', review_statuses=None, form_extras=None)">
311
312 ## comment injected based on assumption that user is logged in
312 ## comment injected based on assumption that user is logged in
313
314 <form ${'id="{}"'.format(form_id) if form_id else '' |n} action="#" method="GET">
313 <form ${'id="{}"'.format(form_id) if form_id else '' |n} action="#" method="GET">
315
314
316 <div class="comment-area">
315 <div class="comment-area">
317 <div class="comment-area-header">
316 <div class="comment-area-header">
318 <ul class="nav-links clearfix">
317 <ul class="nav-links clearfix">
319 <li class="active">
318 <li class="active">
320 <a href="#edit-btn" tabindex="-1" id="edit-btn_${lineno_id}">${_('Write')}</a>
319 <a href="#edit-btn" tabindex="-1" id="edit-btn_${lineno_id}">${_('Write')}</a>
321 </li>
320 </li>
322 <li class="">
321 <li class="">
323 <a href="#preview-btn" tabindex="-1" id="preview-btn_${lineno_id}">${_('Preview')}</a>
322 <a href="#preview-btn" tabindex="-1" id="preview-btn_${lineno_id}">${_('Preview')}</a>
324 </li>
323 </li>
325 <li class="pull-right">
324 <li class="pull-right">
326 <select class="comment-type" id="comment_type_${lineno_id}" name="comment_type">
325 <select class="comment-type" id="comment_type_${lineno_id}" name="comment_type">
327 % for val in c.visual.comment_types:
326 % for val in c.visual.comment_types:
328 <option value="${val}">${val.upper()}</option>
327 <option value="${val}">${val.upper()}</option>
329 % endfor
328 % endfor
330 </select>
329 </select>
331 </li>
330 </li>
332 </ul>
331 </ul>
333 </div>
332 </div>
334
333
335 <div class="comment-area-write" style="display: block;">
334 <div class="comment-area-write" style="display: block;">
336 <div id="edit-container_${lineno_id}">
335 <div id="edit-container_${lineno_id}">
337 <textarea id="text_${lineno_id}" name="text" class="comment-block-ta ac-input"></textarea>
336 <textarea id="text_${lineno_id}" name="text" class="comment-block-ta ac-input"></textarea>
338 </div>
337 </div>
339 <div id="preview-container_${lineno_id}" class="clearfix" style="display: none;">
338 <div id="preview-container_${lineno_id}" class="clearfix" style="display: none;">
340 <div id="preview-box_${lineno_id}" class="preview-box"></div>
339 <div id="preview-box_${lineno_id}" class="preview-box"></div>
341 </div>
340 </div>
342 </div>
341 </div>
343
342
344 <div class="comment-area-footer">
343 <div class="comment-area-footer comment-attachment-uploader">
345 <div class="toolbar">
344 <div class="toolbar">
346 <div class="toolbar-text">
345 <div class="toolbar-text">
347 ${(_('Comments parsed using %s syntax with %s, and %s actions support.') % (
346 ${(_('Comments parsed using %s syntax with %s, and %s actions support.') % (
348 ('<a href="%s">%s</a>' % (h.route_url('%s_help' % c.visual.default_renderer), c.visual.default_renderer.upper())),
347 ('<a href="%s">%s</a>' % (h.route_url('%s_help' % c.visual.default_renderer), c.visual.default_renderer.upper())),
349 ('<span class="tooltip" title="%s">@mention</span>' % _('Use @username inside this text to send notification to this RhodeCode user')),
348 ('<span class="tooltip" title="%s">@mention</span>' % _('Use @username inside this text to send notification to this RhodeCode user')),
350 ('<span class="tooltip" title="%s">`/`</span>' % _('Start typing with / for certain actions to be triggered via text box.'))
349 ('<span class="tooltip" title="%s">`/`</span>' % _('Start typing with / for certain actions to be triggered via text box.'))
351 )
350 )
352 )|n}
351 )|n}
353 </div>
352 </div>
353
354 <div class="comment-attachment-text">
355 <div class="dropzone-text">
356 ${_("Drag'n Drop files here or")} <span class="link pick-attachment">${_('Choose your files')}</span>.<br>
357 </div>
358 <div class="dropzone-upload" style="display:none">
359 <i class="icon-spin animate-spin"></i> ${_('uploading...')}
360 </div>
361 </div>
362
363 ## comments dropzone template, empty on purpose
364 <div style="display: none" class="comment-attachment-uploader-template">
365 <div class="dz-file-preview" style="margin: 0">
366 <div class="dz-error-message"></div>
367 </div>
368 </div>
369
354 </div>
370 </div>
355 </div>
371 </div>
356 </div>
372 </div>
357
373
358 <div class="comment-footer">
374 <div class="comment-footer">
359
375
360 % if review_statuses:
376 % if review_statuses:
361 <div class="status_box">
377 <div class="status_box">
362 <select id="change_status_${lineno_id}" name="changeset_status">
378 <select id="change_status_${lineno_id}" name="changeset_status">
363 <option></option> ## Placeholder
379 <option></option> ## Placeholder
364 % for status, lbl in review_statuses:
380 % for status, lbl in review_statuses:
365 <option value="${status}" data-status="${status}">${lbl}</option>
381 <option value="${status}" data-status="${status}">${lbl}</option>
366 %if is_pull_request and change_status and status in ('approved', 'rejected'):
382 %if is_pull_request and change_status and status in ('approved', 'rejected'):
367 <option value="${status}_closed" data-status="${status}">${lbl} & ${_('Closed')}</option>
383 <option value="${status}_closed" data-status="${status}">${lbl} & ${_('Closed')}</option>
368 %endif
384 %endif
369 % endfor
385 % endfor
370 </select>
386 </select>
371 </div>
387 </div>
372 % endif
388 % endif
373
389
374 ## inject extra inputs into the form
390 ## inject extra inputs into the form
375 % if form_extras and isinstance(form_extras, (list, tuple)):
391 % if form_extras and isinstance(form_extras, (list, tuple)):
376 <div id="comment_form_extras">
392 <div id="comment_form_extras">
377 % for form_ex_el in form_extras:
393 % for form_ex_el in form_extras:
378 ${form_ex_el|n}
394 ${form_ex_el|n}
379 % endfor
395 % endfor
380 </div>
396 </div>
381 % endif
397 % endif
382
398
383 <div class="action-buttons">
399 <div class="action-buttons">
384 ## inline for has a file, and line-number together with cancel hide button.
400 ## inline for has a file, and line-number together with cancel hide button.
385 % if form_type == 'inline':
401 % if form_type == 'inline':
386 <input type="hidden" name="f_path" value="{0}">
402 <input type="hidden" name="f_path" value="{0}">
387 <input type="hidden" name="line" value="${lineno_id}">
403 <input type="hidden" name="line" value="${lineno_id}">
388 <button type="button" class="cb-comment-cancel" onclick="return Rhodecode.comments.cancelComment(this);">
404 <button type="button" class="cb-comment-cancel" onclick="return Rhodecode.comments.cancelComment(this);">
389 ${_('Cancel')}
405 ${_('Cancel')}
390 </button>
406 </button>
391 % endif
407 % endif
392
408
393 % if form_type != 'inline':
409 % if form_type != 'inline':
394 <div class="action-buttons-extra"></div>
410 <div class="action-buttons-extra"></div>
395 % endif
411 % endif
396
412
397 ${h.submit('save', _('Comment'), class_='btn btn-success comment-button-input')}
413 ${h.submit('save', _('Comment'), class_='btn btn-success comment-button-input')}
398
414
399 </div>
415 </div>
400 </div>
416 </div>
401
417
402 </form>
418 </form>
403
419
404 </%def> No newline at end of file
420 </%def>
General Comments 0
You need to be logged in to leave comments. Login now