##// END OF EJS Templates
file-upload: allow specfing custom filename for uploaded archives.
marcink -
r1683:b17a8f3c default
parent child Browse files
Show More
@@ -1,1102 +1,1101 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2017 RhodeCode GmbH
3 # Copyright (C) 2010-2017 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 """
21 """
22 Files controller for RhodeCode Enterprise
22 Files controller for RhodeCode Enterprise
23 """
23 """
24
24
25 import itertools
25 import itertools
26 import logging
26 import logging
27 import os
27 import os
28 import shutil
28 import shutil
29 import tempfile
29 import tempfile
30
30
31 from pylons import request, response, tmpl_context as c, url
31 from pylons import request, response, tmpl_context as c, url
32 from pylons.i18n.translation import _
32 from pylons.i18n.translation import _
33 from pylons.controllers.util import redirect
33 from pylons.controllers.util import redirect
34 from webob.exc import HTTPNotFound, HTTPBadRequest
34 from webob.exc import HTTPNotFound, HTTPBadRequest
35
35
36 from rhodecode.controllers.utils import parse_path_ref
36 from rhodecode.controllers.utils import parse_path_ref
37 from rhodecode.lib import diffs, helpers as h, caches
37 from rhodecode.lib import diffs, helpers as h, caches
38 from rhodecode.lib.compat import OrderedDict
38 from rhodecode.lib.compat import OrderedDict
39 from rhodecode.lib.codeblocks import (
39 from rhodecode.lib.codeblocks import (
40 filenode_as_lines_tokens, filenode_as_annotated_lines_tokens)
40 filenode_as_lines_tokens, filenode_as_annotated_lines_tokens)
41 from rhodecode.lib.utils import jsonify, action_logger
41 from rhodecode.lib.utils import jsonify, action_logger
42 from rhodecode.lib.utils2 import (
42 from rhodecode.lib.utils2 import (
43 convert_line_endings, detect_mode, safe_str, str2bool)
43 convert_line_endings, detect_mode, safe_str, str2bool)
44 from rhodecode.lib.auth import (
44 from rhodecode.lib.auth import (
45 LoginRequired, HasRepoPermissionAnyDecorator, CSRFRequired, XHRRequired)
45 LoginRequired, HasRepoPermissionAnyDecorator, CSRFRequired, XHRRequired)
46 from rhodecode.lib.base import BaseRepoController, render
46 from rhodecode.lib.base import BaseRepoController, render
47 from rhodecode.lib.vcs import path as vcspath
47 from rhodecode.lib.vcs import path as vcspath
48 from rhodecode.lib.vcs.backends.base import EmptyCommit
48 from rhodecode.lib.vcs.backends.base import EmptyCommit
49 from rhodecode.lib.vcs.conf import settings
49 from rhodecode.lib.vcs.conf import settings
50 from rhodecode.lib.vcs.exceptions import (
50 from rhodecode.lib.vcs.exceptions import (
51 RepositoryError, CommitDoesNotExistError, EmptyRepositoryError,
51 RepositoryError, CommitDoesNotExistError, EmptyRepositoryError,
52 ImproperArchiveTypeError, VCSError, NodeAlreadyExistsError,
52 ImproperArchiveTypeError, VCSError, NodeAlreadyExistsError,
53 NodeDoesNotExistError, CommitError, NodeError)
53 NodeDoesNotExistError, CommitError, NodeError)
54 from rhodecode.lib.vcs.nodes import FileNode
54 from rhodecode.lib.vcs.nodes import FileNode
55
55
56 from rhodecode.model.repo import RepoModel
56 from rhodecode.model.repo import RepoModel
57 from rhodecode.model.scm import ScmModel
57 from rhodecode.model.scm import ScmModel
58 from rhodecode.model.db import Repository
58 from rhodecode.model.db import Repository
59
59
60 from rhodecode.controllers.changeset import (
60 from rhodecode.controllers.changeset import (
61 _ignorews_url, _context_url, get_line_ctx, get_ignore_ws)
61 _ignorews_url, _context_url, get_line_ctx, get_ignore_ws)
62 from rhodecode.lib.exceptions import NonRelativePathError
62 from rhodecode.lib.exceptions import NonRelativePathError
63
63
64 log = logging.getLogger(__name__)
64 log = logging.getLogger(__name__)
65
65
66
66
67 class FilesController(BaseRepoController):
67 class FilesController(BaseRepoController):
68
68
69 def __before__(self):
69 def __before__(self):
70 super(FilesController, self).__before__()
70 super(FilesController, self).__before__()
71 c.cut_off_limit = self.cut_off_limit_file
71 c.cut_off_limit = self.cut_off_limit_file
72
72
73 def _get_default_encoding(self):
73 def _get_default_encoding(self):
74 enc_list = getattr(c, 'default_encodings', [])
74 enc_list = getattr(c, 'default_encodings', [])
75 return enc_list[0] if enc_list else 'UTF-8'
75 return enc_list[0] if enc_list else 'UTF-8'
76
76
77 def __get_commit_or_redirect(self, commit_id, repo_name,
77 def __get_commit_or_redirect(self, commit_id, repo_name,
78 redirect_after=True):
78 redirect_after=True):
79 """
79 """
80 This is a safe way to get commit. If an error occurs it redirects to
80 This is a safe way to get commit. If an error occurs it redirects to
81 tip with proper message
81 tip with proper message
82
82
83 :param commit_id: id of commit to fetch
83 :param commit_id: id of commit to fetch
84 :param repo_name: repo name to redirect after
84 :param repo_name: repo name to redirect after
85 :param redirect_after: toggle redirection
85 :param redirect_after: toggle redirection
86 """
86 """
87 try:
87 try:
88 return c.rhodecode_repo.get_commit(commit_id)
88 return c.rhodecode_repo.get_commit(commit_id)
89 except EmptyRepositoryError:
89 except EmptyRepositoryError:
90 if not redirect_after:
90 if not redirect_after:
91 return None
91 return None
92 url_ = url('files_add_home',
92 url_ = url('files_add_home',
93 repo_name=c.repo_name,
93 repo_name=c.repo_name,
94 revision=0, f_path='', anchor='edit')
94 revision=0, f_path='', anchor='edit')
95 if h.HasRepoPermissionAny(
95 if h.HasRepoPermissionAny(
96 'repository.write', 'repository.admin')(c.repo_name):
96 'repository.write', 'repository.admin')(c.repo_name):
97 add_new = h.link_to(
97 add_new = h.link_to(
98 _('Click here to add a new file.'),
98 _('Click here to add a new file.'),
99 url_, class_="alert-link")
99 url_, class_="alert-link")
100 else:
100 else:
101 add_new = ""
101 add_new = ""
102 h.flash(h.literal(
102 h.flash(h.literal(
103 _('There are no files yet. %s') % add_new), category='warning')
103 _('There are no files yet. %s') % add_new), category='warning')
104 redirect(h.url('summary_home', repo_name=repo_name))
104 redirect(h.url('summary_home', repo_name=repo_name))
105 except (CommitDoesNotExistError, LookupError):
105 except (CommitDoesNotExistError, LookupError):
106 msg = _('No such commit exists for this repository')
106 msg = _('No such commit exists for this repository')
107 h.flash(msg, category='error')
107 h.flash(msg, category='error')
108 raise HTTPNotFound()
108 raise HTTPNotFound()
109 except RepositoryError as e:
109 except RepositoryError as e:
110 h.flash(safe_str(e), category='error')
110 h.flash(safe_str(e), category='error')
111 raise HTTPNotFound()
111 raise HTTPNotFound()
112
112
113 def __get_filenode_or_redirect(self, repo_name, commit, path):
113 def __get_filenode_or_redirect(self, repo_name, commit, path):
114 """
114 """
115 Returns file_node, if error occurs or given path is directory,
115 Returns file_node, if error occurs or given path is directory,
116 it'll redirect to top level path
116 it'll redirect to top level path
117
117
118 :param repo_name: repo_name
118 :param repo_name: repo_name
119 :param commit: given commit
119 :param commit: given commit
120 :param path: path to lookup
120 :param path: path to lookup
121 """
121 """
122 try:
122 try:
123 file_node = commit.get_node(path)
123 file_node = commit.get_node(path)
124 if file_node.is_dir():
124 if file_node.is_dir():
125 raise RepositoryError('The given path is a directory')
125 raise RepositoryError('The given path is a directory')
126 except CommitDoesNotExistError:
126 except CommitDoesNotExistError:
127 msg = _('No such commit exists for this repository')
127 msg = _('No such commit exists for this repository')
128 log.exception(msg)
128 log.exception(msg)
129 h.flash(msg, category='error')
129 h.flash(msg, category='error')
130 raise HTTPNotFound()
130 raise HTTPNotFound()
131 except RepositoryError as e:
131 except RepositoryError as e:
132 h.flash(safe_str(e), category='error')
132 h.flash(safe_str(e), category='error')
133 raise HTTPNotFound()
133 raise HTTPNotFound()
134
134
135 return file_node
135 return file_node
136
136
137 def __get_tree_cache_manager(self, repo_name, namespace_type):
137 def __get_tree_cache_manager(self, repo_name, namespace_type):
138 _namespace = caches.get_repo_namespace_key(namespace_type, repo_name)
138 _namespace = caches.get_repo_namespace_key(namespace_type, repo_name)
139 return caches.get_cache_manager('repo_cache_long', _namespace)
139 return caches.get_cache_manager('repo_cache_long', _namespace)
140
140
141 def _get_tree_at_commit(self, repo_name, commit_id, f_path,
141 def _get_tree_at_commit(self, repo_name, commit_id, f_path,
142 full_load=False, force=False):
142 full_load=False, force=False):
143 def _cached_tree():
143 def _cached_tree():
144 log.debug('Generating cached file tree for %s, %s, %s',
144 log.debug('Generating cached file tree for %s, %s, %s',
145 repo_name, commit_id, f_path)
145 repo_name, commit_id, f_path)
146 c.full_load = full_load
146 c.full_load = full_load
147 return render('files/files_browser_tree.mako')
147 return render('files/files_browser_tree.mako')
148
148
149 cache_manager = self.__get_tree_cache_manager(
149 cache_manager = self.__get_tree_cache_manager(
150 repo_name, caches.FILE_TREE)
150 repo_name, caches.FILE_TREE)
151
151
152 cache_key = caches.compute_key_from_params(
152 cache_key = caches.compute_key_from_params(
153 repo_name, commit_id, f_path)
153 repo_name, commit_id, f_path)
154
154
155 if force:
155 if force:
156 # we want to force recompute of caches
156 # we want to force recompute of caches
157 cache_manager.remove_value(cache_key)
157 cache_manager.remove_value(cache_key)
158
158
159 return cache_manager.get(cache_key, createfunc=_cached_tree)
159 return cache_manager.get(cache_key, createfunc=_cached_tree)
160
160
161 def _get_nodelist_at_commit(self, repo_name, commit_id, f_path):
161 def _get_nodelist_at_commit(self, repo_name, commit_id, f_path):
162 def _cached_nodes():
162 def _cached_nodes():
163 log.debug('Generating cached nodelist for %s, %s, %s',
163 log.debug('Generating cached nodelist for %s, %s, %s',
164 repo_name, commit_id, f_path)
164 repo_name, commit_id, f_path)
165 _d, _f = ScmModel().get_nodes(
165 _d, _f = ScmModel().get_nodes(
166 repo_name, commit_id, f_path, flat=False)
166 repo_name, commit_id, f_path, flat=False)
167 return _d + _f
167 return _d + _f
168
168
169 cache_manager = self.__get_tree_cache_manager(
169 cache_manager = self.__get_tree_cache_manager(
170 repo_name, caches.FILE_SEARCH_TREE_META)
170 repo_name, caches.FILE_SEARCH_TREE_META)
171
171
172 cache_key = caches.compute_key_from_params(
172 cache_key = caches.compute_key_from_params(
173 repo_name, commit_id, f_path)
173 repo_name, commit_id, f_path)
174 return cache_manager.get(cache_key, createfunc=_cached_nodes)
174 return cache_manager.get(cache_key, createfunc=_cached_nodes)
175
175
176 @LoginRequired()
176 @LoginRequired()
177 @HasRepoPermissionAnyDecorator(
177 @HasRepoPermissionAnyDecorator(
178 'repository.read', 'repository.write', 'repository.admin')
178 'repository.read', 'repository.write', 'repository.admin')
179 def index(
179 def index(
180 self, repo_name, revision, f_path, annotate=False, rendered=False):
180 self, repo_name, revision, f_path, annotate=False, rendered=False):
181 commit_id = revision
181 commit_id = revision
182
182
183 # redirect to given commit_id from form if given
183 # redirect to given commit_id from form if given
184 get_commit_id = request.GET.get('at_rev', None)
184 get_commit_id = request.GET.get('at_rev', None)
185 if get_commit_id:
185 if get_commit_id:
186 self.__get_commit_or_redirect(get_commit_id, repo_name)
186 self.__get_commit_or_redirect(get_commit_id, repo_name)
187
187
188 c.commit = self.__get_commit_or_redirect(commit_id, repo_name)
188 c.commit = self.__get_commit_or_redirect(commit_id, repo_name)
189 c.branch = request.GET.get('branch', None)
189 c.branch = request.GET.get('branch', None)
190 c.f_path = f_path
190 c.f_path = f_path
191 c.annotate = annotate
191 c.annotate = annotate
192 # default is false, but .rst/.md files later are autorendered, we can
192 # default is false, but .rst/.md files later are autorendered, we can
193 # overwrite autorendering by setting this GET flag
193 # overwrite autorendering by setting this GET flag
194 c.renderer = rendered or not request.GET.get('no-render', False)
194 c.renderer = rendered or not request.GET.get('no-render', False)
195
195
196 # prev link
196 # prev link
197 try:
197 try:
198 prev_commit = c.commit.prev(c.branch)
198 prev_commit = c.commit.prev(c.branch)
199 c.prev_commit = prev_commit
199 c.prev_commit = prev_commit
200 c.url_prev = url('files_home', repo_name=c.repo_name,
200 c.url_prev = url('files_home', repo_name=c.repo_name,
201 revision=prev_commit.raw_id, f_path=f_path)
201 revision=prev_commit.raw_id, f_path=f_path)
202 if c.branch:
202 if c.branch:
203 c.url_prev += '?branch=%s' % c.branch
203 c.url_prev += '?branch=%s' % c.branch
204 except (CommitDoesNotExistError, VCSError):
204 except (CommitDoesNotExistError, VCSError):
205 c.url_prev = '#'
205 c.url_prev = '#'
206 c.prev_commit = EmptyCommit()
206 c.prev_commit = EmptyCommit()
207
207
208 # next link
208 # next link
209 try:
209 try:
210 next_commit = c.commit.next(c.branch)
210 next_commit = c.commit.next(c.branch)
211 c.next_commit = next_commit
211 c.next_commit = next_commit
212 c.url_next = url('files_home', repo_name=c.repo_name,
212 c.url_next = url('files_home', repo_name=c.repo_name,
213 revision=next_commit.raw_id, f_path=f_path)
213 revision=next_commit.raw_id, f_path=f_path)
214 if c.branch:
214 if c.branch:
215 c.url_next += '?branch=%s' % c.branch
215 c.url_next += '?branch=%s' % c.branch
216 except (CommitDoesNotExistError, VCSError):
216 except (CommitDoesNotExistError, VCSError):
217 c.url_next = '#'
217 c.url_next = '#'
218 c.next_commit = EmptyCommit()
218 c.next_commit = EmptyCommit()
219
219
220 # files or dirs
220 # files or dirs
221 try:
221 try:
222 c.file = c.commit.get_node(f_path)
222 c.file = c.commit.get_node(f_path)
223 c.file_author = True
223 c.file_author = True
224 c.file_tree = ''
224 c.file_tree = ''
225 if c.file.is_file():
225 if c.file.is_file():
226 c.lf_node = c.file.get_largefile_node()
226 c.lf_node = c.file.get_largefile_node()
227
227
228 c.file_source_page = 'true'
228 c.file_source_page = 'true'
229 c.file_last_commit = c.file.last_commit
229 c.file_last_commit = c.file.last_commit
230 if c.file.size < self.cut_off_limit_file:
230 if c.file.size < self.cut_off_limit_file:
231 if c.annotate: # annotation has precedence over renderer
231 if c.annotate: # annotation has precedence over renderer
232 c.annotated_lines = filenode_as_annotated_lines_tokens(
232 c.annotated_lines = filenode_as_annotated_lines_tokens(
233 c.file
233 c.file
234 )
234 )
235 else:
235 else:
236 c.renderer = (
236 c.renderer = (
237 c.renderer and h.renderer_from_filename(c.file.path)
237 c.renderer and h.renderer_from_filename(c.file.path)
238 )
238 )
239 if not c.renderer:
239 if not c.renderer:
240 c.lines = filenode_as_lines_tokens(c.file)
240 c.lines = filenode_as_lines_tokens(c.file)
241
241
242 c.on_branch_head = self._is_valid_head(
242 c.on_branch_head = self._is_valid_head(
243 commit_id, c.rhodecode_repo)
243 commit_id, c.rhodecode_repo)
244
244
245 branch = c.commit.branch if (
245 branch = c.commit.branch if (
246 c.commit.branch and '/' not in c.commit.branch) else None
246 c.commit.branch and '/' not in c.commit.branch) else None
247 c.branch_or_raw_id = branch or c.commit.raw_id
247 c.branch_or_raw_id = branch or c.commit.raw_id
248 c.branch_name = c.commit.branch or h.short_id(c.commit.raw_id)
248 c.branch_name = c.commit.branch or h.short_id(c.commit.raw_id)
249
249
250 author = c.file_last_commit.author
250 author = c.file_last_commit.author
251 c.authors = [(h.email(author),
251 c.authors = [(h.email(author),
252 h.person(author, 'username_or_name_or_email'))]
252 h.person(author, 'username_or_name_or_email'))]
253 else:
253 else:
254 c.file_source_page = 'false'
254 c.file_source_page = 'false'
255 c.authors = []
255 c.authors = []
256 c.file_tree = self._get_tree_at_commit(
256 c.file_tree = self._get_tree_at_commit(
257 repo_name, c.commit.raw_id, f_path)
257 repo_name, c.commit.raw_id, f_path)
258
258
259 except RepositoryError as e:
259 except RepositoryError as e:
260 h.flash(safe_str(e), category='error')
260 h.flash(safe_str(e), category='error')
261 raise HTTPNotFound()
261 raise HTTPNotFound()
262
262
263 if request.environ.get('HTTP_X_PJAX'):
263 if request.environ.get('HTTP_X_PJAX'):
264 return render('files/files_pjax.mako')
264 return render('files/files_pjax.mako')
265
265
266 return render('files/files.mako')
266 return render('files/files.mako')
267
267
268 @LoginRequired()
268 @LoginRequired()
269 @HasRepoPermissionAnyDecorator(
269 @HasRepoPermissionAnyDecorator(
270 'repository.read', 'repository.write', 'repository.admin')
270 'repository.read', 'repository.write', 'repository.admin')
271 def annotate_previous(self, repo_name, revision, f_path):
271 def annotate_previous(self, repo_name, revision, f_path):
272
272
273 commit_id = revision
273 commit_id = revision
274 commit = self.__get_commit_or_redirect(commit_id, repo_name)
274 commit = self.__get_commit_or_redirect(commit_id, repo_name)
275 prev_commit_id = commit.raw_id
275 prev_commit_id = commit.raw_id
276
276
277 f_path = f_path
277 f_path = f_path
278 is_file = False
278 is_file = False
279 try:
279 try:
280 _file = commit.get_node(f_path)
280 _file = commit.get_node(f_path)
281 is_file = _file.is_file()
281 is_file = _file.is_file()
282 except (NodeDoesNotExistError, CommitDoesNotExistError, VCSError):
282 except (NodeDoesNotExistError, CommitDoesNotExistError, VCSError):
283 pass
283 pass
284
284
285 if is_file:
285 if is_file:
286 history = commit.get_file_history(f_path)
286 history = commit.get_file_history(f_path)
287 prev_commit_id = history[1].raw_id \
287 prev_commit_id = history[1].raw_id \
288 if len(history) > 1 else prev_commit_id
288 if len(history) > 1 else prev_commit_id
289
289
290 return redirect(h.url(
290 return redirect(h.url(
291 'files_annotate_home', repo_name=repo_name,
291 'files_annotate_home', repo_name=repo_name,
292 revision=prev_commit_id, f_path=f_path))
292 revision=prev_commit_id, f_path=f_path))
293
293
294 @LoginRequired()
294 @LoginRequired()
295 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
295 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
296 'repository.admin')
296 'repository.admin')
297 @jsonify
297 @jsonify
298 def history(self, repo_name, revision, f_path):
298 def history(self, repo_name, revision, f_path):
299 commit = self.__get_commit_or_redirect(revision, repo_name)
299 commit = self.__get_commit_or_redirect(revision, repo_name)
300 f_path = f_path
300 f_path = f_path
301 _file = commit.get_node(f_path)
301 _file = commit.get_node(f_path)
302 if _file.is_file():
302 if _file.is_file():
303 file_history, _hist = self._get_node_history(commit, f_path)
303 file_history, _hist = self._get_node_history(commit, f_path)
304
304
305 res = []
305 res = []
306 for obj in file_history:
306 for obj in file_history:
307 res.append({
307 res.append({
308 'text': obj[1],
308 'text': obj[1],
309 'children': [{'id': o[0], 'text': o[1]} for o in obj[0]]
309 'children': [{'id': o[0], 'text': o[1]} for o in obj[0]]
310 })
310 })
311
311
312 data = {
312 data = {
313 'more': False,
313 'more': False,
314 'results': res
314 'results': res
315 }
315 }
316 return data
316 return data
317
317
318 @LoginRequired()
318 @LoginRequired()
319 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
319 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
320 'repository.admin')
320 'repository.admin')
321 def authors(self, repo_name, revision, f_path):
321 def authors(self, repo_name, revision, f_path):
322 commit = self.__get_commit_or_redirect(revision, repo_name)
322 commit = self.__get_commit_or_redirect(revision, repo_name)
323 file_node = commit.get_node(f_path)
323 file_node = commit.get_node(f_path)
324 if file_node.is_file():
324 if file_node.is_file():
325 c.file_last_commit = file_node.last_commit
325 c.file_last_commit = file_node.last_commit
326 if request.GET.get('annotate') == '1':
326 if request.GET.get('annotate') == '1':
327 # use _hist from annotation if annotation mode is on
327 # use _hist from annotation if annotation mode is on
328 commit_ids = set(x[1] for x in file_node.annotate)
328 commit_ids = set(x[1] for x in file_node.annotate)
329 _hist = (
329 _hist = (
330 c.rhodecode_repo.get_commit(commit_id)
330 c.rhodecode_repo.get_commit(commit_id)
331 for commit_id in commit_ids)
331 for commit_id in commit_ids)
332 else:
332 else:
333 _f_history, _hist = self._get_node_history(commit, f_path)
333 _f_history, _hist = self._get_node_history(commit, f_path)
334 c.file_author = False
334 c.file_author = False
335 c.authors = []
335 c.authors = []
336 for author in set(commit.author for commit in _hist):
336 for author in set(commit.author for commit in _hist):
337 c.authors.append((
337 c.authors.append((
338 h.email(author),
338 h.email(author),
339 h.person(author, 'username_or_name_or_email')))
339 h.person(author, 'username_or_name_or_email')))
340 return render('files/file_authors_box.mako')
340 return render('files/file_authors_box.mako')
341
341
342 @LoginRequired()
342 @LoginRequired()
343 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
343 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
344 'repository.admin')
344 'repository.admin')
345 def rawfile(self, repo_name, revision, f_path):
345 def rawfile(self, repo_name, revision, f_path):
346 """
346 """
347 Action for download as raw
347 Action for download as raw
348 """
348 """
349 commit = self.__get_commit_or_redirect(revision, repo_name)
349 commit = self.__get_commit_or_redirect(revision, repo_name)
350 file_node = self.__get_filenode_or_redirect(repo_name, commit, f_path)
350 file_node = self.__get_filenode_or_redirect(repo_name, commit, f_path)
351
351
352 if request.GET.get('lf'):
352 if request.GET.get('lf'):
353 # only if lf get flag is passed, we download this file
353 # only if lf get flag is passed, we download this file
354 # as LFS/Largefile
354 # as LFS/Largefile
355 lf_node = file_node.get_largefile_node()
355 lf_node = file_node.get_largefile_node()
356 if lf_node:
356 if lf_node:
357 # overwrite our pointer with the REAL large-file
357 # overwrite our pointer with the REAL large-file
358 file_node = lf_node
358 file_node = lf_node
359
359
360 response.content_disposition = 'attachment; filename=%s' % \
360 response.content_disposition = 'attachment; filename=%s' % \
361 safe_str(f_path.split(Repository.NAME_SEP)[-1])
361 safe_str(f_path.split(Repository.NAME_SEP)[-1])
362
362
363 response.content_type = file_node.mimetype
363 response.content_type = file_node.mimetype
364 charset = self._get_default_encoding()
364 charset = self._get_default_encoding()
365 if charset:
365 if charset:
366 response.charset = charset
366 response.charset = charset
367
367
368 return file_node.content
368 return file_node.content
369
369
370 @LoginRequired()
370 @LoginRequired()
371 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
371 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
372 'repository.admin')
372 'repository.admin')
373 def raw(self, repo_name, revision, f_path):
373 def raw(self, repo_name, revision, f_path):
374 """
374 """
375 Action for show as raw, some mimetypes are "rendered",
375 Action for show as raw, some mimetypes are "rendered",
376 those include images, icons.
376 those include images, icons.
377 """
377 """
378 commit = self.__get_commit_or_redirect(revision, repo_name)
378 commit = self.__get_commit_or_redirect(revision, repo_name)
379 file_node = self.__get_filenode_or_redirect(repo_name, commit, f_path)
379 file_node = self.__get_filenode_or_redirect(repo_name, commit, f_path)
380
380
381 raw_mimetype_mapping = {
381 raw_mimetype_mapping = {
382 # map original mimetype to a mimetype used for "show as raw"
382 # map original mimetype to a mimetype used for "show as raw"
383 # you can also provide a content-disposition to override the
383 # you can also provide a content-disposition to override the
384 # default "attachment" disposition.
384 # default "attachment" disposition.
385 # orig_type: (new_type, new_dispo)
385 # orig_type: (new_type, new_dispo)
386
386
387 # show images inline:
387 # show images inline:
388 # Do not re-add SVG: it is unsafe and permits XSS attacks. One can
388 # Do not re-add SVG: it is unsafe and permits XSS attacks. One can
389 # for example render an SVG with javascript inside or even render
389 # for example render an SVG with javascript inside or even render
390 # HTML.
390 # HTML.
391 'image/x-icon': ('image/x-icon', 'inline'),
391 'image/x-icon': ('image/x-icon', 'inline'),
392 'image/png': ('image/png', 'inline'),
392 'image/png': ('image/png', 'inline'),
393 'image/gif': ('image/gif', 'inline'),
393 'image/gif': ('image/gif', 'inline'),
394 'image/jpeg': ('image/jpeg', 'inline'),
394 'image/jpeg': ('image/jpeg', 'inline'),
395 'application/pdf': ('application/pdf', 'inline'),
395 'application/pdf': ('application/pdf', 'inline'),
396 }
396 }
397
397
398 mimetype = file_node.mimetype
398 mimetype = file_node.mimetype
399 try:
399 try:
400 mimetype, dispo = raw_mimetype_mapping[mimetype]
400 mimetype, dispo = raw_mimetype_mapping[mimetype]
401 except KeyError:
401 except KeyError:
402 # we don't know anything special about this, handle it safely
402 # we don't know anything special about this, handle it safely
403 if file_node.is_binary:
403 if file_node.is_binary:
404 # do same as download raw for binary files
404 # do same as download raw for binary files
405 mimetype, dispo = 'application/octet-stream', 'attachment'
405 mimetype, dispo = 'application/octet-stream', 'attachment'
406 else:
406 else:
407 # do not just use the original mimetype, but force text/plain,
407 # do not just use the original mimetype, but force text/plain,
408 # otherwise it would serve text/html and that might be unsafe.
408 # otherwise it would serve text/html and that might be unsafe.
409 # Note: underlying vcs library fakes text/plain mimetype if the
409 # Note: underlying vcs library fakes text/plain mimetype if the
410 # mimetype can not be determined and it thinks it is not
410 # mimetype can not be determined and it thinks it is not
411 # binary.This might lead to erroneous text display in some
411 # binary.This might lead to erroneous text display in some
412 # cases, but helps in other cases, like with text files
412 # cases, but helps in other cases, like with text files
413 # without extension.
413 # without extension.
414 mimetype, dispo = 'text/plain', 'inline'
414 mimetype, dispo = 'text/plain', 'inline'
415
415
416 if dispo == 'attachment':
416 if dispo == 'attachment':
417 dispo = 'attachment; filename=%s' % safe_str(
417 dispo = 'attachment; filename=%s' % safe_str(
418 f_path.split(os.sep)[-1])
418 f_path.split(os.sep)[-1])
419
419
420 response.content_disposition = dispo
420 response.content_disposition = dispo
421 response.content_type = mimetype
421 response.content_type = mimetype
422 charset = self._get_default_encoding()
422 charset = self._get_default_encoding()
423 if charset:
423 if charset:
424 response.charset = charset
424 response.charset = charset
425 return file_node.content
425 return file_node.content
426
426
427 @CSRFRequired()
427 @CSRFRequired()
428 @LoginRequired()
428 @LoginRequired()
429 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
429 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
430 def delete(self, repo_name, revision, f_path):
430 def delete(self, repo_name, revision, f_path):
431 commit_id = revision
431 commit_id = revision
432
432
433 repo = c.rhodecode_db_repo
433 repo = c.rhodecode_db_repo
434 if repo.enable_locking and repo.locked[0]:
434 if repo.enable_locking and repo.locked[0]:
435 h.flash(_('This repository has been locked by %s on %s')
435 h.flash(_('This repository has been locked by %s on %s')
436 % (h.person_by_id(repo.locked[0]),
436 % (h.person_by_id(repo.locked[0]),
437 h.format_date(h.time_to_datetime(repo.locked[1]))),
437 h.format_date(h.time_to_datetime(repo.locked[1]))),
438 'warning')
438 'warning')
439 return redirect(h.url('files_home',
439 return redirect(h.url('files_home',
440 repo_name=repo_name, revision='tip'))
440 repo_name=repo_name, revision='tip'))
441
441
442 if not self._is_valid_head(commit_id, repo.scm_instance()):
442 if not self._is_valid_head(commit_id, repo.scm_instance()):
443 h.flash(_('You can only delete files with revision '
443 h.flash(_('You can only delete files with revision '
444 'being a valid branch '), category='warning')
444 'being a valid branch '), category='warning')
445 return redirect(h.url('files_home',
445 return redirect(h.url('files_home',
446 repo_name=repo_name, revision='tip',
446 repo_name=repo_name, revision='tip',
447 f_path=f_path))
447 f_path=f_path))
448
448
449 c.commit = self.__get_commit_or_redirect(commit_id, repo_name)
449 c.commit = self.__get_commit_or_redirect(commit_id, repo_name)
450 c.file = self.__get_filenode_or_redirect(repo_name, c.commit, f_path)
450 c.file = self.__get_filenode_or_redirect(repo_name, c.commit, f_path)
451
451
452 c.default_message = _(
452 c.default_message = _(
453 'Deleted file %s via RhodeCode Enterprise') % (f_path)
453 'Deleted file %s via RhodeCode Enterprise') % (f_path)
454 c.f_path = f_path
454 c.f_path = f_path
455 node_path = f_path
455 node_path = f_path
456 author = c.rhodecode_user.full_contact
456 author = c.rhodecode_user.full_contact
457 message = request.POST.get('message') or c.default_message
457 message = request.POST.get('message') or c.default_message
458 try:
458 try:
459 nodes = {
459 nodes = {
460 node_path: {
460 node_path: {
461 'content': ''
461 'content': ''
462 }
462 }
463 }
463 }
464 self.scm_model.delete_nodes(
464 self.scm_model.delete_nodes(
465 user=c.rhodecode_user.user_id, repo=c.rhodecode_db_repo,
465 user=c.rhodecode_user.user_id, repo=c.rhodecode_db_repo,
466 message=message,
466 message=message,
467 nodes=nodes,
467 nodes=nodes,
468 parent_commit=c.commit,
468 parent_commit=c.commit,
469 author=author,
469 author=author,
470 )
470 )
471
471
472 h.flash(_('Successfully deleted file %s') % f_path,
472 h.flash(_('Successfully deleted file %s') % f_path,
473 category='success')
473 category='success')
474 except Exception:
474 except Exception:
475 msg = _('Error occurred during commit')
475 msg = _('Error occurred during commit')
476 log.exception(msg)
476 log.exception(msg)
477 h.flash(msg, category='error')
477 h.flash(msg, category='error')
478 return redirect(url('changeset_home',
478 return redirect(url('changeset_home',
479 repo_name=c.repo_name, revision='tip'))
479 repo_name=c.repo_name, revision='tip'))
480
480
481 @LoginRequired()
481 @LoginRequired()
482 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
482 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
483 def delete_home(self, repo_name, revision, f_path):
483 def delete_home(self, repo_name, revision, f_path):
484 commit_id = revision
484 commit_id = revision
485
485
486 repo = c.rhodecode_db_repo
486 repo = c.rhodecode_db_repo
487 if repo.enable_locking and repo.locked[0]:
487 if repo.enable_locking and repo.locked[0]:
488 h.flash(_('This repository has been locked by %s on %s')
488 h.flash(_('This repository has been locked by %s on %s')
489 % (h.person_by_id(repo.locked[0]),
489 % (h.person_by_id(repo.locked[0]),
490 h.format_date(h.time_to_datetime(repo.locked[1]))),
490 h.format_date(h.time_to_datetime(repo.locked[1]))),
491 'warning')
491 'warning')
492 return redirect(h.url('files_home',
492 return redirect(h.url('files_home',
493 repo_name=repo_name, revision='tip'))
493 repo_name=repo_name, revision='tip'))
494
494
495 if not self._is_valid_head(commit_id, repo.scm_instance()):
495 if not self._is_valid_head(commit_id, repo.scm_instance()):
496 h.flash(_('You can only delete files with revision '
496 h.flash(_('You can only delete files with revision '
497 'being a valid branch '), category='warning')
497 'being a valid branch '), category='warning')
498 return redirect(h.url('files_home',
498 return redirect(h.url('files_home',
499 repo_name=repo_name, revision='tip',
499 repo_name=repo_name, revision='tip',
500 f_path=f_path))
500 f_path=f_path))
501
501
502 c.commit = self.__get_commit_or_redirect(commit_id, repo_name)
502 c.commit = self.__get_commit_or_redirect(commit_id, repo_name)
503 c.file = self.__get_filenode_or_redirect(repo_name, c.commit, f_path)
503 c.file = self.__get_filenode_or_redirect(repo_name, c.commit, f_path)
504
504
505 c.default_message = _(
505 c.default_message = _(
506 'Deleted file %s via RhodeCode Enterprise') % (f_path)
506 'Deleted file %s via RhodeCode Enterprise') % (f_path)
507 c.f_path = f_path
507 c.f_path = f_path
508
508
509 return render('files/files_delete.mako')
509 return render('files/files_delete.mako')
510
510
511 @CSRFRequired()
511 @CSRFRequired()
512 @LoginRequired()
512 @LoginRequired()
513 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
513 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
514 def edit(self, repo_name, revision, f_path):
514 def edit(self, repo_name, revision, f_path):
515 commit_id = revision
515 commit_id = revision
516
516
517 repo = c.rhodecode_db_repo
517 repo = c.rhodecode_db_repo
518 if repo.enable_locking and repo.locked[0]:
518 if repo.enable_locking and repo.locked[0]:
519 h.flash(_('This repository has been locked by %s on %s')
519 h.flash(_('This repository has been locked by %s on %s')
520 % (h.person_by_id(repo.locked[0]),
520 % (h.person_by_id(repo.locked[0]),
521 h.format_date(h.time_to_datetime(repo.locked[1]))),
521 h.format_date(h.time_to_datetime(repo.locked[1]))),
522 'warning')
522 'warning')
523 return redirect(h.url('files_home',
523 return redirect(h.url('files_home',
524 repo_name=repo_name, revision='tip'))
524 repo_name=repo_name, revision='tip'))
525
525
526 if not self._is_valid_head(commit_id, repo.scm_instance()):
526 if not self._is_valid_head(commit_id, repo.scm_instance()):
527 h.flash(_('You can only edit files with revision '
527 h.flash(_('You can only edit files with revision '
528 'being a valid branch '), category='warning')
528 'being a valid branch '), category='warning')
529 return redirect(h.url('files_home',
529 return redirect(h.url('files_home',
530 repo_name=repo_name, revision='tip',
530 repo_name=repo_name, revision='tip',
531 f_path=f_path))
531 f_path=f_path))
532
532
533 c.commit = self.__get_commit_or_redirect(commit_id, repo_name)
533 c.commit = self.__get_commit_or_redirect(commit_id, repo_name)
534 c.file = self.__get_filenode_or_redirect(repo_name, c.commit, f_path)
534 c.file = self.__get_filenode_or_redirect(repo_name, c.commit, f_path)
535
535
536 if c.file.is_binary:
536 if c.file.is_binary:
537 return redirect(url('files_home', repo_name=c.repo_name,
537 return redirect(url('files_home', repo_name=c.repo_name,
538 revision=c.commit.raw_id, f_path=f_path))
538 revision=c.commit.raw_id, f_path=f_path))
539 c.default_message = _(
539 c.default_message = _(
540 'Edited file %s via RhodeCode Enterprise') % (f_path)
540 'Edited file %s via RhodeCode Enterprise') % (f_path)
541 c.f_path = f_path
541 c.f_path = f_path
542 old_content = c.file.content
542 old_content = c.file.content
543 sl = old_content.splitlines(1)
543 sl = old_content.splitlines(1)
544 first_line = sl[0] if sl else ''
544 first_line = sl[0] if sl else ''
545
545
546 # modes: 0 - Unix, 1 - Mac, 2 - DOS
546 # modes: 0 - Unix, 1 - Mac, 2 - DOS
547 mode = detect_mode(first_line, 0)
547 mode = detect_mode(first_line, 0)
548 content = convert_line_endings(request.POST.get('content', ''), mode)
548 content = convert_line_endings(request.POST.get('content', ''), mode)
549
549
550 message = request.POST.get('message') or c.default_message
550 message = request.POST.get('message') or c.default_message
551 org_f_path = c.file.unicode_path
551 org_f_path = c.file.unicode_path
552 filename = request.POST['filename']
552 filename = request.POST['filename']
553 org_filename = c.file.name
553 org_filename = c.file.name
554
554
555 if content == old_content and filename == org_filename:
555 if content == old_content and filename == org_filename:
556 h.flash(_('No changes'), category='warning')
556 h.flash(_('No changes'), category='warning')
557 return redirect(url('changeset_home', repo_name=c.repo_name,
557 return redirect(url('changeset_home', repo_name=c.repo_name,
558 revision='tip'))
558 revision='tip'))
559 try:
559 try:
560 mapping = {
560 mapping = {
561 org_f_path: {
561 org_f_path: {
562 'org_filename': org_f_path,
562 'org_filename': org_f_path,
563 'filename': os.path.join(c.file.dir_path, filename),
563 'filename': os.path.join(c.file.dir_path, filename),
564 'content': content,
564 'content': content,
565 'lexer': '',
565 'lexer': '',
566 'op': 'mod',
566 'op': 'mod',
567 }
567 }
568 }
568 }
569
569
570 ScmModel().update_nodes(
570 ScmModel().update_nodes(
571 user=c.rhodecode_user.user_id,
571 user=c.rhodecode_user.user_id,
572 repo=c.rhodecode_db_repo,
572 repo=c.rhodecode_db_repo,
573 message=message,
573 message=message,
574 nodes=mapping,
574 nodes=mapping,
575 parent_commit=c.commit,
575 parent_commit=c.commit,
576 )
576 )
577
577
578 h.flash(_('Successfully committed to %s') % f_path,
578 h.flash(_('Successfully committed to %s') % f_path,
579 category='success')
579 category='success')
580 except Exception:
580 except Exception:
581 msg = _('Error occurred during commit')
581 msg = _('Error occurred during commit')
582 log.exception(msg)
582 log.exception(msg)
583 h.flash(msg, category='error')
583 h.flash(msg, category='error')
584 return redirect(url('changeset_home',
584 return redirect(url('changeset_home',
585 repo_name=c.repo_name, revision='tip'))
585 repo_name=c.repo_name, revision='tip'))
586
586
587 @LoginRequired()
587 @LoginRequired()
588 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
588 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
589 def edit_home(self, repo_name, revision, f_path):
589 def edit_home(self, repo_name, revision, f_path):
590 commit_id = revision
590 commit_id = revision
591
591
592 repo = c.rhodecode_db_repo
592 repo = c.rhodecode_db_repo
593 if repo.enable_locking and repo.locked[0]:
593 if repo.enable_locking and repo.locked[0]:
594 h.flash(_('This repository has been locked by %s on %s')
594 h.flash(_('This repository has been locked by %s on %s')
595 % (h.person_by_id(repo.locked[0]),
595 % (h.person_by_id(repo.locked[0]),
596 h.format_date(h.time_to_datetime(repo.locked[1]))),
596 h.format_date(h.time_to_datetime(repo.locked[1]))),
597 'warning')
597 'warning')
598 return redirect(h.url('files_home',
598 return redirect(h.url('files_home',
599 repo_name=repo_name, revision='tip'))
599 repo_name=repo_name, revision='tip'))
600
600
601 if not self._is_valid_head(commit_id, repo.scm_instance()):
601 if not self._is_valid_head(commit_id, repo.scm_instance()):
602 h.flash(_('You can only edit files with revision '
602 h.flash(_('You can only edit files with revision '
603 'being a valid branch '), category='warning')
603 'being a valid branch '), category='warning')
604 return redirect(h.url('files_home',
604 return redirect(h.url('files_home',
605 repo_name=repo_name, revision='tip',
605 repo_name=repo_name, revision='tip',
606 f_path=f_path))
606 f_path=f_path))
607
607
608 c.commit = self.__get_commit_or_redirect(commit_id, repo_name)
608 c.commit = self.__get_commit_or_redirect(commit_id, repo_name)
609 c.file = self.__get_filenode_or_redirect(repo_name, c.commit, f_path)
609 c.file = self.__get_filenode_or_redirect(repo_name, c.commit, f_path)
610
610
611 if c.file.is_binary:
611 if c.file.is_binary:
612 return redirect(url('files_home', repo_name=c.repo_name,
612 return redirect(url('files_home', repo_name=c.repo_name,
613 revision=c.commit.raw_id, f_path=f_path))
613 revision=c.commit.raw_id, f_path=f_path))
614 c.default_message = _(
614 c.default_message = _(
615 'Edited file %s via RhodeCode Enterprise') % (f_path)
615 'Edited file %s via RhodeCode Enterprise') % (f_path)
616 c.f_path = f_path
616 c.f_path = f_path
617
617
618 return render('files/files_edit.mako')
618 return render('files/files_edit.mako')
619
619
620 def _is_valid_head(self, commit_id, repo):
620 def _is_valid_head(self, commit_id, repo):
621 # check if commit is a branch identifier- basically we cannot
621 # check if commit is a branch identifier- basically we cannot
622 # create multiple heads via file editing
622 # create multiple heads via file editing
623 valid_heads = repo.branches.keys() + repo.branches.values()
623 valid_heads = repo.branches.keys() + repo.branches.values()
624
624
625 if h.is_svn(repo) and not repo.is_empty():
625 if h.is_svn(repo) and not repo.is_empty():
626 # Note: Subversion only has one head, we add it here in case there
626 # Note: Subversion only has one head, we add it here in case there
627 # is no branch matched.
627 # is no branch matched.
628 valid_heads.append(repo.get_commit(commit_idx=-1).raw_id)
628 valid_heads.append(repo.get_commit(commit_idx=-1).raw_id)
629
629
630 # check if commit is a branch name or branch hash
630 # check if commit is a branch name or branch hash
631 return commit_id in valid_heads
631 return commit_id in valid_heads
632
632
633 @CSRFRequired()
633 @CSRFRequired()
634 @LoginRequired()
634 @LoginRequired()
635 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
635 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
636 def add(self, repo_name, revision, f_path):
636 def add(self, repo_name, revision, f_path):
637 repo = Repository.get_by_repo_name(repo_name)
637 repo = Repository.get_by_repo_name(repo_name)
638 if repo.enable_locking and repo.locked[0]:
638 if repo.enable_locking and repo.locked[0]:
639 h.flash(_('This repository has been locked by %s on %s')
639 h.flash(_('This repository has been locked by %s on %s')
640 % (h.person_by_id(repo.locked[0]),
640 % (h.person_by_id(repo.locked[0]),
641 h.format_date(h.time_to_datetime(repo.locked[1]))),
641 h.format_date(h.time_to_datetime(repo.locked[1]))),
642 'warning')
642 'warning')
643 return redirect(h.url('files_home',
643 return redirect(h.url('files_home',
644 repo_name=repo_name, revision='tip'))
644 repo_name=repo_name, revision='tip'))
645
645
646 r_post = request.POST
646 r_post = request.POST
647
647
648 c.commit = self.__get_commit_or_redirect(
648 c.commit = self.__get_commit_or_redirect(
649 revision, repo_name, redirect_after=False)
649 revision, repo_name, redirect_after=False)
650 if c.commit is None:
650 if c.commit is None:
651 c.commit = EmptyCommit(alias=c.rhodecode_repo.alias)
651 c.commit = EmptyCommit(alias=c.rhodecode_repo.alias)
652 c.default_message = (_('Added file via RhodeCode Enterprise'))
652 c.default_message = (_('Added file via RhodeCode Enterprise'))
653 c.f_path = f_path
653 c.f_path = f_path
654 unix_mode = 0
654 unix_mode = 0
655 content = convert_line_endings(r_post.get('content', ''), unix_mode)
655 content = convert_line_endings(r_post.get('content', ''), unix_mode)
656
656
657 message = r_post.get('message') or c.default_message
657 message = r_post.get('message') or c.default_message
658 filename = r_post.get('filename')
658 filename = r_post.get('filename')
659 location = r_post.get('location', '') # dir location
659 location = r_post.get('location', '') # dir location
660 file_obj = r_post.get('upload_file', None)
660 file_obj = r_post.get('upload_file', None)
661
661
662 if file_obj is not None and hasattr(file_obj, 'filename'):
662 if file_obj is not None and hasattr(file_obj, 'filename'):
663 filename = file_obj.filename
664 content = file_obj.file
663 content = file_obj.file
665
664
666 if hasattr(content, 'file'):
665 if hasattr(content, 'file'):
667 # non posix systems store real file under file attr
666 # non posix systems store real file under file attr
668 content = content.file
667 content = content.file
669
668
670 # If there's no commit, redirect to repo summary
669 # If there's no commit, redirect to repo summary
671 if type(c.commit) is EmptyCommit:
670 if type(c.commit) is EmptyCommit:
672 redirect_url = "summary_home"
671 redirect_url = "summary_home"
673 else:
672 else:
674 redirect_url = "changeset_home"
673 redirect_url = "changeset_home"
675
674
676 if not filename:
675 if not filename:
677 h.flash(_('No filename'), category='warning')
676 h.flash(_('No filename'), category='warning')
678 return redirect(url(redirect_url, repo_name=c.repo_name,
677 return redirect(url(redirect_url, repo_name=c.repo_name,
679 revision='tip'))
678 revision='tip'))
680
679
681 # extract the location from filename,
680 # extract the location from filename,
682 # allows using foo/bar.txt syntax to create subdirectories
681 # allows using foo/bar.txt syntax to create subdirectories
683 subdir_loc = filename.rsplit('/', 1)
682 subdir_loc = filename.rsplit('/', 1)
684 if len(subdir_loc) == 2:
683 if len(subdir_loc) == 2:
685 location = os.path.join(location, subdir_loc[0])
684 location = os.path.join(location, subdir_loc[0])
686
685
687 # strip all crap out of file, just leave the basename
686 # strip all crap out of file, just leave the basename
688 filename = os.path.basename(filename)
687 filename = os.path.basename(filename)
689 node_path = os.path.join(location, filename)
688 node_path = os.path.join(location, filename)
690 author = c.rhodecode_user.full_contact
689 author = c.rhodecode_user.full_contact
691
690
692 try:
691 try:
693 nodes = {
692 nodes = {
694 node_path: {
693 node_path: {
695 'content': content
694 'content': content
696 }
695 }
697 }
696 }
698 self.scm_model.create_nodes(
697 self.scm_model.create_nodes(
699 user=c.rhodecode_user.user_id,
698 user=c.rhodecode_user.user_id,
700 repo=c.rhodecode_db_repo,
699 repo=c.rhodecode_db_repo,
701 message=message,
700 message=message,
702 nodes=nodes,
701 nodes=nodes,
703 parent_commit=c.commit,
702 parent_commit=c.commit,
704 author=author,
703 author=author,
705 )
704 )
706
705
707 h.flash(_('Successfully committed to %s') % node_path,
706 h.flash(_('Successfully committed to %s') % node_path,
708 category='success')
707 category='success')
709 except NonRelativePathError as e:
708 except NonRelativePathError as e:
710 h.flash(_(
709 h.flash(_(
711 'The location specified must be a relative path and must not '
710 'The location specified must be a relative path and must not '
712 'contain .. in the path'), category='warning')
711 'contain .. in the path'), category='warning')
713 return redirect(url('changeset_home', repo_name=c.repo_name,
712 return redirect(url('changeset_home', repo_name=c.repo_name,
714 revision='tip'))
713 revision='tip'))
715 except (NodeError, NodeAlreadyExistsError) as e:
714 except (NodeError, NodeAlreadyExistsError) as e:
716 h.flash(_(e), category='error')
715 h.flash(_(e), category='error')
717 except Exception:
716 except Exception:
718 msg = _('Error occurred during commit')
717 msg = _('Error occurred during commit')
719 log.exception(msg)
718 log.exception(msg)
720 h.flash(msg, category='error')
719 h.flash(msg, category='error')
721 return redirect(url('changeset_home',
720 return redirect(url('changeset_home',
722 repo_name=c.repo_name, revision='tip'))
721 repo_name=c.repo_name, revision='tip'))
723
722
724 @LoginRequired()
723 @LoginRequired()
725 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
724 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
726 def add_home(self, repo_name, revision, f_path):
725 def add_home(self, repo_name, revision, f_path):
727
726
728 repo = Repository.get_by_repo_name(repo_name)
727 repo = Repository.get_by_repo_name(repo_name)
729 if repo.enable_locking and repo.locked[0]:
728 if repo.enable_locking and repo.locked[0]:
730 h.flash(_('This repository has been locked by %s on %s')
729 h.flash(_('This repository has been locked by %s on %s')
731 % (h.person_by_id(repo.locked[0]),
730 % (h.person_by_id(repo.locked[0]),
732 h.format_date(h.time_to_datetime(repo.locked[1]))),
731 h.format_date(h.time_to_datetime(repo.locked[1]))),
733 'warning')
732 'warning')
734 return redirect(h.url('files_home',
733 return redirect(h.url('files_home',
735 repo_name=repo_name, revision='tip'))
734 repo_name=repo_name, revision='tip'))
736
735
737 c.commit = self.__get_commit_or_redirect(
736 c.commit = self.__get_commit_or_redirect(
738 revision, repo_name, redirect_after=False)
737 revision, repo_name, redirect_after=False)
739 if c.commit is None:
738 if c.commit is None:
740 c.commit = EmptyCommit(alias=c.rhodecode_repo.alias)
739 c.commit = EmptyCommit(alias=c.rhodecode_repo.alias)
741 c.default_message = (_('Added file via RhodeCode Enterprise'))
740 c.default_message = (_('Added file via RhodeCode Enterprise'))
742 c.f_path = f_path
741 c.f_path = f_path
743
742
744 return render('files/files_add.mako')
743 return render('files/files_add.mako')
745
744
746 @LoginRequired()
745 @LoginRequired()
747 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
746 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
748 'repository.admin')
747 'repository.admin')
749 def archivefile(self, repo_name, fname):
748 def archivefile(self, repo_name, fname):
750 fileformat = None
749 fileformat = None
751 commit_id = None
750 commit_id = None
752 ext = None
751 ext = None
753 subrepos = request.GET.get('subrepos') == 'true'
752 subrepos = request.GET.get('subrepos') == 'true'
754
753
755 for a_type, ext_data in settings.ARCHIVE_SPECS.items():
754 for a_type, ext_data in settings.ARCHIVE_SPECS.items():
756 archive_spec = fname.split(ext_data[1])
755 archive_spec = fname.split(ext_data[1])
757 if len(archive_spec) == 2 and archive_spec[1] == '':
756 if len(archive_spec) == 2 and archive_spec[1] == '':
758 fileformat = a_type or ext_data[1]
757 fileformat = a_type or ext_data[1]
759 commit_id = archive_spec[0]
758 commit_id = archive_spec[0]
760 ext = ext_data[1]
759 ext = ext_data[1]
761
760
762 dbrepo = RepoModel().get_by_repo_name(repo_name)
761 dbrepo = RepoModel().get_by_repo_name(repo_name)
763 if not dbrepo.enable_downloads:
762 if not dbrepo.enable_downloads:
764 return _('Downloads disabled')
763 return _('Downloads disabled')
765
764
766 try:
765 try:
767 commit = c.rhodecode_repo.get_commit(commit_id)
766 commit = c.rhodecode_repo.get_commit(commit_id)
768 content_type = settings.ARCHIVE_SPECS[fileformat][0]
767 content_type = settings.ARCHIVE_SPECS[fileformat][0]
769 except CommitDoesNotExistError:
768 except CommitDoesNotExistError:
770 return _('Unknown revision %s') % commit_id
769 return _('Unknown revision %s') % commit_id
771 except EmptyRepositoryError:
770 except EmptyRepositoryError:
772 return _('Empty repository')
771 return _('Empty repository')
773 except KeyError:
772 except KeyError:
774 return _('Unknown archive type')
773 return _('Unknown archive type')
775
774
776 # archive cache
775 # archive cache
777 from rhodecode import CONFIG
776 from rhodecode import CONFIG
778
777
779 archive_name = '%s-%s%s%s' % (
778 archive_name = '%s-%s%s%s' % (
780 safe_str(repo_name.replace('/', '_')),
779 safe_str(repo_name.replace('/', '_')),
781 '-sub' if subrepos else '',
780 '-sub' if subrepos else '',
782 safe_str(commit.short_id), ext)
781 safe_str(commit.short_id), ext)
783
782
784 use_cached_archive = False
783 use_cached_archive = False
785 archive_cache_enabled = CONFIG.get(
784 archive_cache_enabled = CONFIG.get(
786 'archive_cache_dir') and not request.GET.get('no_cache')
785 'archive_cache_dir') and not request.GET.get('no_cache')
787
786
788 if archive_cache_enabled:
787 if archive_cache_enabled:
789 # check if we it's ok to write
788 # check if we it's ok to write
790 if not os.path.isdir(CONFIG['archive_cache_dir']):
789 if not os.path.isdir(CONFIG['archive_cache_dir']):
791 os.makedirs(CONFIG['archive_cache_dir'])
790 os.makedirs(CONFIG['archive_cache_dir'])
792 cached_archive_path = os.path.join(
791 cached_archive_path = os.path.join(
793 CONFIG['archive_cache_dir'], archive_name)
792 CONFIG['archive_cache_dir'], archive_name)
794 if os.path.isfile(cached_archive_path):
793 if os.path.isfile(cached_archive_path):
795 log.debug('Found cached archive in %s', cached_archive_path)
794 log.debug('Found cached archive in %s', cached_archive_path)
796 fd, archive = None, cached_archive_path
795 fd, archive = None, cached_archive_path
797 use_cached_archive = True
796 use_cached_archive = True
798 else:
797 else:
799 log.debug('Archive %s is not yet cached', archive_name)
798 log.debug('Archive %s is not yet cached', archive_name)
800
799
801 if not use_cached_archive:
800 if not use_cached_archive:
802 # generate new archive
801 # generate new archive
803 fd, archive = tempfile.mkstemp()
802 fd, archive = tempfile.mkstemp()
804 log.debug('Creating new temp archive in %s' % (archive,))
803 log.debug('Creating new temp archive in %s' % (archive,))
805 try:
804 try:
806 commit.archive_repo(archive, kind=fileformat, subrepos=subrepos)
805 commit.archive_repo(archive, kind=fileformat, subrepos=subrepos)
807 except ImproperArchiveTypeError:
806 except ImproperArchiveTypeError:
808 return _('Unknown archive type')
807 return _('Unknown archive type')
809 if archive_cache_enabled:
808 if archive_cache_enabled:
810 # if we generated the archive and we have cache enabled
809 # if we generated the archive and we have cache enabled
811 # let's use this for future
810 # let's use this for future
812 log.debug('Storing new archive in %s' % (cached_archive_path,))
811 log.debug('Storing new archive in %s' % (cached_archive_path,))
813 shutil.move(archive, cached_archive_path)
812 shutil.move(archive, cached_archive_path)
814 archive = cached_archive_path
813 archive = cached_archive_path
815
814
816 def get_chunked_archive(archive):
815 def get_chunked_archive(archive):
817 with open(archive, 'rb') as stream:
816 with open(archive, 'rb') as stream:
818 while True:
817 while True:
819 data = stream.read(16 * 1024)
818 data = stream.read(16 * 1024)
820 if not data:
819 if not data:
821 if fd: # fd means we used temporary file
820 if fd: # fd means we used temporary file
822 os.close(fd)
821 os.close(fd)
823 if not archive_cache_enabled:
822 if not archive_cache_enabled:
824 log.debug('Destroying temp archive %s', archive)
823 log.debug('Destroying temp archive %s', archive)
825 os.remove(archive)
824 os.remove(archive)
826 break
825 break
827 yield data
826 yield data
828
827
829 # store download action
828 # store download action
830 action_logger(user=c.rhodecode_user,
829 action_logger(user=c.rhodecode_user,
831 action='user_downloaded_archive:%s' % archive_name,
830 action='user_downloaded_archive:%s' % archive_name,
832 repo=repo_name, ipaddr=self.ip_addr, commit=True)
831 repo=repo_name, ipaddr=self.ip_addr, commit=True)
833 response.content_disposition = str(
832 response.content_disposition = str(
834 'attachment; filename=%s' % archive_name)
833 'attachment; filename=%s' % archive_name)
835 response.content_type = str(content_type)
834 response.content_type = str(content_type)
836
835
837 return get_chunked_archive(archive)
836 return get_chunked_archive(archive)
838
837
839 @LoginRequired()
838 @LoginRequired()
840 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
839 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
841 'repository.admin')
840 'repository.admin')
842 def diff(self, repo_name, f_path):
841 def diff(self, repo_name, f_path):
843
842
844 c.action = request.GET.get('diff')
843 c.action = request.GET.get('diff')
845 diff1 = request.GET.get('diff1', '')
844 diff1 = request.GET.get('diff1', '')
846 diff2 = request.GET.get('diff2', '')
845 diff2 = request.GET.get('diff2', '')
847
846
848 path1, diff1 = parse_path_ref(diff1, default_path=f_path)
847 path1, diff1 = parse_path_ref(diff1, default_path=f_path)
849
848
850 ignore_whitespace = str2bool(request.GET.get('ignorews'))
849 ignore_whitespace = str2bool(request.GET.get('ignorews'))
851 line_context = request.GET.get('context', 3)
850 line_context = request.GET.get('context', 3)
852
851
853 if not any((diff1, diff2)):
852 if not any((diff1, diff2)):
854 h.flash(
853 h.flash(
855 'Need query parameter "diff1" or "diff2" to generate a diff.',
854 'Need query parameter "diff1" or "diff2" to generate a diff.',
856 category='error')
855 category='error')
857 raise HTTPBadRequest()
856 raise HTTPBadRequest()
858
857
859 if c.action not in ['download', 'raw']:
858 if c.action not in ['download', 'raw']:
860 # redirect to new view if we render diff
859 # redirect to new view if we render diff
861 return redirect(
860 return redirect(
862 url('compare_url', repo_name=repo_name,
861 url('compare_url', repo_name=repo_name,
863 source_ref_type='rev',
862 source_ref_type='rev',
864 source_ref=diff1,
863 source_ref=diff1,
865 target_repo=c.repo_name,
864 target_repo=c.repo_name,
866 target_ref_type='rev',
865 target_ref_type='rev',
867 target_ref=diff2,
866 target_ref=diff2,
868 f_path=f_path))
867 f_path=f_path))
869
868
870 try:
869 try:
871 node1 = self._get_file_node(diff1, path1)
870 node1 = self._get_file_node(diff1, path1)
872 node2 = self._get_file_node(diff2, f_path)
871 node2 = self._get_file_node(diff2, f_path)
873 except (RepositoryError, NodeError):
872 except (RepositoryError, NodeError):
874 log.exception("Exception while trying to get node from repository")
873 log.exception("Exception while trying to get node from repository")
875 return redirect(url(
874 return redirect(url(
876 'files_home', repo_name=c.repo_name, f_path=f_path))
875 'files_home', repo_name=c.repo_name, f_path=f_path))
877
876
878 if all(isinstance(node.commit, EmptyCommit)
877 if all(isinstance(node.commit, EmptyCommit)
879 for node in (node1, node2)):
878 for node in (node1, node2)):
880 raise HTTPNotFound
879 raise HTTPNotFound
881
880
882 c.commit_1 = node1.commit
881 c.commit_1 = node1.commit
883 c.commit_2 = node2.commit
882 c.commit_2 = node2.commit
884
883
885 if c.action == 'download':
884 if c.action == 'download':
886 _diff = diffs.get_gitdiff(node1, node2,
885 _diff = diffs.get_gitdiff(node1, node2,
887 ignore_whitespace=ignore_whitespace,
886 ignore_whitespace=ignore_whitespace,
888 context=line_context)
887 context=line_context)
889 diff = diffs.DiffProcessor(_diff, format='gitdiff')
888 diff = diffs.DiffProcessor(_diff, format='gitdiff')
890
889
891 diff_name = '%s_vs_%s.diff' % (diff1, diff2)
890 diff_name = '%s_vs_%s.diff' % (diff1, diff2)
892 response.content_type = 'text/plain'
891 response.content_type = 'text/plain'
893 response.content_disposition = (
892 response.content_disposition = (
894 'attachment; filename=%s' % (diff_name,)
893 'attachment; filename=%s' % (diff_name,)
895 )
894 )
896 charset = self._get_default_encoding()
895 charset = self._get_default_encoding()
897 if charset:
896 if charset:
898 response.charset = charset
897 response.charset = charset
899 return diff.as_raw()
898 return diff.as_raw()
900
899
901 elif c.action == 'raw':
900 elif c.action == 'raw':
902 _diff = diffs.get_gitdiff(node1, node2,
901 _diff = diffs.get_gitdiff(node1, node2,
903 ignore_whitespace=ignore_whitespace,
902 ignore_whitespace=ignore_whitespace,
904 context=line_context)
903 context=line_context)
905 diff = diffs.DiffProcessor(_diff, format='gitdiff')
904 diff = diffs.DiffProcessor(_diff, format='gitdiff')
906 response.content_type = 'text/plain'
905 response.content_type = 'text/plain'
907 charset = self._get_default_encoding()
906 charset = self._get_default_encoding()
908 if charset:
907 if charset:
909 response.charset = charset
908 response.charset = charset
910 return diff.as_raw()
909 return diff.as_raw()
911
910
912 else:
911 else:
913 return redirect(
912 return redirect(
914 url('compare_url', repo_name=repo_name,
913 url('compare_url', repo_name=repo_name,
915 source_ref_type='rev',
914 source_ref_type='rev',
916 source_ref=diff1,
915 source_ref=diff1,
917 target_repo=c.repo_name,
916 target_repo=c.repo_name,
918 target_ref_type='rev',
917 target_ref_type='rev',
919 target_ref=diff2,
918 target_ref=diff2,
920 f_path=f_path))
919 f_path=f_path))
921
920
922 @LoginRequired()
921 @LoginRequired()
923 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
922 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
924 'repository.admin')
923 'repository.admin')
925 def diff_2way(self, repo_name, f_path):
924 def diff_2way(self, repo_name, f_path):
926 """
925 """
927 Kept only to make OLD links work
926 Kept only to make OLD links work
928 """
927 """
929 diff1 = request.GET.get('diff1', '')
928 diff1 = request.GET.get('diff1', '')
930 diff2 = request.GET.get('diff2', '')
929 diff2 = request.GET.get('diff2', '')
931
930
932 if not any((diff1, diff2)):
931 if not any((diff1, diff2)):
933 h.flash(
932 h.flash(
934 'Need query parameter "diff1" or "diff2" to generate a diff.',
933 'Need query parameter "diff1" or "diff2" to generate a diff.',
935 category='error')
934 category='error')
936 raise HTTPBadRequest()
935 raise HTTPBadRequest()
937
936
938 return redirect(
937 return redirect(
939 url('compare_url', repo_name=repo_name,
938 url('compare_url', repo_name=repo_name,
940 source_ref_type='rev',
939 source_ref_type='rev',
941 source_ref=diff1,
940 source_ref=diff1,
942 target_repo=c.repo_name,
941 target_repo=c.repo_name,
943 target_ref_type='rev',
942 target_ref_type='rev',
944 target_ref=diff2,
943 target_ref=diff2,
945 f_path=f_path,
944 f_path=f_path,
946 diffmode='sideside'))
945 diffmode='sideside'))
947
946
948 def _get_file_node(self, commit_id, f_path):
947 def _get_file_node(self, commit_id, f_path):
949 if commit_id not in ['', None, 'None', '0' * 12, '0' * 40]:
948 if commit_id not in ['', None, 'None', '0' * 12, '0' * 40]:
950 commit = c.rhodecode_repo.get_commit(commit_id=commit_id)
949 commit = c.rhodecode_repo.get_commit(commit_id=commit_id)
951 try:
950 try:
952 node = commit.get_node(f_path)
951 node = commit.get_node(f_path)
953 if node.is_dir():
952 if node.is_dir():
954 raise NodeError('%s path is a %s not a file'
953 raise NodeError('%s path is a %s not a file'
955 % (node, type(node)))
954 % (node, type(node)))
956 except NodeDoesNotExistError:
955 except NodeDoesNotExistError:
957 commit = EmptyCommit(
956 commit = EmptyCommit(
958 commit_id=commit_id,
957 commit_id=commit_id,
959 idx=commit.idx,
958 idx=commit.idx,
960 repo=commit.repository,
959 repo=commit.repository,
961 alias=commit.repository.alias,
960 alias=commit.repository.alias,
962 message=commit.message,
961 message=commit.message,
963 author=commit.author,
962 author=commit.author,
964 date=commit.date)
963 date=commit.date)
965 node = FileNode(f_path, '', commit=commit)
964 node = FileNode(f_path, '', commit=commit)
966 else:
965 else:
967 commit = EmptyCommit(
966 commit = EmptyCommit(
968 repo=c.rhodecode_repo,
967 repo=c.rhodecode_repo,
969 alias=c.rhodecode_repo.alias)
968 alias=c.rhodecode_repo.alias)
970 node = FileNode(f_path, '', commit=commit)
969 node = FileNode(f_path, '', commit=commit)
971 return node
970 return node
972
971
973 def _get_node_history(self, commit, f_path, commits=None):
972 def _get_node_history(self, commit, f_path, commits=None):
974 """
973 """
975 get commit history for given node
974 get commit history for given node
976
975
977 :param commit: commit to calculate history
976 :param commit: commit to calculate history
978 :param f_path: path for node to calculate history for
977 :param f_path: path for node to calculate history for
979 :param commits: if passed don't calculate history and take
978 :param commits: if passed don't calculate history and take
980 commits defined in this list
979 commits defined in this list
981 """
980 """
982 # calculate history based on tip
981 # calculate history based on tip
983 tip = c.rhodecode_repo.get_commit()
982 tip = c.rhodecode_repo.get_commit()
984 if commits is None:
983 if commits is None:
985 pre_load = ["author", "branch"]
984 pre_load = ["author", "branch"]
986 try:
985 try:
987 commits = tip.get_file_history(f_path, pre_load=pre_load)
986 commits = tip.get_file_history(f_path, pre_load=pre_load)
988 except (NodeDoesNotExistError, CommitError):
987 except (NodeDoesNotExistError, CommitError):
989 # this node is not present at tip!
988 # this node is not present at tip!
990 commits = commit.get_file_history(f_path, pre_load=pre_load)
989 commits = commit.get_file_history(f_path, pre_load=pre_load)
991
990
992 history = []
991 history = []
993 commits_group = ([], _("Changesets"))
992 commits_group = ([], _("Changesets"))
994 for commit in commits:
993 for commit in commits:
995 branch = ' (%s)' % commit.branch if commit.branch else ''
994 branch = ' (%s)' % commit.branch if commit.branch else ''
996 n_desc = 'r%s:%s%s' % (commit.idx, commit.short_id, branch)
995 n_desc = 'r%s:%s%s' % (commit.idx, commit.short_id, branch)
997 commits_group[0].append((commit.raw_id, n_desc,))
996 commits_group[0].append((commit.raw_id, n_desc,))
998 history.append(commits_group)
997 history.append(commits_group)
999
998
1000 symbolic_reference = self._symbolic_reference
999 symbolic_reference = self._symbolic_reference
1001
1000
1002 if c.rhodecode_repo.alias == 'svn':
1001 if c.rhodecode_repo.alias == 'svn':
1003 adjusted_f_path = self._adjust_file_path_for_svn(
1002 adjusted_f_path = self._adjust_file_path_for_svn(
1004 f_path, c.rhodecode_repo)
1003 f_path, c.rhodecode_repo)
1005 if adjusted_f_path != f_path:
1004 if adjusted_f_path != f_path:
1006 log.debug(
1005 log.debug(
1007 'Recognized svn tag or branch in file "%s", using svn '
1006 'Recognized svn tag or branch in file "%s", using svn '
1008 'specific symbolic references', f_path)
1007 'specific symbolic references', f_path)
1009 f_path = adjusted_f_path
1008 f_path = adjusted_f_path
1010 symbolic_reference = self._symbolic_reference_svn
1009 symbolic_reference = self._symbolic_reference_svn
1011
1010
1012 branches = self._create_references(
1011 branches = self._create_references(
1013 c.rhodecode_repo.branches, symbolic_reference, f_path)
1012 c.rhodecode_repo.branches, symbolic_reference, f_path)
1014 branches_group = (branches, _("Branches"))
1013 branches_group = (branches, _("Branches"))
1015
1014
1016 tags = self._create_references(
1015 tags = self._create_references(
1017 c.rhodecode_repo.tags, symbolic_reference, f_path)
1016 c.rhodecode_repo.tags, symbolic_reference, f_path)
1018 tags_group = (tags, _("Tags"))
1017 tags_group = (tags, _("Tags"))
1019
1018
1020 history.append(branches_group)
1019 history.append(branches_group)
1021 history.append(tags_group)
1020 history.append(tags_group)
1022
1021
1023 return history, commits
1022 return history, commits
1024
1023
1025 def _adjust_file_path_for_svn(self, f_path, repo):
1024 def _adjust_file_path_for_svn(self, f_path, repo):
1026 """
1025 """
1027 Computes the relative path of `f_path`.
1026 Computes the relative path of `f_path`.
1028
1027
1029 This is mainly based on prefix matching of the recognized tags and
1028 This is mainly based on prefix matching of the recognized tags and
1030 branches in the underlying repository.
1029 branches in the underlying repository.
1031 """
1030 """
1032 tags_and_branches = itertools.chain(
1031 tags_and_branches = itertools.chain(
1033 repo.branches.iterkeys(),
1032 repo.branches.iterkeys(),
1034 repo.tags.iterkeys())
1033 repo.tags.iterkeys())
1035 tags_and_branches = sorted(tags_and_branches, key=len, reverse=True)
1034 tags_and_branches = sorted(tags_and_branches, key=len, reverse=True)
1036
1035
1037 for name in tags_and_branches:
1036 for name in tags_and_branches:
1038 if f_path.startswith(name + '/'):
1037 if f_path.startswith(name + '/'):
1039 f_path = vcspath.relpath(f_path, name)
1038 f_path = vcspath.relpath(f_path, name)
1040 break
1039 break
1041 return f_path
1040 return f_path
1042
1041
1043 def _create_references(
1042 def _create_references(
1044 self, branches_or_tags, symbolic_reference, f_path):
1043 self, branches_or_tags, symbolic_reference, f_path):
1045 items = []
1044 items = []
1046 for name, commit_id in branches_or_tags.items():
1045 for name, commit_id in branches_or_tags.items():
1047 sym_ref = symbolic_reference(commit_id, name, f_path)
1046 sym_ref = symbolic_reference(commit_id, name, f_path)
1048 items.append((sym_ref, name))
1047 items.append((sym_ref, name))
1049 return items
1048 return items
1050
1049
1051 def _symbolic_reference(self, commit_id, name, f_path):
1050 def _symbolic_reference(self, commit_id, name, f_path):
1052 return commit_id
1051 return commit_id
1053
1052
1054 def _symbolic_reference_svn(self, commit_id, name, f_path):
1053 def _symbolic_reference_svn(self, commit_id, name, f_path):
1055 new_f_path = vcspath.join(name, f_path)
1054 new_f_path = vcspath.join(name, f_path)
1056 return u'%s@%s' % (new_f_path, commit_id)
1055 return u'%s@%s' % (new_f_path, commit_id)
1057
1056
1058 @LoginRequired()
1057 @LoginRequired()
1059 @XHRRequired()
1058 @XHRRequired()
1060 @HasRepoPermissionAnyDecorator(
1059 @HasRepoPermissionAnyDecorator(
1061 'repository.read', 'repository.write', 'repository.admin')
1060 'repository.read', 'repository.write', 'repository.admin')
1062 @jsonify
1061 @jsonify
1063 def nodelist(self, repo_name, revision, f_path):
1062 def nodelist(self, repo_name, revision, f_path):
1064 commit = self.__get_commit_or_redirect(revision, repo_name)
1063 commit = self.__get_commit_or_redirect(revision, repo_name)
1065
1064
1066 metadata = self._get_nodelist_at_commit(
1065 metadata = self._get_nodelist_at_commit(
1067 repo_name, commit.raw_id, f_path)
1066 repo_name, commit.raw_id, f_path)
1068 return {'nodes': metadata}
1067 return {'nodes': metadata}
1069
1068
1070 @LoginRequired()
1069 @LoginRequired()
1071 @XHRRequired()
1070 @XHRRequired()
1072 @HasRepoPermissionAnyDecorator(
1071 @HasRepoPermissionAnyDecorator(
1073 'repository.read', 'repository.write', 'repository.admin')
1072 'repository.read', 'repository.write', 'repository.admin')
1074 def nodetree_full(self, repo_name, commit_id, f_path):
1073 def nodetree_full(self, repo_name, commit_id, f_path):
1075 """
1074 """
1076 Returns rendered html of file tree that contains commit date,
1075 Returns rendered html of file tree that contains commit date,
1077 author, revision for the specified combination of
1076 author, revision for the specified combination of
1078 repo, commit_id and file path
1077 repo, commit_id and file path
1079
1078
1080 :param repo_name: name of the repository
1079 :param repo_name: name of the repository
1081 :param commit_id: commit_id of file tree
1080 :param commit_id: commit_id of file tree
1082 :param f_path: file path of the requested directory
1081 :param f_path: file path of the requested directory
1083 """
1082 """
1084
1083
1085 commit = self.__get_commit_or_redirect(commit_id, repo_name)
1084 commit = self.__get_commit_or_redirect(commit_id, repo_name)
1086 try:
1085 try:
1087 dir_node = commit.get_node(f_path)
1086 dir_node = commit.get_node(f_path)
1088 except RepositoryError as e:
1087 except RepositoryError as e:
1089 return 'error {}'.format(safe_str(e))
1088 return 'error {}'.format(safe_str(e))
1090
1089
1091 if dir_node.is_file():
1090 if dir_node.is_file():
1092 return ''
1091 return ''
1093
1092
1094 c.file = dir_node
1093 c.file = dir_node
1095 c.commit = commit
1094 c.commit = commit
1096
1095
1097 # using force=True here, make a little trick. We flush the cache and
1096 # using force=True here, make a little trick. We flush the cache and
1098 # compute it using the same key as without full_load, so the fully
1097 # compute it using the same key as without full_load, so the fully
1099 # loaded cached tree is now returned instead of partial
1098 # loaded cached tree is now returned instead of partial
1100 return self._get_tree_at_commit(
1099 return self._get_tree_at_commit(
1101 repo_name, commit.raw_id, dir_node.path, full_load=True,
1100 repo_name, commit.raw_id, dir_node.path, full_load=True,
1102 force=True)
1101 force=True)
@@ -1,2342 +1,2346 b''
1 //Primary CSS
1 //Primary CSS
2
2
3 //--- IMPORTS ------------------//
3 //--- IMPORTS ------------------//
4
4
5 @import 'helpers';
5 @import 'helpers';
6 @import 'mixins';
6 @import 'mixins';
7 @import 'rcicons';
7 @import 'rcicons';
8 @import 'fonts';
8 @import 'fonts';
9 @import 'variables';
9 @import 'variables';
10 @import 'bootstrap-variables';
10 @import 'bootstrap-variables';
11 @import 'form-bootstrap';
11 @import 'form-bootstrap';
12 @import 'codemirror';
12 @import 'codemirror';
13 @import 'legacy_code_styles';
13 @import 'legacy_code_styles';
14 @import 'progress-bar';
14 @import 'progress-bar';
15
15
16 @import 'type';
16 @import 'type';
17 @import 'alerts';
17 @import 'alerts';
18 @import 'buttons';
18 @import 'buttons';
19 @import 'tags';
19 @import 'tags';
20 @import 'code-block';
20 @import 'code-block';
21 @import 'examples';
21 @import 'examples';
22 @import 'login';
22 @import 'login';
23 @import 'main-content';
23 @import 'main-content';
24 @import 'select2';
24 @import 'select2';
25 @import 'comments';
25 @import 'comments';
26 @import 'panels-bootstrap';
26 @import 'panels-bootstrap';
27 @import 'panels';
27 @import 'panels';
28 @import 'deform';
28 @import 'deform';
29
29
30 //--- BASE ------------------//
30 //--- BASE ------------------//
31 .noscript-error {
31 .noscript-error {
32 top: 0;
32 top: 0;
33 left: 0;
33 left: 0;
34 width: 100%;
34 width: 100%;
35 z-index: 101;
35 z-index: 101;
36 text-align: center;
36 text-align: center;
37 font-family: @text-semibold;
37 font-family: @text-semibold;
38 font-size: 120%;
38 font-size: 120%;
39 color: white;
39 color: white;
40 background-color: @alert2;
40 background-color: @alert2;
41 padding: 5px 0 5px 0;
41 padding: 5px 0 5px 0;
42 }
42 }
43
43
44 html {
44 html {
45 display: table;
45 display: table;
46 height: 100%;
46 height: 100%;
47 width: 100%;
47 width: 100%;
48 }
48 }
49
49
50 body {
50 body {
51 display: table-cell;
51 display: table-cell;
52 width: 100%;
52 width: 100%;
53 }
53 }
54
54
55 //--- LAYOUT ------------------//
55 //--- LAYOUT ------------------//
56
56
57 .hidden{
57 .hidden{
58 display: none !important;
58 display: none !important;
59 }
59 }
60
60
61 .box{
61 .box{
62 float: left;
62 float: left;
63 width: 100%;
63 width: 100%;
64 }
64 }
65
65
66 .browser-header {
66 .browser-header {
67 clear: both;
67 clear: both;
68 }
68 }
69 .main {
69 .main {
70 clear: both;
70 clear: both;
71 padding:0 0 @pagepadding;
71 padding:0 0 @pagepadding;
72 height: auto;
72 height: auto;
73
73
74 &:after { //clearfix
74 &:after { //clearfix
75 content:"";
75 content:"";
76 clear:both;
76 clear:both;
77 width:100%;
77 width:100%;
78 display:block;
78 display:block;
79 }
79 }
80 }
80 }
81
81
82 .action-link{
82 .action-link{
83 margin-left: @padding;
83 margin-left: @padding;
84 padding-left: @padding;
84 padding-left: @padding;
85 border-left: @border-thickness solid @border-default-color;
85 border-left: @border-thickness solid @border-default-color;
86 }
86 }
87
87
88 input + .action-link, .action-link.first{
88 input + .action-link, .action-link.first{
89 border-left: none;
89 border-left: none;
90 }
90 }
91
91
92 .action-link.last{
92 .action-link.last{
93 margin-right: @padding;
93 margin-right: @padding;
94 padding-right: @padding;
94 padding-right: @padding;
95 }
95 }
96
96
97 .action-link.active,
97 .action-link.active,
98 .action-link.active a{
98 .action-link.active a{
99 color: @grey4;
99 color: @grey4;
100 }
100 }
101
101
102 ul.simple-list{
102 ul.simple-list{
103 list-style: none;
103 list-style: none;
104 margin: 0;
104 margin: 0;
105 padding: 0;
105 padding: 0;
106 }
106 }
107
107
108 .main-content {
108 .main-content {
109 padding-bottom: @pagepadding;
109 padding-bottom: @pagepadding;
110 }
110 }
111
111
112 .wide-mode-wrapper {
112 .wide-mode-wrapper {
113 max-width:4000px !important;
113 max-width:4000px !important;
114 }
114 }
115
115
116 .wrapper {
116 .wrapper {
117 position: relative;
117 position: relative;
118 max-width: @wrapper-maxwidth;
118 max-width: @wrapper-maxwidth;
119 margin: 0 auto;
119 margin: 0 auto;
120 }
120 }
121
121
122 #content {
122 #content {
123 clear: both;
123 clear: both;
124 padding: 0 @contentpadding;
124 padding: 0 @contentpadding;
125 }
125 }
126
126
127 .advanced-settings-fields{
127 .advanced-settings-fields{
128 input{
128 input{
129 margin-left: @textmargin;
129 margin-left: @textmargin;
130 margin-right: @padding/2;
130 margin-right: @padding/2;
131 }
131 }
132 }
132 }
133
133
134 .cs_files_title {
134 .cs_files_title {
135 margin: @pagepadding 0 0;
135 margin: @pagepadding 0 0;
136 }
136 }
137
137
138 input.inline[type="file"] {
138 input.inline[type="file"] {
139 display: inline;
139 display: inline;
140 }
140 }
141
141
142 .error_page {
142 .error_page {
143 margin: 10% auto;
143 margin: 10% auto;
144
144
145 h1 {
145 h1 {
146 color: @grey2;
146 color: @grey2;
147 }
147 }
148
148
149 .alert {
149 .alert {
150 margin: @padding 0;
150 margin: @padding 0;
151 }
151 }
152
152
153 .error-branding {
153 .error-branding {
154 font-family: @text-semibold;
154 font-family: @text-semibold;
155 color: @grey4;
155 color: @grey4;
156 }
156 }
157
157
158 .error_message {
158 .error_message {
159 font-family: @text-regular;
159 font-family: @text-regular;
160 }
160 }
161
161
162 .sidebar {
162 .sidebar {
163 min-height: 275px;
163 min-height: 275px;
164 margin: 0;
164 margin: 0;
165 padding: 0 0 @sidebarpadding @sidebarpadding;
165 padding: 0 0 @sidebarpadding @sidebarpadding;
166 border: none;
166 border: none;
167 }
167 }
168
168
169 .main-content {
169 .main-content {
170 position: relative;
170 position: relative;
171 margin: 0 @sidebarpadding @sidebarpadding;
171 margin: 0 @sidebarpadding @sidebarpadding;
172 padding: 0 0 0 @sidebarpadding;
172 padding: 0 0 0 @sidebarpadding;
173 border-left: @border-thickness solid @grey5;
173 border-left: @border-thickness solid @grey5;
174
174
175 @media (max-width:767px) {
175 @media (max-width:767px) {
176 clear: both;
176 clear: both;
177 width: 100%;
177 width: 100%;
178 margin: 0;
178 margin: 0;
179 border: none;
179 border: none;
180 }
180 }
181 }
181 }
182
182
183 .inner-column {
183 .inner-column {
184 float: left;
184 float: left;
185 width: 29.75%;
185 width: 29.75%;
186 min-height: 150px;
186 min-height: 150px;
187 margin: @sidebarpadding 2% 0 0;
187 margin: @sidebarpadding 2% 0 0;
188 padding: 0 2% 0 0;
188 padding: 0 2% 0 0;
189 border-right: @border-thickness solid @grey5;
189 border-right: @border-thickness solid @grey5;
190
190
191 @media (max-width:767px) {
191 @media (max-width:767px) {
192 clear: both;
192 clear: both;
193 width: 100%;
193 width: 100%;
194 border: none;
194 border: none;
195 }
195 }
196
196
197 ul {
197 ul {
198 padding-left: 1.25em;
198 padding-left: 1.25em;
199 }
199 }
200
200
201 &:last-child {
201 &:last-child {
202 margin: @sidebarpadding 0 0;
202 margin: @sidebarpadding 0 0;
203 border: none;
203 border: none;
204 }
204 }
205
205
206 h4 {
206 h4 {
207 margin: 0 0 @padding;
207 margin: 0 0 @padding;
208 font-family: @text-semibold;
208 font-family: @text-semibold;
209 }
209 }
210 }
210 }
211 }
211 }
212 .error-page-logo {
212 .error-page-logo {
213 width: 130px;
213 width: 130px;
214 height: 160px;
214 height: 160px;
215 }
215 }
216
216
217 // HEADER
217 // HEADER
218 .header {
218 .header {
219
219
220 // TODO: johbo: Fix login pages, so that they work without a min-height
220 // TODO: johbo: Fix login pages, so that they work without a min-height
221 // for the header and then remove the min-height. I chose a smaller value
221 // for the header and then remove the min-height. I chose a smaller value
222 // intentionally here to avoid rendering issues in the main navigation.
222 // intentionally here to avoid rendering issues in the main navigation.
223 min-height: 49px;
223 min-height: 49px;
224
224
225 position: relative;
225 position: relative;
226 vertical-align: bottom;
226 vertical-align: bottom;
227 padding: 0 @header-padding;
227 padding: 0 @header-padding;
228 background-color: @grey2;
228 background-color: @grey2;
229 color: @grey5;
229 color: @grey5;
230
230
231 .title {
231 .title {
232 overflow: visible;
232 overflow: visible;
233 }
233 }
234
234
235 &:before,
235 &:before,
236 &:after {
236 &:after {
237 content: "";
237 content: "";
238 clear: both;
238 clear: both;
239 width: 100%;
239 width: 100%;
240 }
240 }
241
241
242 // TODO: johbo: Avoids breaking "Repositories" chooser
242 // TODO: johbo: Avoids breaking "Repositories" chooser
243 .select2-container .select2-choice .select2-arrow {
243 .select2-container .select2-choice .select2-arrow {
244 display: none;
244 display: none;
245 }
245 }
246 }
246 }
247
247
248 #header-inner {
248 #header-inner {
249 &.title {
249 &.title {
250 margin: 0;
250 margin: 0;
251 }
251 }
252 &:before,
252 &:before,
253 &:after {
253 &:after {
254 content: "";
254 content: "";
255 clear: both;
255 clear: both;
256 }
256 }
257 }
257 }
258
258
259 // Gists
259 // Gists
260 #files_data {
260 #files_data {
261 clear: both; //for firefox
261 clear: both; //for firefox
262 }
262 }
263 #gistid {
263 #gistid {
264 margin-right: @padding;
264 margin-right: @padding;
265 }
265 }
266
266
267 // Global Settings Editor
267 // Global Settings Editor
268 .textarea.editor {
268 .textarea.editor {
269 float: left;
269 float: left;
270 position: relative;
270 position: relative;
271 max-width: @texteditor-width;
271 max-width: @texteditor-width;
272
272
273 select {
273 select {
274 position: absolute;
274 position: absolute;
275 top:10px;
275 top:10px;
276 right:0;
276 right:0;
277 }
277 }
278
278
279 .CodeMirror {
279 .CodeMirror {
280 margin: 0;
280 margin: 0;
281 }
281 }
282
282
283 .help-block {
283 .help-block {
284 margin: 0 0 @padding;
284 margin: 0 0 @padding;
285 padding:.5em;
285 padding:.5em;
286 background-color: @grey6;
286 background-color: @grey6;
287 }
287 }
288 }
288 }
289
289
290 ul.auth_plugins {
290 ul.auth_plugins {
291 margin: @padding 0 @padding @legend-width;
291 margin: @padding 0 @padding @legend-width;
292 padding: 0;
292 padding: 0;
293
293
294 li {
294 li {
295 margin-bottom: @padding;
295 margin-bottom: @padding;
296 line-height: 1em;
296 line-height: 1em;
297 list-style-type: none;
297 list-style-type: none;
298
298
299 .auth_buttons .btn {
299 .auth_buttons .btn {
300 margin-right: @padding;
300 margin-right: @padding;
301 }
301 }
302
302
303 &:before { content: none; }
303 &:before { content: none; }
304 }
304 }
305 }
305 }
306
306
307
307
308 // My Account PR list
308 // My Account PR list
309
309
310 #show_closed {
310 #show_closed {
311 margin: 0 1em 0 0;
311 margin: 0 1em 0 0;
312 }
312 }
313
313
314 .pullrequestlist {
314 .pullrequestlist {
315 .closed {
315 .closed {
316 background-color: @grey6;
316 background-color: @grey6;
317 }
317 }
318 .td-status {
318 .td-status {
319 padding-left: .5em;
319 padding-left: .5em;
320 }
320 }
321 .log-container .truncate {
321 .log-container .truncate {
322 height: 2.75em;
322 height: 2.75em;
323 white-space: pre-line;
323 white-space: pre-line;
324 }
324 }
325 table.rctable .user {
325 table.rctable .user {
326 padding-left: 0;
326 padding-left: 0;
327 }
327 }
328 table.rctable {
328 table.rctable {
329 td.td-description,
329 td.td-description,
330 .rc-user {
330 .rc-user {
331 min-width: auto;
331 min-width: auto;
332 }
332 }
333 }
333 }
334 }
334 }
335
335
336 // Pull Requests
336 // Pull Requests
337
337
338 .pullrequests_section_head {
338 .pullrequests_section_head {
339 display: block;
339 display: block;
340 clear: both;
340 clear: both;
341 margin: @padding 0;
341 margin: @padding 0;
342 font-family: @text-bold;
342 font-family: @text-bold;
343 }
343 }
344
344
345 .pr-origininfo, .pr-targetinfo {
345 .pr-origininfo, .pr-targetinfo {
346 position: relative;
346 position: relative;
347
347
348 .tag {
348 .tag {
349 display: inline-block;
349 display: inline-block;
350 margin: 0 1em .5em 0;
350 margin: 0 1em .5em 0;
351 }
351 }
352
352
353 .clone-url {
353 .clone-url {
354 display: inline-block;
354 display: inline-block;
355 margin: 0 0 .5em 0;
355 margin: 0 0 .5em 0;
356 padding: 0;
356 padding: 0;
357 line-height: 1.2em;
357 line-height: 1.2em;
358 }
358 }
359 }
359 }
360
360
361 .pr-pullinfo {
361 .pr-pullinfo {
362 clear: both;
362 clear: both;
363 margin: .5em 0;
363 margin: .5em 0;
364 }
364 }
365
365
366 #pr-title-input {
366 #pr-title-input {
367 width: 72%;
367 width: 72%;
368 font-size: 1em;
368 font-size: 1em;
369 font-family: @text-bold;
369 font-family: @text-bold;
370 margin: 0;
370 margin: 0;
371 padding: 0 0 0 @padding/4;
371 padding: 0 0 0 @padding/4;
372 line-height: 1.7em;
372 line-height: 1.7em;
373 color: @text-color;
373 color: @text-color;
374 letter-spacing: .02em;
374 letter-spacing: .02em;
375 }
375 }
376
376
377 #pullrequest_title {
377 #pullrequest_title {
378 width: 100%;
378 width: 100%;
379 box-sizing: border-box;
379 box-sizing: border-box;
380 }
380 }
381
381
382 #pr_open_message {
382 #pr_open_message {
383 border: @border-thickness solid #fff;
383 border: @border-thickness solid #fff;
384 border-radius: @border-radius;
384 border-radius: @border-radius;
385 padding: @padding-large-vertical @padding-large-vertical @padding-large-vertical 0;
385 padding: @padding-large-vertical @padding-large-vertical @padding-large-vertical 0;
386 text-align: left;
386 text-align: left;
387 overflow: hidden;
387 overflow: hidden;
388 }
388 }
389
389
390 .pr-submit-button {
390 .pr-submit-button {
391 float: right;
391 float: right;
392 margin: 0 0 0 5px;
392 margin: 0 0 0 5px;
393 }
393 }
394
394
395 .pr-spacing-container {
395 .pr-spacing-container {
396 padding: 20px;
396 padding: 20px;
397 clear: both
397 clear: both
398 }
398 }
399
399
400 #pr-description-input {
400 #pr-description-input {
401 margin-bottom: 0;
401 margin-bottom: 0;
402 }
402 }
403
403
404 .pr-description-label {
404 .pr-description-label {
405 vertical-align: top;
405 vertical-align: top;
406 }
406 }
407
407
408 .perms_section_head {
408 .perms_section_head {
409 min-width: 625px;
409 min-width: 625px;
410
410
411 h2 {
411 h2 {
412 margin-bottom: 0;
412 margin-bottom: 0;
413 }
413 }
414
414
415 .label-checkbox {
415 .label-checkbox {
416 float: left;
416 float: left;
417 }
417 }
418
418
419 &.field {
419 &.field {
420 margin: @space 0 @padding;
420 margin: @space 0 @padding;
421 }
421 }
422
422
423 &:first-child.field {
423 &:first-child.field {
424 margin-top: 0;
424 margin-top: 0;
425
425
426 .label {
426 .label {
427 margin-top: 0;
427 margin-top: 0;
428 padding-top: 0;
428 padding-top: 0;
429 }
429 }
430
430
431 .radios {
431 .radios {
432 padding-top: 0;
432 padding-top: 0;
433 }
433 }
434 }
434 }
435
435
436 .radios {
436 .radios {
437 float: right;
437 float: right;
438 position: relative;
438 position: relative;
439 width: 405px;
439 width: 405px;
440 }
440 }
441 }
441 }
442
442
443 //--- MODULES ------------------//
443 //--- MODULES ------------------//
444
444
445
445
446 // Server Announcement
446 // Server Announcement
447 #server-announcement {
447 #server-announcement {
448 width: 95%;
448 width: 95%;
449 margin: @padding auto;
449 margin: @padding auto;
450 padding: @padding;
450 padding: @padding;
451 border-width: 2px;
451 border-width: 2px;
452 border-style: solid;
452 border-style: solid;
453 .border-radius(2px);
453 .border-radius(2px);
454 font-family: @text-bold;
454 font-family: @text-bold;
455
455
456 &.info { border-color: @alert4; background-color: @alert4-inner; }
456 &.info { border-color: @alert4; background-color: @alert4-inner; }
457 &.warning { border-color: @alert3; background-color: @alert3-inner; }
457 &.warning { border-color: @alert3; background-color: @alert3-inner; }
458 &.error { border-color: @alert2; background-color: @alert2-inner; }
458 &.error { border-color: @alert2; background-color: @alert2-inner; }
459 &.success { border-color: @alert1; background-color: @alert1-inner; }
459 &.success { border-color: @alert1; background-color: @alert1-inner; }
460 &.neutral { border-color: @grey3; background-color: @grey6; }
460 &.neutral { border-color: @grey3; background-color: @grey6; }
461 }
461 }
462
462
463 // Fixed Sidebar Column
463 // Fixed Sidebar Column
464 .sidebar-col-wrapper {
464 .sidebar-col-wrapper {
465 padding-left: @sidebar-all-width;
465 padding-left: @sidebar-all-width;
466
466
467 .sidebar {
467 .sidebar {
468 width: @sidebar-width;
468 width: @sidebar-width;
469 margin-left: -@sidebar-all-width;
469 margin-left: -@sidebar-all-width;
470 }
470 }
471 }
471 }
472
472
473 .sidebar-col-wrapper.scw-small {
473 .sidebar-col-wrapper.scw-small {
474 padding-left: @sidebar-small-all-width;
474 padding-left: @sidebar-small-all-width;
475
475
476 .sidebar {
476 .sidebar {
477 width: @sidebar-small-width;
477 width: @sidebar-small-width;
478 margin-left: -@sidebar-small-all-width;
478 margin-left: -@sidebar-small-all-width;
479 }
479 }
480 }
480 }
481
481
482
482
483 // FOOTER
483 // FOOTER
484 #footer {
484 #footer {
485 padding: 0;
485 padding: 0;
486 text-align: center;
486 text-align: center;
487 vertical-align: middle;
487 vertical-align: middle;
488 color: @grey2;
488 color: @grey2;
489 background-color: @grey6;
489 background-color: @grey6;
490
490
491 p {
491 p {
492 margin: 0;
492 margin: 0;
493 padding: 1em;
493 padding: 1em;
494 line-height: 1em;
494 line-height: 1em;
495 }
495 }
496
496
497 .server-instance { //server instance
497 .server-instance { //server instance
498 display: none;
498 display: none;
499 }
499 }
500
500
501 .title {
501 .title {
502 float: none;
502 float: none;
503 margin: 0 auto;
503 margin: 0 auto;
504 }
504 }
505 }
505 }
506
506
507 button.close {
507 button.close {
508 padding: 0;
508 padding: 0;
509 cursor: pointer;
509 cursor: pointer;
510 background: transparent;
510 background: transparent;
511 border: 0;
511 border: 0;
512 .box-shadow(none);
512 .box-shadow(none);
513 -webkit-appearance: none;
513 -webkit-appearance: none;
514 }
514 }
515
515
516 .close {
516 .close {
517 float: right;
517 float: right;
518 font-size: 21px;
518 font-size: 21px;
519 font-family: @text-bootstrap;
519 font-family: @text-bootstrap;
520 line-height: 1em;
520 line-height: 1em;
521 font-weight: bold;
521 font-weight: bold;
522 color: @grey2;
522 color: @grey2;
523
523
524 &:hover,
524 &:hover,
525 &:focus {
525 &:focus {
526 color: @grey1;
526 color: @grey1;
527 text-decoration: none;
527 text-decoration: none;
528 cursor: pointer;
528 cursor: pointer;
529 }
529 }
530 }
530 }
531
531
532 // GRID
532 // GRID
533 .sorting,
533 .sorting,
534 .sorting_desc,
534 .sorting_desc,
535 .sorting_asc {
535 .sorting_asc {
536 cursor: pointer;
536 cursor: pointer;
537 }
537 }
538 .sorting_desc:after {
538 .sorting_desc:after {
539 content: "\00A0\25B2";
539 content: "\00A0\25B2";
540 font-size: .75em;
540 font-size: .75em;
541 }
541 }
542 .sorting_asc:after {
542 .sorting_asc:after {
543 content: "\00A0\25BC";
543 content: "\00A0\25BC";
544 font-size: .68em;
544 font-size: .68em;
545 }
545 }
546
546
547
547
548 .user_auth_tokens {
548 .user_auth_tokens {
549
549
550 &.truncate {
550 &.truncate {
551 white-space: nowrap;
551 white-space: nowrap;
552 overflow: hidden;
552 overflow: hidden;
553 text-overflow: ellipsis;
553 text-overflow: ellipsis;
554 }
554 }
555
555
556 .fields .field .input {
556 .fields .field .input {
557 margin: 0;
557 margin: 0;
558 }
558 }
559
559
560 input#description {
560 input#description {
561 width: 100px;
561 width: 100px;
562 margin: 0;
562 margin: 0;
563 }
563 }
564
564
565 .drop-menu {
565 .drop-menu {
566 // TODO: johbo: Remove this, should work out of the box when
566 // TODO: johbo: Remove this, should work out of the box when
567 // having multiple inputs inline
567 // having multiple inputs inline
568 margin: 0 0 0 5px;
568 margin: 0 0 0 5px;
569 }
569 }
570 }
570 }
571 #user_list_table {
571 #user_list_table {
572 .closed {
572 .closed {
573 background-color: @grey6;
573 background-color: @grey6;
574 }
574 }
575 }
575 }
576
576
577
577
578 input {
578 input {
579 &.disabled {
579 &.disabled {
580 opacity: .5;
580 opacity: .5;
581 }
581 }
582 }
582 }
583
583
584 // remove extra padding in firefox
584 // remove extra padding in firefox
585 input::-moz-focus-inner { border:0; padding:0 }
585 input::-moz-focus-inner { border:0; padding:0 }
586
586
587 .adjacent input {
587 .adjacent input {
588 margin-bottom: @padding;
588 margin-bottom: @padding;
589 }
589 }
590
590
591 .permissions_boxes {
591 .permissions_boxes {
592 display: block;
592 display: block;
593 }
593 }
594
594
595 //TODO: lisa: this should be in tables
595 //TODO: lisa: this should be in tables
596 .show_more_col {
596 .show_more_col {
597 width: 20px;
597 width: 20px;
598 }
598 }
599
599
600 //FORMS
600 //FORMS
601
601
602 .medium-inline,
602 .medium-inline,
603 input#description.medium-inline {
603 input#description.medium-inline {
604 display: inline;
604 display: inline;
605 width: @medium-inline-input-width;
605 width: @medium-inline-input-width;
606 min-width: 100px;
606 min-width: 100px;
607 }
607 }
608
608
609 select {
609 select {
610 //reset
610 //reset
611 -webkit-appearance: none;
611 -webkit-appearance: none;
612 -moz-appearance: none;
612 -moz-appearance: none;
613
613
614 display: inline-block;
614 display: inline-block;
615 height: 28px;
615 height: 28px;
616 width: auto;
616 width: auto;
617 margin: 0 @padding @padding 0;
617 margin: 0 @padding @padding 0;
618 padding: 0 18px 0 8px;
618 padding: 0 18px 0 8px;
619 line-height:1em;
619 line-height:1em;
620 font-size: @basefontsize;
620 font-size: @basefontsize;
621 border: @border-thickness solid @rcblue;
621 border: @border-thickness solid @rcblue;
622 background:white url("../images/dt-arrow-dn.png") no-repeat 100% 50%;
622 background:white url("../images/dt-arrow-dn.png") no-repeat 100% 50%;
623 color: @rcblue;
623 color: @rcblue;
624
624
625 &:after {
625 &:after {
626 content: "\00A0\25BE";
626 content: "\00A0\25BE";
627 }
627 }
628
628
629 &:focus {
629 &:focus {
630 outline: none;
630 outline: none;
631 }
631 }
632 }
632 }
633
633
634 option {
634 option {
635 &:focus {
635 &:focus {
636 outline: none;
636 outline: none;
637 }
637 }
638 }
638 }
639
639
640 input,
640 input,
641 textarea {
641 textarea {
642 padding: @input-padding;
642 padding: @input-padding;
643 border: @input-border-thickness solid @border-highlight-color;
643 border: @input-border-thickness solid @border-highlight-color;
644 .border-radius (@border-radius);
644 .border-radius (@border-radius);
645 font-family: @text-light;
645 font-family: @text-light;
646 font-size: @basefontsize;
646 font-size: @basefontsize;
647
647
648 &.input-sm {
648 &.input-sm {
649 padding: 5px;
649 padding: 5px;
650 }
650 }
651
651
652 &#description {
652 &#description {
653 min-width: @input-description-minwidth;
653 min-width: @input-description-minwidth;
654 min-height: 1em;
654 min-height: 1em;
655 padding: 10px;
655 padding: 10px;
656 }
656 }
657 }
657 }
658
658
659 .field-sm {
659 .field-sm {
660 input,
660 input,
661 textarea {
661 textarea {
662 padding: 5px;
662 padding: 5px;
663 }
663 }
664 }
664 }
665
665
666 textarea {
666 textarea {
667 display: block;
667 display: block;
668 clear: both;
668 clear: both;
669 width: 100%;
669 width: 100%;
670 min-height: 100px;
670 min-height: 100px;
671 margin-bottom: @padding;
671 margin-bottom: @padding;
672 .box-sizing(border-box);
672 .box-sizing(border-box);
673 overflow: auto;
673 overflow: auto;
674 }
674 }
675
675
676 label {
676 label {
677 font-family: @text-light;
677 font-family: @text-light;
678 }
678 }
679
679
680 // GRAVATARS
680 // GRAVATARS
681 // centers gravatar on username to the right
681 // centers gravatar on username to the right
682
682
683 .gravatar {
683 .gravatar {
684 display: inline;
684 display: inline;
685 min-width: 16px;
685 min-width: 16px;
686 min-height: 16px;
686 min-height: 16px;
687 margin: -5px 0;
687 margin: -5px 0;
688 padding: 0;
688 padding: 0;
689 line-height: 1em;
689 line-height: 1em;
690 border: 1px solid @grey4;
690 border: 1px solid @grey4;
691 box-sizing: content-box;
691 box-sizing: content-box;
692
692
693 &.gravatar-large {
693 &.gravatar-large {
694 margin: -0.5em .25em -0.5em 0;
694 margin: -0.5em .25em -0.5em 0;
695 }
695 }
696
696
697 & + .user {
697 & + .user {
698 display: inline;
698 display: inline;
699 margin: 0;
699 margin: 0;
700 padding: 0 0 0 .17em;
700 padding: 0 0 0 .17em;
701 line-height: 1em;
701 line-height: 1em;
702 }
702 }
703 }
703 }
704
704
705 .user-inline-data {
705 .user-inline-data {
706 display: inline-block;
706 display: inline-block;
707 float: left;
707 float: left;
708 padding-left: .5em;
708 padding-left: .5em;
709 line-height: 1.3em;
709 line-height: 1.3em;
710 }
710 }
711
711
712 .rc-user { // gravatar + user wrapper
712 .rc-user { // gravatar + user wrapper
713 float: left;
713 float: left;
714 position: relative;
714 position: relative;
715 min-width: 100px;
715 min-width: 100px;
716 max-width: 200px;
716 max-width: 200px;
717 min-height: (@gravatar-size + @border-thickness * 2); // account for border
717 min-height: (@gravatar-size + @border-thickness * 2); // account for border
718 display: block;
718 display: block;
719 padding: 0 0 0 (@gravatar-size + @basefontsize/2 + @border-thickness * 2);
719 padding: 0 0 0 (@gravatar-size + @basefontsize/2 + @border-thickness * 2);
720
720
721
721
722 .gravatar {
722 .gravatar {
723 display: block;
723 display: block;
724 position: absolute;
724 position: absolute;
725 top: 0;
725 top: 0;
726 left: 0;
726 left: 0;
727 min-width: @gravatar-size;
727 min-width: @gravatar-size;
728 min-height: @gravatar-size;
728 min-height: @gravatar-size;
729 margin: 0;
729 margin: 0;
730 }
730 }
731
731
732 .user {
732 .user {
733 display: block;
733 display: block;
734 max-width: 175px;
734 max-width: 175px;
735 padding-top: 2px;
735 padding-top: 2px;
736 overflow: hidden;
736 overflow: hidden;
737 text-overflow: ellipsis;
737 text-overflow: ellipsis;
738 }
738 }
739 }
739 }
740
740
741 .gist-gravatar,
741 .gist-gravatar,
742 .journal_container {
742 .journal_container {
743 .gravatar-large {
743 .gravatar-large {
744 margin: 0 .5em -10px 0;
744 margin: 0 .5em -10px 0;
745 }
745 }
746 }
746 }
747
747
748
748
749 // ADMIN SETTINGS
749 // ADMIN SETTINGS
750
750
751 // Tag Patterns
751 // Tag Patterns
752 .tag_patterns {
752 .tag_patterns {
753 .tag_input {
753 .tag_input {
754 margin-bottom: @padding;
754 margin-bottom: @padding;
755 }
755 }
756 }
756 }
757
757
758 .locked_input {
758 .locked_input {
759 position: relative;
759 position: relative;
760
760
761 input {
761 input {
762 display: inline;
762 display: inline;
763 margin: 3px 5px 0px 0px;
763 margin: 3px 5px 0px 0px;
764 }
764 }
765
765
766 br {
766 br {
767 display: none;
767 display: none;
768 }
768 }
769
769
770 .error-message {
770 .error-message {
771 float: left;
771 float: left;
772 width: 100%;
772 width: 100%;
773 }
773 }
774
774
775 .lock_input_button {
775 .lock_input_button {
776 display: inline;
776 display: inline;
777 }
777 }
778
778
779 .help-block {
779 .help-block {
780 clear: both;
780 clear: both;
781 }
781 }
782 }
782 }
783
783
784 // Notifications
784 // Notifications
785
785
786 .notifications_buttons {
786 .notifications_buttons {
787 margin: 0 0 @space 0;
787 margin: 0 0 @space 0;
788 padding: 0;
788 padding: 0;
789
789
790 .btn {
790 .btn {
791 display: inline-block;
791 display: inline-block;
792 }
792 }
793 }
793 }
794
794
795 .notification-list {
795 .notification-list {
796
796
797 div {
797 div {
798 display: inline-block;
798 display: inline-block;
799 vertical-align: middle;
799 vertical-align: middle;
800 }
800 }
801
801
802 .container {
802 .container {
803 display: block;
803 display: block;
804 margin: 0 0 @padding 0;
804 margin: 0 0 @padding 0;
805 }
805 }
806
806
807 .delete-notifications {
807 .delete-notifications {
808 margin-left: @padding;
808 margin-left: @padding;
809 text-align: right;
809 text-align: right;
810 cursor: pointer;
810 cursor: pointer;
811 }
811 }
812
812
813 .read-notifications {
813 .read-notifications {
814 margin-left: @padding/2;
814 margin-left: @padding/2;
815 text-align: right;
815 text-align: right;
816 width: 35px;
816 width: 35px;
817 cursor: pointer;
817 cursor: pointer;
818 }
818 }
819
819
820 .icon-minus-sign {
820 .icon-minus-sign {
821 color: @alert2;
821 color: @alert2;
822 }
822 }
823
823
824 .icon-ok-sign {
824 .icon-ok-sign {
825 color: @alert1;
825 color: @alert1;
826 }
826 }
827 }
827 }
828
828
829 .user_settings {
829 .user_settings {
830 float: left;
830 float: left;
831 clear: both;
831 clear: both;
832 display: block;
832 display: block;
833 width: 100%;
833 width: 100%;
834
834
835 .gravatar_box {
835 .gravatar_box {
836 margin-bottom: @padding;
836 margin-bottom: @padding;
837
837
838 &:after {
838 &:after {
839 content: " ";
839 content: " ";
840 clear: both;
840 clear: both;
841 width: 100%;
841 width: 100%;
842 }
842 }
843 }
843 }
844
844
845 .fields .field {
845 .fields .field {
846 clear: both;
846 clear: both;
847 }
847 }
848 }
848 }
849
849
850 .advanced_settings {
850 .advanced_settings {
851 margin-bottom: @space;
851 margin-bottom: @space;
852
852
853 .help-block {
853 .help-block {
854 margin-left: 0;
854 margin-left: 0;
855 }
855 }
856
856
857 button + .help-block {
857 button + .help-block {
858 margin-top: @padding;
858 margin-top: @padding;
859 }
859 }
860 }
860 }
861
861
862 // admin settings radio buttons and labels
862 // admin settings radio buttons and labels
863 .label-2 {
863 .label-2 {
864 float: left;
864 float: left;
865 width: @label2-width;
865 width: @label2-width;
866
866
867 label {
867 label {
868 color: @grey1;
868 color: @grey1;
869 }
869 }
870 }
870 }
871 .checkboxes {
871 .checkboxes {
872 float: left;
872 float: left;
873 width: @checkboxes-width;
873 width: @checkboxes-width;
874 margin-bottom: @padding;
874 margin-bottom: @padding;
875
875
876 .checkbox {
876 .checkbox {
877 width: 100%;
877 width: 100%;
878
878
879 label {
879 label {
880 margin: 0;
880 margin: 0;
881 padding: 0;
881 padding: 0;
882 }
882 }
883 }
883 }
884
884
885 .checkbox + .checkbox {
885 .checkbox + .checkbox {
886 display: inline-block;
886 display: inline-block;
887 }
887 }
888
888
889 label {
889 label {
890 margin-right: 1em;
890 margin-right: 1em;
891 }
891 }
892 }
892 }
893
893
894 // CHANGELOG
894 // CHANGELOG
895 .container_header {
895 .container_header {
896 float: left;
896 float: left;
897 display: block;
897 display: block;
898 width: 100%;
898 width: 100%;
899 margin: @padding 0 @padding;
899 margin: @padding 0 @padding;
900
900
901 #filter_changelog {
901 #filter_changelog {
902 float: left;
902 float: left;
903 margin-right: @padding;
903 margin-right: @padding;
904 }
904 }
905
905
906 .breadcrumbs_light {
906 .breadcrumbs_light {
907 display: inline-block;
907 display: inline-block;
908 }
908 }
909 }
909 }
910
910
911 .info_box {
911 .info_box {
912 float: right;
912 float: right;
913 }
913 }
914
914
915
915
916 #graph_nodes {
916 #graph_nodes {
917 padding-top: 43px;
917 padding-top: 43px;
918 }
918 }
919
919
920 #graph_content{
920 #graph_content{
921
921
922 // adjust for table headers so that graph renders properly
922 // adjust for table headers so that graph renders properly
923 // #graph_nodes padding - table cell padding
923 // #graph_nodes padding - table cell padding
924 padding-top: (@space - (@basefontsize * 2.4));
924 padding-top: (@space - (@basefontsize * 2.4));
925
925
926 &.graph_full_width {
926 &.graph_full_width {
927 width: 100%;
927 width: 100%;
928 max-width: 100%;
928 max-width: 100%;
929 }
929 }
930 }
930 }
931
931
932 #graph {
932 #graph {
933 .flag_status {
933 .flag_status {
934 margin: 0;
934 margin: 0;
935 }
935 }
936
936
937 .pagination-left {
937 .pagination-left {
938 float: left;
938 float: left;
939 clear: both;
939 clear: both;
940 }
940 }
941
941
942 .log-container {
942 .log-container {
943 max-width: 345px;
943 max-width: 345px;
944
944
945 .message{
945 .message{
946 max-width: 340px;
946 max-width: 340px;
947 }
947 }
948 }
948 }
949
949
950 .graph-col-wrapper {
950 .graph-col-wrapper {
951 padding-left: 110px;
951 padding-left: 110px;
952
952
953 #graph_nodes {
953 #graph_nodes {
954 width: 100px;
954 width: 100px;
955 margin-left: -110px;
955 margin-left: -110px;
956 float: left;
956 float: left;
957 clear: left;
957 clear: left;
958 }
958 }
959 }
959 }
960
960
961 .load-more-commits {
961 .load-more-commits {
962 text-align: center;
962 text-align: center;
963 }
963 }
964 .load-more-commits:hover {
964 .load-more-commits:hover {
965 background-color: @grey7;
965 background-color: @grey7;
966 }
966 }
967 .load-more-commits {
967 .load-more-commits {
968 a {
968 a {
969 display: block;
969 display: block;
970 }
970 }
971 }
971 }
972 }
972 }
973
973
974 #filter_changelog {
974 #filter_changelog {
975 float: left;
975 float: left;
976 }
976 }
977
977
978
978
979 //--- THEME ------------------//
979 //--- THEME ------------------//
980
980
981 #logo {
981 #logo {
982 float: left;
982 float: left;
983 margin: 9px 0 0 0;
983 margin: 9px 0 0 0;
984
984
985 .header {
985 .header {
986 background-color: transparent;
986 background-color: transparent;
987 }
987 }
988
988
989 a {
989 a {
990 display: inline-block;
990 display: inline-block;
991 }
991 }
992
992
993 img {
993 img {
994 height:30px;
994 height:30px;
995 }
995 }
996 }
996 }
997
997
998 .logo-wrapper {
998 .logo-wrapper {
999 float:left;
999 float:left;
1000 }
1000 }
1001
1001
1002 .branding{
1002 .branding{
1003 float: left;
1003 float: left;
1004 padding: 9px 2px;
1004 padding: 9px 2px;
1005 line-height: 1em;
1005 line-height: 1em;
1006 font-size: @navigation-fontsize;
1006 font-size: @navigation-fontsize;
1007 }
1007 }
1008
1008
1009 img {
1009 img {
1010 border: none;
1010 border: none;
1011 outline: none;
1011 outline: none;
1012 }
1012 }
1013 user-profile-header
1013 user-profile-header
1014 label {
1014 label {
1015
1015
1016 input[type="checkbox"] {
1016 input[type="checkbox"] {
1017 margin-right: 1em;
1017 margin-right: 1em;
1018 }
1018 }
1019 input[type="radio"] {
1019 input[type="radio"] {
1020 margin-right: 1em;
1020 margin-right: 1em;
1021 }
1021 }
1022 }
1022 }
1023
1023
1024 .flag_status {
1024 .flag_status {
1025 margin: 2px 8px 6px 2px;
1025 margin: 2px 8px 6px 2px;
1026 &.under_review {
1026 &.under_review {
1027 .circle(5px, @alert3);
1027 .circle(5px, @alert3);
1028 }
1028 }
1029 &.approved {
1029 &.approved {
1030 .circle(5px, @alert1);
1030 .circle(5px, @alert1);
1031 }
1031 }
1032 &.rejected,
1032 &.rejected,
1033 &.forced_closed{
1033 &.forced_closed{
1034 .circle(5px, @alert2);
1034 .circle(5px, @alert2);
1035 }
1035 }
1036 &.not_reviewed {
1036 &.not_reviewed {
1037 .circle(5px, @grey5);
1037 .circle(5px, @grey5);
1038 }
1038 }
1039 }
1039 }
1040
1040
1041 .flag_status_comment_box {
1041 .flag_status_comment_box {
1042 margin: 5px 6px 0px 2px;
1042 margin: 5px 6px 0px 2px;
1043 }
1043 }
1044 .test_pattern_preview {
1044 .test_pattern_preview {
1045 margin: @space 0;
1045 margin: @space 0;
1046
1046
1047 p {
1047 p {
1048 margin-bottom: 0;
1048 margin-bottom: 0;
1049 border-bottom: @border-thickness solid @border-default-color;
1049 border-bottom: @border-thickness solid @border-default-color;
1050 color: @grey3;
1050 color: @grey3;
1051 }
1051 }
1052
1052
1053 .btn {
1053 .btn {
1054 margin-bottom: @padding;
1054 margin-bottom: @padding;
1055 }
1055 }
1056 }
1056 }
1057 #test_pattern_result {
1057 #test_pattern_result {
1058 display: none;
1058 display: none;
1059 &:extend(pre);
1059 &:extend(pre);
1060 padding: .9em;
1060 padding: .9em;
1061 color: @grey3;
1061 color: @grey3;
1062 background-color: @grey7;
1062 background-color: @grey7;
1063 border-right: @border-thickness solid @border-default-color;
1063 border-right: @border-thickness solid @border-default-color;
1064 border-bottom: @border-thickness solid @border-default-color;
1064 border-bottom: @border-thickness solid @border-default-color;
1065 border-left: @border-thickness solid @border-default-color;
1065 border-left: @border-thickness solid @border-default-color;
1066 }
1066 }
1067
1067
1068 #repo_vcs_settings {
1068 #repo_vcs_settings {
1069 #inherit_overlay_vcs_default {
1069 #inherit_overlay_vcs_default {
1070 display: none;
1070 display: none;
1071 }
1071 }
1072 #inherit_overlay_vcs_custom {
1072 #inherit_overlay_vcs_custom {
1073 display: custom;
1073 display: custom;
1074 }
1074 }
1075 &.inherited {
1075 &.inherited {
1076 #inherit_overlay_vcs_default {
1076 #inherit_overlay_vcs_default {
1077 display: block;
1077 display: block;
1078 }
1078 }
1079 #inherit_overlay_vcs_custom {
1079 #inherit_overlay_vcs_custom {
1080 display: none;
1080 display: none;
1081 }
1081 }
1082 }
1082 }
1083 }
1083 }
1084
1084
1085 .issue-tracker-link {
1085 .issue-tracker-link {
1086 color: @rcblue;
1086 color: @rcblue;
1087 }
1087 }
1088
1088
1089 // Issue Tracker Table Show/Hide
1089 // Issue Tracker Table Show/Hide
1090 #repo_issue_tracker {
1090 #repo_issue_tracker {
1091 #inherit_overlay {
1091 #inherit_overlay {
1092 display: none;
1092 display: none;
1093 }
1093 }
1094 #custom_overlay {
1094 #custom_overlay {
1095 display: custom;
1095 display: custom;
1096 }
1096 }
1097 &.inherited {
1097 &.inherited {
1098 #inherit_overlay {
1098 #inherit_overlay {
1099 display: block;
1099 display: block;
1100 }
1100 }
1101 #custom_overlay {
1101 #custom_overlay {
1102 display: none;
1102 display: none;
1103 }
1103 }
1104 }
1104 }
1105 }
1105 }
1106 table.issuetracker {
1106 table.issuetracker {
1107 &.readonly {
1107 &.readonly {
1108 tr, td {
1108 tr, td {
1109 color: @grey3;
1109 color: @grey3;
1110 }
1110 }
1111 }
1111 }
1112 .edit {
1112 .edit {
1113 display: none;
1113 display: none;
1114 }
1114 }
1115 .editopen {
1115 .editopen {
1116 .edit {
1116 .edit {
1117 display: inline;
1117 display: inline;
1118 }
1118 }
1119 .entry {
1119 .entry {
1120 display: none;
1120 display: none;
1121 }
1121 }
1122 }
1122 }
1123 tr td.td-action {
1123 tr td.td-action {
1124 min-width: 117px;
1124 min-width: 117px;
1125 }
1125 }
1126 td input {
1126 td input {
1127 max-width: none;
1127 max-width: none;
1128 min-width: 30px;
1128 min-width: 30px;
1129 width: 80%;
1129 width: 80%;
1130 }
1130 }
1131 .issuetracker_pref input {
1131 .issuetracker_pref input {
1132 width: 40%;
1132 width: 40%;
1133 }
1133 }
1134 input.edit_issuetracker_update {
1134 input.edit_issuetracker_update {
1135 margin-right: 0;
1135 margin-right: 0;
1136 width: auto;
1136 width: auto;
1137 }
1137 }
1138 }
1138 }
1139
1139
1140 table.integrations {
1140 table.integrations {
1141 .td-icon {
1141 .td-icon {
1142 width: 20px;
1142 width: 20px;
1143 .integration-icon {
1143 .integration-icon {
1144 height: 20px;
1144 height: 20px;
1145 width: 20px;
1145 width: 20px;
1146 }
1146 }
1147 }
1147 }
1148 }
1148 }
1149
1149
1150 .integrations {
1150 .integrations {
1151 a.integration-box {
1151 a.integration-box {
1152 color: @text-color;
1152 color: @text-color;
1153 &:hover {
1153 &:hover {
1154 .panel {
1154 .panel {
1155 background: #fbfbfb;
1155 background: #fbfbfb;
1156 }
1156 }
1157 }
1157 }
1158 .integration-icon {
1158 .integration-icon {
1159 width: 30px;
1159 width: 30px;
1160 height: 30px;
1160 height: 30px;
1161 margin-right: 20px;
1161 margin-right: 20px;
1162 float: left;
1162 float: left;
1163 }
1163 }
1164
1164
1165 .panel-body {
1165 .panel-body {
1166 padding: 10px;
1166 padding: 10px;
1167 }
1167 }
1168 .panel {
1168 .panel {
1169 margin-bottom: 10px;
1169 margin-bottom: 10px;
1170 }
1170 }
1171 h2 {
1171 h2 {
1172 display: inline-block;
1172 display: inline-block;
1173 margin: 0;
1173 margin: 0;
1174 min-width: 140px;
1174 min-width: 140px;
1175 }
1175 }
1176 }
1176 }
1177 }
1177 }
1178
1178
1179 //Permissions Settings
1179 //Permissions Settings
1180 #add_perm {
1180 #add_perm {
1181 margin: 0 0 @padding;
1181 margin: 0 0 @padding;
1182 cursor: pointer;
1182 cursor: pointer;
1183 }
1183 }
1184
1184
1185 .perm_ac {
1185 .perm_ac {
1186 input {
1186 input {
1187 width: 95%;
1187 width: 95%;
1188 }
1188 }
1189 }
1189 }
1190
1190
1191 .autocomplete-suggestions {
1191 .autocomplete-suggestions {
1192 width: auto !important; // overrides autocomplete.js
1192 width: auto !important; // overrides autocomplete.js
1193 margin: 0;
1193 margin: 0;
1194 border: @border-thickness solid @rcblue;
1194 border: @border-thickness solid @rcblue;
1195 border-radius: @border-radius;
1195 border-radius: @border-radius;
1196 color: @rcblue;
1196 color: @rcblue;
1197 background-color: white;
1197 background-color: white;
1198 }
1198 }
1199 .autocomplete-selected {
1199 .autocomplete-selected {
1200 background: #F0F0F0;
1200 background: #F0F0F0;
1201 }
1201 }
1202 .ac-container-wrap {
1202 .ac-container-wrap {
1203 margin: 0;
1203 margin: 0;
1204 padding: 8px;
1204 padding: 8px;
1205 border-bottom: @border-thickness solid @rclightblue;
1205 border-bottom: @border-thickness solid @rclightblue;
1206 list-style-type: none;
1206 list-style-type: none;
1207 cursor: pointer;
1207 cursor: pointer;
1208
1208
1209 &:hover {
1209 &:hover {
1210 background-color: @rclightblue;
1210 background-color: @rclightblue;
1211 }
1211 }
1212
1212
1213 img {
1213 img {
1214 height: @gravatar-size;
1214 height: @gravatar-size;
1215 width: @gravatar-size;
1215 width: @gravatar-size;
1216 margin-right: 1em;
1216 margin-right: 1em;
1217 }
1217 }
1218
1218
1219 strong {
1219 strong {
1220 font-weight: normal;
1220 font-weight: normal;
1221 }
1221 }
1222 }
1222 }
1223
1223
1224 // Settings Dropdown
1224 // Settings Dropdown
1225 .user-menu .container {
1225 .user-menu .container {
1226 padding: 0 4px;
1226 padding: 0 4px;
1227 margin: 0;
1227 margin: 0;
1228 }
1228 }
1229
1229
1230 .user-menu .gravatar {
1230 .user-menu .gravatar {
1231 cursor: pointer;
1231 cursor: pointer;
1232 }
1232 }
1233
1233
1234 .codeblock {
1234 .codeblock {
1235 margin-bottom: @padding;
1235 margin-bottom: @padding;
1236 clear: both;
1236 clear: both;
1237
1237
1238 .stats{
1238 .stats{
1239 overflow: hidden;
1239 overflow: hidden;
1240 }
1240 }
1241
1241
1242 .message{
1242 .message{
1243 textarea{
1243 textarea{
1244 margin: 0;
1244 margin: 0;
1245 }
1245 }
1246 }
1246 }
1247
1247
1248 .code-header {
1248 .code-header {
1249 .stats {
1249 .stats {
1250 line-height: 2em;
1250 line-height: 2em;
1251
1251
1252 .revision_id {
1252 .revision_id {
1253 margin-left: 0;
1253 margin-left: 0;
1254 }
1254 }
1255 .buttons {
1255 .buttons {
1256 padding-right: 0;
1256 padding-right: 0;
1257 }
1257 }
1258 }
1258 }
1259
1259
1260 .item{
1260 .item{
1261 margin-right: 0.5em;
1261 margin-right: 0.5em;
1262 }
1262 }
1263 }
1263 }
1264
1264
1265 #editor_container{
1265 #editor_container{
1266 position: relative;
1266 position: relative;
1267 margin: @padding;
1267 margin: @padding;
1268 }
1268 }
1269 }
1269 }
1270
1270
1271 #file_history_container {
1271 #file_history_container {
1272 display: none;
1272 display: none;
1273 }
1273 }
1274
1274
1275 .file-history-inner {
1275 .file-history-inner {
1276 margin-bottom: 10px;
1276 margin-bottom: 10px;
1277 }
1277 }
1278
1278
1279 // Pull Requests
1279 // Pull Requests
1280 .summary-details {
1280 .summary-details {
1281 width: 72%;
1281 width: 72%;
1282 }
1282 }
1283 .pr-summary {
1283 .pr-summary {
1284 border-bottom: @border-thickness solid @grey5;
1284 border-bottom: @border-thickness solid @grey5;
1285 margin-bottom: @space;
1285 margin-bottom: @space;
1286 }
1286 }
1287 .reviewers-title {
1287 .reviewers-title {
1288 width: 25%;
1288 width: 25%;
1289 min-width: 200px;
1289 min-width: 200px;
1290 }
1290 }
1291 .reviewers {
1291 .reviewers {
1292 width: 25%;
1292 width: 25%;
1293 min-width: 200px;
1293 min-width: 200px;
1294 }
1294 }
1295 .reviewers ul li {
1295 .reviewers ul li {
1296 position: relative;
1296 position: relative;
1297 width: 100%;
1297 width: 100%;
1298 margin-bottom: 8px;
1298 margin-bottom: 8px;
1299 }
1299 }
1300 .reviewers_member {
1300 .reviewers_member {
1301 width: 100%;
1301 width: 100%;
1302 overflow: auto;
1302 overflow: auto;
1303 }
1303 }
1304 .reviewer_reason {
1304 .reviewer_reason {
1305 padding-left: 20px;
1305 padding-left: 20px;
1306 }
1306 }
1307 .reviewer_status {
1307 .reviewer_status {
1308 display: inline-block;
1308 display: inline-block;
1309 vertical-align: top;
1309 vertical-align: top;
1310 width: 7%;
1310 width: 7%;
1311 min-width: 20px;
1311 min-width: 20px;
1312 height: 1.2em;
1312 height: 1.2em;
1313 margin-top: 3px;
1313 margin-top: 3px;
1314 line-height: 1em;
1314 line-height: 1em;
1315 }
1315 }
1316
1316
1317 .reviewer_name {
1317 .reviewer_name {
1318 display: inline-block;
1318 display: inline-block;
1319 max-width: 83%;
1319 max-width: 83%;
1320 padding-right: 20px;
1320 padding-right: 20px;
1321 vertical-align: middle;
1321 vertical-align: middle;
1322 line-height: 1;
1322 line-height: 1;
1323
1323
1324 .rc-user {
1324 .rc-user {
1325 min-width: 0;
1325 min-width: 0;
1326 margin: -2px 1em 0 0;
1326 margin: -2px 1em 0 0;
1327 }
1327 }
1328
1328
1329 .reviewer {
1329 .reviewer {
1330 float: left;
1330 float: left;
1331 }
1331 }
1332 }
1332 }
1333
1333
1334 .reviewer_member_remove {
1334 .reviewer_member_remove {
1335 position: absolute;
1335 position: absolute;
1336 right: 0;
1336 right: 0;
1337 top: 0;
1337 top: 0;
1338 width: 16px;
1338 width: 16px;
1339 margin-bottom: 10px;
1339 margin-bottom: 10px;
1340 padding: 0;
1340 padding: 0;
1341 color: black;
1341 color: black;
1342 }
1342 }
1343 .reviewer_member_status {
1343 .reviewer_member_status {
1344 margin-top: 5px;
1344 margin-top: 5px;
1345 }
1345 }
1346 .pr-summary #summary{
1346 .pr-summary #summary{
1347 width: 100%;
1347 width: 100%;
1348 }
1348 }
1349 .pr-summary .action_button:hover {
1349 .pr-summary .action_button:hover {
1350 border: 0;
1350 border: 0;
1351 cursor: pointer;
1351 cursor: pointer;
1352 }
1352 }
1353 .pr-details-title {
1353 .pr-details-title {
1354 padding-bottom: 8px;
1354 padding-bottom: 8px;
1355 border-bottom: @border-thickness solid @grey5;
1355 border-bottom: @border-thickness solid @grey5;
1356
1356
1357 .action_button.disabled {
1357 .action_button.disabled {
1358 color: @grey4;
1358 color: @grey4;
1359 cursor: inherit;
1359 cursor: inherit;
1360 }
1360 }
1361 .action_button {
1361 .action_button {
1362 color: @rcblue;
1362 color: @rcblue;
1363 }
1363 }
1364 }
1364 }
1365 .pr-details-content {
1365 .pr-details-content {
1366 margin-top: @textmargin;
1366 margin-top: @textmargin;
1367 margin-bottom: @textmargin;
1367 margin-bottom: @textmargin;
1368 }
1368 }
1369 .pr-description {
1369 .pr-description {
1370 white-space:pre-wrap;
1370 white-space:pre-wrap;
1371 }
1371 }
1372 .group_members {
1372 .group_members {
1373 margin-top: 0;
1373 margin-top: 0;
1374 padding: 0;
1374 padding: 0;
1375 list-style: outside none none;
1375 list-style: outside none none;
1376
1376
1377 img {
1377 img {
1378 height: @gravatar-size;
1378 height: @gravatar-size;
1379 width: @gravatar-size;
1379 width: @gravatar-size;
1380 margin-right: .5em;
1380 margin-right: .5em;
1381 margin-left: 3px;
1381 margin-left: 3px;
1382 }
1382 }
1383
1383
1384 .to-delete {
1384 .to-delete {
1385 .user {
1385 .user {
1386 text-decoration: line-through;
1386 text-decoration: line-through;
1387 }
1387 }
1388 }
1388 }
1389 }
1389 }
1390
1390
1391 .compare_view_commits_title {
1391 .compare_view_commits_title {
1392 .disabled {
1392 .disabled {
1393 cursor: inherit;
1393 cursor: inherit;
1394 &:hover{
1394 &:hover{
1395 background-color: inherit;
1395 background-color: inherit;
1396 color: inherit;
1396 color: inherit;
1397 }
1397 }
1398 }
1398 }
1399 }
1399 }
1400
1400
1401 .subtitle-compare {
1401 .subtitle-compare {
1402 margin: -15px 0px 0px 0px;
1402 margin: -15px 0px 0px 0px;
1403 }
1403 }
1404
1404
1405 .comments-summary-td {
1405 .comments-summary-td {
1406 border-top: 1px dashed @grey5;
1406 border-top: 1px dashed @grey5;
1407 }
1407 }
1408
1408
1409 // new entry in group_members
1409 // new entry in group_members
1410 .td-author-new-entry {
1410 .td-author-new-entry {
1411 background-color: rgba(red(@alert1), green(@alert1), blue(@alert1), 0.3);
1411 background-color: rgba(red(@alert1), green(@alert1), blue(@alert1), 0.3);
1412 }
1412 }
1413
1413
1414 .usergroup_member_remove {
1414 .usergroup_member_remove {
1415 width: 16px;
1415 width: 16px;
1416 margin-bottom: 10px;
1416 margin-bottom: 10px;
1417 padding: 0;
1417 padding: 0;
1418 color: black !important;
1418 color: black !important;
1419 cursor: pointer;
1419 cursor: pointer;
1420 }
1420 }
1421
1421
1422 .reviewer_ac .ac-input {
1422 .reviewer_ac .ac-input {
1423 width: 92%;
1423 width: 92%;
1424 margin-bottom: 1em;
1424 margin-bottom: 1em;
1425 }
1425 }
1426
1426
1427 .compare_view_commits tr{
1427 .compare_view_commits tr{
1428 height: 20px;
1428 height: 20px;
1429 }
1429 }
1430 .compare_view_commits td {
1430 .compare_view_commits td {
1431 vertical-align: top;
1431 vertical-align: top;
1432 padding-top: 10px;
1432 padding-top: 10px;
1433 }
1433 }
1434 .compare_view_commits .author {
1434 .compare_view_commits .author {
1435 margin-left: 5px;
1435 margin-left: 5px;
1436 }
1436 }
1437
1437
1438 .compare_view_commits {
1438 .compare_view_commits {
1439 .color-a {
1439 .color-a {
1440 color: @alert1;
1440 color: @alert1;
1441 }
1441 }
1442
1442
1443 .color-c {
1443 .color-c {
1444 color: @color3;
1444 color: @color3;
1445 }
1445 }
1446
1446
1447 .color-r {
1447 .color-r {
1448 color: @color5;
1448 color: @color5;
1449 }
1449 }
1450
1450
1451 .color-a-bg {
1451 .color-a-bg {
1452 background-color: @alert1;
1452 background-color: @alert1;
1453 }
1453 }
1454
1454
1455 .color-c-bg {
1455 .color-c-bg {
1456 background-color: @alert3;
1456 background-color: @alert3;
1457 }
1457 }
1458
1458
1459 .color-r-bg {
1459 .color-r-bg {
1460 background-color: @alert2;
1460 background-color: @alert2;
1461 }
1461 }
1462
1462
1463 .color-a-border {
1463 .color-a-border {
1464 border: 1px solid @alert1;
1464 border: 1px solid @alert1;
1465 }
1465 }
1466
1466
1467 .color-c-border {
1467 .color-c-border {
1468 border: 1px solid @alert3;
1468 border: 1px solid @alert3;
1469 }
1469 }
1470
1470
1471 .color-r-border {
1471 .color-r-border {
1472 border: 1px solid @alert2;
1472 border: 1px solid @alert2;
1473 }
1473 }
1474
1474
1475 .commit-change-indicator {
1475 .commit-change-indicator {
1476 width: 15px;
1476 width: 15px;
1477 height: 15px;
1477 height: 15px;
1478 position: relative;
1478 position: relative;
1479 left: 15px;
1479 left: 15px;
1480 }
1480 }
1481
1481
1482 .commit-change-content {
1482 .commit-change-content {
1483 text-align: center;
1483 text-align: center;
1484 vertical-align: middle;
1484 vertical-align: middle;
1485 line-height: 15px;
1485 line-height: 15px;
1486 }
1486 }
1487 }
1487 }
1488
1488
1489 .compare_view_files {
1489 .compare_view_files {
1490 width: 100%;
1490 width: 100%;
1491
1491
1492 td {
1492 td {
1493 vertical-align: middle;
1493 vertical-align: middle;
1494 }
1494 }
1495 }
1495 }
1496
1496
1497 .compare_view_filepath {
1497 .compare_view_filepath {
1498 color: @grey1;
1498 color: @grey1;
1499 }
1499 }
1500
1500
1501 .show_more {
1501 .show_more {
1502 display: inline-block;
1502 display: inline-block;
1503 position: relative;
1503 position: relative;
1504 vertical-align: middle;
1504 vertical-align: middle;
1505 width: 4px;
1505 width: 4px;
1506 height: @basefontsize;
1506 height: @basefontsize;
1507
1507
1508 &:after {
1508 &:after {
1509 content: "\00A0\25BE";
1509 content: "\00A0\25BE";
1510 display: inline-block;
1510 display: inline-block;
1511 width:10px;
1511 width:10px;
1512 line-height: 5px;
1512 line-height: 5px;
1513 font-size: 12px;
1513 font-size: 12px;
1514 cursor: pointer;
1514 cursor: pointer;
1515 }
1515 }
1516 }
1516 }
1517
1517
1518 .journal_more .show_more {
1518 .journal_more .show_more {
1519 display: inline;
1519 display: inline;
1520
1520
1521 &:after {
1521 &:after {
1522 content: none;
1522 content: none;
1523 }
1523 }
1524 }
1524 }
1525
1525
1526 .open .show_more:after,
1526 .open .show_more:after,
1527 .select2-dropdown-open .show_more:after {
1527 .select2-dropdown-open .show_more:after {
1528 .rotate(180deg);
1528 .rotate(180deg);
1529 margin-left: 4px;
1529 margin-left: 4px;
1530 }
1530 }
1531
1531
1532
1532
1533 .compare_view_commits .collapse_commit:after {
1533 .compare_view_commits .collapse_commit:after {
1534 cursor: pointer;
1534 cursor: pointer;
1535 content: "\00A0\25B4";
1535 content: "\00A0\25B4";
1536 margin-left: -3px;
1536 margin-left: -3px;
1537 font-size: 17px;
1537 font-size: 17px;
1538 color: @grey4;
1538 color: @grey4;
1539 }
1539 }
1540
1540
1541 .diff_links {
1541 .diff_links {
1542 margin-left: 8px;
1542 margin-left: 8px;
1543 }
1543 }
1544
1544
1545 div.ancestor {
1545 div.ancestor {
1546 margin: -30px 0px;
1546 margin: -30px 0px;
1547 }
1547 }
1548
1548
1549 .cs_icon_td input[type="checkbox"] {
1549 .cs_icon_td input[type="checkbox"] {
1550 display: none;
1550 display: none;
1551 }
1551 }
1552
1552
1553 .cs_icon_td .expand_file_icon:after {
1553 .cs_icon_td .expand_file_icon:after {
1554 cursor: pointer;
1554 cursor: pointer;
1555 content: "\00A0\25B6";
1555 content: "\00A0\25B6";
1556 font-size: 12px;
1556 font-size: 12px;
1557 color: @grey4;
1557 color: @grey4;
1558 }
1558 }
1559
1559
1560 .cs_icon_td .collapse_file_icon:after {
1560 .cs_icon_td .collapse_file_icon:after {
1561 cursor: pointer;
1561 cursor: pointer;
1562 content: "\00A0\25BC";
1562 content: "\00A0\25BC";
1563 font-size: 12px;
1563 font-size: 12px;
1564 color: @grey4;
1564 color: @grey4;
1565 }
1565 }
1566
1566
1567 /*new binary
1567 /*new binary
1568 NEW_FILENODE = 1
1568 NEW_FILENODE = 1
1569 DEL_FILENODE = 2
1569 DEL_FILENODE = 2
1570 MOD_FILENODE = 3
1570 MOD_FILENODE = 3
1571 RENAMED_FILENODE = 4
1571 RENAMED_FILENODE = 4
1572 COPIED_FILENODE = 5
1572 COPIED_FILENODE = 5
1573 CHMOD_FILENODE = 6
1573 CHMOD_FILENODE = 6
1574 BIN_FILENODE = 7
1574 BIN_FILENODE = 7
1575 */
1575 */
1576 .cs_files_expand {
1576 .cs_files_expand {
1577 font-size: @basefontsize + 5px;
1577 font-size: @basefontsize + 5px;
1578 line-height: 1.8em;
1578 line-height: 1.8em;
1579 float: right;
1579 float: right;
1580 }
1580 }
1581
1581
1582 .cs_files_expand span{
1582 .cs_files_expand span{
1583 color: @rcblue;
1583 color: @rcblue;
1584 cursor: pointer;
1584 cursor: pointer;
1585 }
1585 }
1586 .cs_files {
1586 .cs_files {
1587 clear: both;
1587 clear: both;
1588 padding-bottom: @padding;
1588 padding-bottom: @padding;
1589
1589
1590 .cur_cs {
1590 .cur_cs {
1591 margin: 10px 2px;
1591 margin: 10px 2px;
1592 font-weight: bold;
1592 font-weight: bold;
1593 }
1593 }
1594
1594
1595 .node {
1595 .node {
1596 float: left;
1596 float: left;
1597 }
1597 }
1598
1598
1599 .changes {
1599 .changes {
1600 float: right;
1600 float: right;
1601 color: white;
1601 color: white;
1602 font-size: @basefontsize - 4px;
1602 font-size: @basefontsize - 4px;
1603 margin-top: 4px;
1603 margin-top: 4px;
1604 opacity: 0.6;
1604 opacity: 0.6;
1605 filter: Alpha(opacity=60); /* IE8 and earlier */
1605 filter: Alpha(opacity=60); /* IE8 and earlier */
1606
1606
1607 .added {
1607 .added {
1608 background-color: @alert1;
1608 background-color: @alert1;
1609 float: left;
1609 float: left;
1610 text-align: center;
1610 text-align: center;
1611 }
1611 }
1612
1612
1613 .deleted {
1613 .deleted {
1614 background-color: @alert2;
1614 background-color: @alert2;
1615 float: left;
1615 float: left;
1616 text-align: center;
1616 text-align: center;
1617 }
1617 }
1618
1618
1619 .bin {
1619 .bin {
1620 background-color: @alert1;
1620 background-color: @alert1;
1621 text-align: center;
1621 text-align: center;
1622 }
1622 }
1623
1623
1624 /*new binary*/
1624 /*new binary*/
1625 .bin.bin1 {
1625 .bin.bin1 {
1626 background-color: @alert1;
1626 background-color: @alert1;
1627 text-align: center;
1627 text-align: center;
1628 }
1628 }
1629
1629
1630 /*deleted binary*/
1630 /*deleted binary*/
1631 .bin.bin2 {
1631 .bin.bin2 {
1632 background-color: @alert2;
1632 background-color: @alert2;
1633 text-align: center;
1633 text-align: center;
1634 }
1634 }
1635
1635
1636 /*mod binary*/
1636 /*mod binary*/
1637 .bin.bin3 {
1637 .bin.bin3 {
1638 background-color: @grey2;
1638 background-color: @grey2;
1639 text-align: center;
1639 text-align: center;
1640 }
1640 }
1641
1641
1642 /*rename file*/
1642 /*rename file*/
1643 .bin.bin4 {
1643 .bin.bin4 {
1644 background-color: @alert4;
1644 background-color: @alert4;
1645 text-align: center;
1645 text-align: center;
1646 }
1646 }
1647
1647
1648 /*copied file*/
1648 /*copied file*/
1649 .bin.bin5 {
1649 .bin.bin5 {
1650 background-color: @alert4;
1650 background-color: @alert4;
1651 text-align: center;
1651 text-align: center;
1652 }
1652 }
1653
1653
1654 /*chmod file*/
1654 /*chmod file*/
1655 .bin.bin6 {
1655 .bin.bin6 {
1656 background-color: @grey2;
1656 background-color: @grey2;
1657 text-align: center;
1657 text-align: center;
1658 }
1658 }
1659 }
1659 }
1660 }
1660 }
1661
1661
1662 .cs_files .cs_added, .cs_files .cs_A,
1662 .cs_files .cs_added, .cs_files .cs_A,
1663 .cs_files .cs_added, .cs_files .cs_M,
1663 .cs_files .cs_added, .cs_files .cs_M,
1664 .cs_files .cs_added, .cs_files .cs_D {
1664 .cs_files .cs_added, .cs_files .cs_D {
1665 height: 16px;
1665 height: 16px;
1666 padding-right: 10px;
1666 padding-right: 10px;
1667 margin-top: 7px;
1667 margin-top: 7px;
1668 text-align: left;
1668 text-align: left;
1669 }
1669 }
1670
1670
1671 .cs_icon_td {
1671 .cs_icon_td {
1672 min-width: 16px;
1672 min-width: 16px;
1673 width: 16px;
1673 width: 16px;
1674 }
1674 }
1675
1675
1676 .pull-request-merge {
1676 .pull-request-merge {
1677 border: 1px solid @grey5;
1677 border: 1px solid @grey5;
1678 padding: 10px 0px 20px;
1678 padding: 10px 0px 20px;
1679 margin-top: 10px;
1679 margin-top: 10px;
1680 margin-bottom: 20px;
1680 margin-bottom: 20px;
1681 }
1681 }
1682
1682
1683 .pull-request-merge ul {
1683 .pull-request-merge ul {
1684 padding: 0px 0px;
1684 padding: 0px 0px;
1685 }
1685 }
1686
1686
1687 .pull-request-merge li:before{
1687 .pull-request-merge li:before{
1688 content:none;
1688 content:none;
1689 }
1689 }
1690
1690
1691 .pull-request-merge .pull-request-wrap {
1691 .pull-request-merge .pull-request-wrap {
1692 height: auto;
1692 height: auto;
1693 padding: 0px 0px;
1693 padding: 0px 0px;
1694 text-align: right;
1694 text-align: right;
1695 }
1695 }
1696
1696
1697 .pull-request-merge span {
1697 .pull-request-merge span {
1698 margin-right: 5px;
1698 margin-right: 5px;
1699 }
1699 }
1700
1700
1701 .pull-request-merge-actions {
1701 .pull-request-merge-actions {
1702 height: 30px;
1702 height: 30px;
1703 padding: 0px 0px;
1703 padding: 0px 0px;
1704 }
1704 }
1705
1705
1706 .merge-status {
1706 .merge-status {
1707 margin-right: 5px;
1707 margin-right: 5px;
1708 }
1708 }
1709
1709
1710 .merge-message {
1710 .merge-message {
1711 font-size: 1.2em
1711 font-size: 1.2em
1712 }
1712 }
1713
1713
1714 .merge-message.success i,
1714 .merge-message.success i,
1715 .merge-icon.success i {
1715 .merge-icon.success i {
1716 color:@alert1;
1716 color:@alert1;
1717 }
1717 }
1718
1718
1719 .merge-message.warning i,
1719 .merge-message.warning i,
1720 .merge-icon.warning i {
1720 .merge-icon.warning i {
1721 color: @alert3;
1721 color: @alert3;
1722 }
1722 }
1723
1723
1724 .merge-message.error i,
1724 .merge-message.error i,
1725 .merge-icon.error i {
1725 .merge-icon.error i {
1726 color:@alert2;
1726 color:@alert2;
1727 }
1727 }
1728
1728
1729 .pr-versions {
1729 .pr-versions {
1730 font-size: 1.1em;
1730 font-size: 1.1em;
1731
1731
1732 table {
1732 table {
1733 padding: 0px 5px;
1733 padding: 0px 5px;
1734 }
1734 }
1735
1735
1736 td {
1736 td {
1737 line-height: 15px;
1737 line-height: 15px;
1738 }
1738 }
1739
1739
1740 .flag_status {
1740 .flag_status {
1741 margin: 0;
1741 margin: 0;
1742 }
1742 }
1743
1743
1744 .compare-radio-button {
1744 .compare-radio-button {
1745 position: relative;
1745 position: relative;
1746 top: -3px;
1746 top: -3px;
1747 }
1747 }
1748 }
1748 }
1749
1749
1750
1750
1751 #close_pull_request {
1751 #close_pull_request {
1752 margin-right: 0px;
1752 margin-right: 0px;
1753 }
1753 }
1754
1754
1755 .empty_data {
1755 .empty_data {
1756 color: @grey4;
1756 color: @grey4;
1757 }
1757 }
1758
1758
1759 #changeset_compare_view_content {
1759 #changeset_compare_view_content {
1760 margin-bottom: @space;
1760 margin-bottom: @space;
1761 clear: both;
1761 clear: both;
1762 width: 100%;
1762 width: 100%;
1763 box-sizing: border-box;
1763 box-sizing: border-box;
1764 .border-radius(@border-radius);
1764 .border-radius(@border-radius);
1765
1765
1766 .help-block {
1766 .help-block {
1767 margin: @padding 0;
1767 margin: @padding 0;
1768 color: @text-color;
1768 color: @text-color;
1769 }
1769 }
1770
1770
1771 .empty_data {
1771 .empty_data {
1772 margin: @padding 0;
1772 margin: @padding 0;
1773 }
1773 }
1774
1774
1775 .alert {
1775 .alert {
1776 margin-bottom: @space;
1776 margin-bottom: @space;
1777 }
1777 }
1778 }
1778 }
1779
1779
1780 .table_disp {
1780 .table_disp {
1781 .status {
1781 .status {
1782 width: auto;
1782 width: auto;
1783
1783
1784 .flag_status {
1784 .flag_status {
1785 float: left;
1785 float: left;
1786 }
1786 }
1787 }
1787 }
1788 }
1788 }
1789
1789
1790 .status_box_menu {
1790 .status_box_menu {
1791 margin: 0;
1791 margin: 0;
1792 }
1792 }
1793
1793
1794 .notification-table{
1794 .notification-table{
1795 margin-bottom: @space;
1795 margin-bottom: @space;
1796 display: table;
1796 display: table;
1797 width: 100%;
1797 width: 100%;
1798
1798
1799 .container{
1799 .container{
1800 display: table-row;
1800 display: table-row;
1801
1801
1802 .notification-header{
1802 .notification-header{
1803 border-bottom: @border-thickness solid @border-default-color;
1803 border-bottom: @border-thickness solid @border-default-color;
1804 }
1804 }
1805
1805
1806 .notification-subject{
1806 .notification-subject{
1807 display: table-cell;
1807 display: table-cell;
1808 }
1808 }
1809 }
1809 }
1810 }
1810 }
1811
1811
1812 // Notifications
1812 // Notifications
1813 .notification-header{
1813 .notification-header{
1814 display: table;
1814 display: table;
1815 width: 100%;
1815 width: 100%;
1816 padding: floor(@basefontsize/2) 0;
1816 padding: floor(@basefontsize/2) 0;
1817 line-height: 1em;
1817 line-height: 1em;
1818
1818
1819 .desc, .delete-notifications, .read-notifications{
1819 .desc, .delete-notifications, .read-notifications{
1820 display: table-cell;
1820 display: table-cell;
1821 text-align: left;
1821 text-align: left;
1822 }
1822 }
1823
1823
1824 .desc{
1824 .desc{
1825 width: 1163px;
1825 width: 1163px;
1826 }
1826 }
1827
1827
1828 .delete-notifications, .read-notifications{
1828 .delete-notifications, .read-notifications{
1829 width: 35px;
1829 width: 35px;
1830 min-width: 35px; //fixes when only one button is displayed
1830 min-width: 35px; //fixes when only one button is displayed
1831 }
1831 }
1832 }
1832 }
1833
1833
1834 .notification-body {
1834 .notification-body {
1835 .markdown-block,
1835 .markdown-block,
1836 .rst-block {
1836 .rst-block {
1837 padding: @padding 0;
1837 padding: @padding 0;
1838 }
1838 }
1839
1839
1840 .notification-subject {
1840 .notification-subject {
1841 padding: @textmargin 0;
1841 padding: @textmargin 0;
1842 border-bottom: @border-thickness solid @border-default-color;
1842 border-bottom: @border-thickness solid @border-default-color;
1843 }
1843 }
1844 }
1844 }
1845
1845
1846
1846
1847 .notifications_buttons{
1847 .notifications_buttons{
1848 float: right;
1848 float: right;
1849 }
1849 }
1850
1850
1851 #notification-status{
1851 #notification-status{
1852 display: inline;
1852 display: inline;
1853 }
1853 }
1854
1854
1855 // Repositories
1855 // Repositories
1856
1856
1857 #summary.fields{
1857 #summary.fields{
1858 display: table;
1858 display: table;
1859
1859
1860 .field{
1860 .field{
1861 display: table-row;
1861 display: table-row;
1862
1862
1863 .label-summary{
1863 .label-summary{
1864 display: table-cell;
1864 display: table-cell;
1865 min-width: @label-summary-minwidth;
1865 min-width: @label-summary-minwidth;
1866 padding-top: @padding/2;
1866 padding-top: @padding/2;
1867 padding-bottom: @padding/2;
1867 padding-bottom: @padding/2;
1868 padding-right: @padding/2;
1868 padding-right: @padding/2;
1869 }
1869 }
1870
1870
1871 .input{
1871 .input{
1872 display: table-cell;
1872 display: table-cell;
1873 padding: @padding/2;
1873 padding: @padding/2;
1874
1874
1875 input{
1875 input{
1876 min-width: 29em;
1876 min-width: 29em;
1877 padding: @padding/4;
1877 padding: @padding/4;
1878 }
1878 }
1879 }
1879 }
1880 .statistics, .downloads{
1880 .statistics, .downloads{
1881 .disabled{
1881 .disabled{
1882 color: @grey4;
1882 color: @grey4;
1883 }
1883 }
1884 }
1884 }
1885 }
1885 }
1886 }
1886 }
1887
1887
1888 #summary{
1888 #summary{
1889 width: 70%;
1889 width: 70%;
1890 }
1890 }
1891
1891
1892
1892
1893 // Journal
1893 // Journal
1894 .journal.title {
1894 .journal.title {
1895 h5 {
1895 h5 {
1896 float: left;
1896 float: left;
1897 margin: 0;
1897 margin: 0;
1898 width: 70%;
1898 width: 70%;
1899 }
1899 }
1900
1900
1901 ul {
1901 ul {
1902 float: right;
1902 float: right;
1903 display: inline-block;
1903 display: inline-block;
1904 margin: 0;
1904 margin: 0;
1905 width: 30%;
1905 width: 30%;
1906 text-align: right;
1906 text-align: right;
1907
1907
1908 li {
1908 li {
1909 display: inline;
1909 display: inline;
1910 font-size: @journal-fontsize;
1910 font-size: @journal-fontsize;
1911 line-height: 1em;
1911 line-height: 1em;
1912
1912
1913 &:before { content: none; }
1913 &:before { content: none; }
1914 }
1914 }
1915 }
1915 }
1916 }
1916 }
1917
1917
1918 .filterexample {
1918 .filterexample {
1919 position: absolute;
1919 position: absolute;
1920 top: 95px;
1920 top: 95px;
1921 left: @contentpadding;
1921 left: @contentpadding;
1922 color: @rcblue;
1922 color: @rcblue;
1923 font-size: 11px;
1923 font-size: 11px;
1924 font-family: @text-regular;
1924 font-family: @text-regular;
1925 cursor: help;
1925 cursor: help;
1926
1926
1927 &:hover {
1927 &:hover {
1928 color: @rcdarkblue;
1928 color: @rcdarkblue;
1929 }
1929 }
1930
1930
1931 @media (max-width:768px) {
1931 @media (max-width:768px) {
1932 position: relative;
1932 position: relative;
1933 top: auto;
1933 top: auto;
1934 left: auto;
1934 left: auto;
1935 display: block;
1935 display: block;
1936 }
1936 }
1937 }
1937 }
1938
1938
1939
1939
1940 #journal{
1940 #journal{
1941 margin-bottom: @space;
1941 margin-bottom: @space;
1942
1942
1943 .journal_day{
1943 .journal_day{
1944 margin-bottom: @textmargin/2;
1944 margin-bottom: @textmargin/2;
1945 padding-bottom: @textmargin/2;
1945 padding-bottom: @textmargin/2;
1946 font-size: @journal-fontsize;
1946 font-size: @journal-fontsize;
1947 border-bottom: @border-thickness solid @border-default-color;
1947 border-bottom: @border-thickness solid @border-default-color;
1948 }
1948 }
1949
1949
1950 .journal_container{
1950 .journal_container{
1951 margin-bottom: @space;
1951 margin-bottom: @space;
1952
1952
1953 .journal_user{
1953 .journal_user{
1954 display: inline-block;
1954 display: inline-block;
1955 }
1955 }
1956 .journal_action_container{
1956 .journal_action_container{
1957 display: block;
1957 display: block;
1958 margin-top: @textmargin;
1958 margin-top: @textmargin;
1959
1959
1960 div{
1960 div{
1961 display: inline;
1961 display: inline;
1962 }
1962 }
1963
1963
1964 div.journal_action_params{
1964 div.journal_action_params{
1965 display: block;
1965 display: block;
1966 }
1966 }
1967
1967
1968 div.journal_repo:after{
1968 div.journal_repo:after{
1969 content: "\A";
1969 content: "\A";
1970 white-space: pre;
1970 white-space: pre;
1971 }
1971 }
1972
1972
1973 div.date{
1973 div.date{
1974 display: block;
1974 display: block;
1975 margin-bottom: @textmargin;
1975 margin-bottom: @textmargin;
1976 }
1976 }
1977 }
1977 }
1978 }
1978 }
1979 }
1979 }
1980
1980
1981 // Files
1981 // Files
1982 .edit-file-title {
1982 .edit-file-title {
1983 border-bottom: @border-thickness solid @border-default-color;
1983 border-bottom: @border-thickness solid @border-default-color;
1984
1984
1985 .breadcrumbs {
1985 .breadcrumbs {
1986 margin-bottom: 0;
1986 margin-bottom: 0;
1987 }
1987 }
1988 }
1988 }
1989
1989
1990 .edit-file-fieldset {
1990 .edit-file-fieldset {
1991 margin-top: @sidebarpadding;
1991 margin-top: @sidebarpadding;
1992
1992
1993 .fieldset {
1993 .fieldset {
1994 .left-label {
1994 .left-label {
1995 width: 13%;
1995 width: 13%;
1996 }
1996 }
1997 .right-content {
1997 .right-content {
1998 width: 87%;
1998 width: 87%;
1999 max-width: 100%;
1999 max-width: 100%;
2000 }
2000 }
2001 .filename-label {
2001 .filename-label {
2002 margin-top: 13px;
2002 margin-top: 13px;
2003 }
2003 }
2004 .commit-message-label {
2004 .commit-message-label {
2005 margin-top: 4px;
2005 margin-top: 4px;
2006 }
2006 }
2007 .file-upload-input {
2007 .file-upload-input {
2008 input {
2008 input {
2009 display: none;
2009 display: none;
2010 }
2010 }
2011 margin-top: 10px;
2012 }
2013 .file-upload-label {
2014 margin-top: 10px;
2011 }
2015 }
2012 p {
2016 p {
2013 margin-top: 5px;
2017 margin-top: 5px;
2014 }
2018 }
2015
2019
2016 }
2020 }
2017 .custom-path-link {
2021 .custom-path-link {
2018 margin-left: 5px;
2022 margin-left: 5px;
2019 }
2023 }
2020 #commit {
2024 #commit {
2021 resize: vertical;
2025 resize: vertical;
2022 }
2026 }
2023 }
2027 }
2024
2028
2025 .delete-file-preview {
2029 .delete-file-preview {
2026 max-height: 250px;
2030 max-height: 250px;
2027 }
2031 }
2028
2032
2029 .new-file,
2033 .new-file,
2030 #filter_activate,
2034 #filter_activate,
2031 #filter_deactivate {
2035 #filter_deactivate {
2032 float: left;
2036 float: left;
2033 margin: 0 0 0 15px;
2037 margin: 0 0 0 15px;
2034 }
2038 }
2035
2039
2036 h3.files_location{
2040 h3.files_location{
2037 line-height: 2.4em;
2041 line-height: 2.4em;
2038 }
2042 }
2039
2043
2040 .browser-nav {
2044 .browser-nav {
2041 display: table;
2045 display: table;
2042 margin-bottom: @space;
2046 margin-bottom: @space;
2043
2047
2044
2048
2045 .info_box {
2049 .info_box {
2046 display: inline-table;
2050 display: inline-table;
2047 height: 2.5em;
2051 height: 2.5em;
2048
2052
2049 .browser-cur-rev, .info_box_elem {
2053 .browser-cur-rev, .info_box_elem {
2050 display: table-cell;
2054 display: table-cell;
2051 vertical-align: middle;
2055 vertical-align: middle;
2052 }
2056 }
2053
2057
2054 .info_box_elem {
2058 .info_box_elem {
2055 border-top: @border-thickness solid @rcblue;
2059 border-top: @border-thickness solid @rcblue;
2056 border-bottom: @border-thickness solid @rcblue;
2060 border-bottom: @border-thickness solid @rcblue;
2057
2061
2058 #at_rev, a {
2062 #at_rev, a {
2059 padding: 0.6em 0.9em;
2063 padding: 0.6em 0.9em;
2060 margin: 0;
2064 margin: 0;
2061 .box-shadow(none);
2065 .box-shadow(none);
2062 border: 0;
2066 border: 0;
2063 height: 12px;
2067 height: 12px;
2064 }
2068 }
2065
2069
2066 input#at_rev {
2070 input#at_rev {
2067 max-width: 50px;
2071 max-width: 50px;
2068 text-align: right;
2072 text-align: right;
2069 }
2073 }
2070
2074
2071 &.previous {
2075 &.previous {
2072 border: @border-thickness solid @rcblue;
2076 border: @border-thickness solid @rcblue;
2073 .disabled {
2077 .disabled {
2074 color: @grey4;
2078 color: @grey4;
2075 cursor: not-allowed;
2079 cursor: not-allowed;
2076 }
2080 }
2077 }
2081 }
2078
2082
2079 &.next {
2083 &.next {
2080 border: @border-thickness solid @rcblue;
2084 border: @border-thickness solid @rcblue;
2081 .disabled {
2085 .disabled {
2082 color: @grey4;
2086 color: @grey4;
2083 cursor: not-allowed;
2087 cursor: not-allowed;
2084 }
2088 }
2085 }
2089 }
2086 }
2090 }
2087
2091
2088 .browser-cur-rev {
2092 .browser-cur-rev {
2089
2093
2090 span{
2094 span{
2091 margin: 0;
2095 margin: 0;
2092 color: @rcblue;
2096 color: @rcblue;
2093 height: 12px;
2097 height: 12px;
2094 display: inline-block;
2098 display: inline-block;
2095 padding: 0.7em 1em ;
2099 padding: 0.7em 1em ;
2096 border: @border-thickness solid @rcblue;
2100 border: @border-thickness solid @rcblue;
2097 margin-right: @padding;
2101 margin-right: @padding;
2098 }
2102 }
2099 }
2103 }
2100 }
2104 }
2101
2105
2102 .search_activate {
2106 .search_activate {
2103 display: table-cell;
2107 display: table-cell;
2104 vertical-align: middle;
2108 vertical-align: middle;
2105
2109
2106 input, label{
2110 input, label{
2107 margin: 0;
2111 margin: 0;
2108 padding: 0;
2112 padding: 0;
2109 }
2113 }
2110
2114
2111 input{
2115 input{
2112 margin-left: @textmargin;
2116 margin-left: @textmargin;
2113 }
2117 }
2114
2118
2115 }
2119 }
2116 }
2120 }
2117
2121
2118 .browser-cur-rev{
2122 .browser-cur-rev{
2119 margin-bottom: @textmargin;
2123 margin-bottom: @textmargin;
2120 }
2124 }
2121
2125
2122 #node_filter_box_loading{
2126 #node_filter_box_loading{
2123 .info_text;
2127 .info_text;
2124 }
2128 }
2125
2129
2126 .browser-search {
2130 .browser-search {
2127 margin: -25px 0px 5px 0px;
2131 margin: -25px 0px 5px 0px;
2128 }
2132 }
2129
2133
2130 .node-filter {
2134 .node-filter {
2131 font-size: @repo-title-fontsize;
2135 font-size: @repo-title-fontsize;
2132 padding: 4px 0px 0px 0px;
2136 padding: 4px 0px 0px 0px;
2133
2137
2134 .node-filter-path {
2138 .node-filter-path {
2135 float: left;
2139 float: left;
2136 color: @grey4;
2140 color: @grey4;
2137 }
2141 }
2138 .node-filter-input {
2142 .node-filter-input {
2139 float: left;
2143 float: left;
2140 margin: -2px 0px 0px 2px;
2144 margin: -2px 0px 0px 2px;
2141 input {
2145 input {
2142 padding: 2px;
2146 padding: 2px;
2143 border: none;
2147 border: none;
2144 font-size: @repo-title-fontsize;
2148 font-size: @repo-title-fontsize;
2145 }
2149 }
2146 }
2150 }
2147 }
2151 }
2148
2152
2149
2153
2150 .browser-result{
2154 .browser-result{
2151 td a{
2155 td a{
2152 margin-left: 0.5em;
2156 margin-left: 0.5em;
2153 display: inline-block;
2157 display: inline-block;
2154
2158
2155 em{
2159 em{
2156 font-family: @text-bold;
2160 font-family: @text-bold;
2157 }
2161 }
2158 }
2162 }
2159 }
2163 }
2160
2164
2161 .browser-highlight{
2165 .browser-highlight{
2162 background-color: @grey5-alpha;
2166 background-color: @grey5-alpha;
2163 }
2167 }
2164
2168
2165
2169
2166 // Search
2170 // Search
2167
2171
2168 .search-form{
2172 .search-form{
2169 #q {
2173 #q {
2170 width: @search-form-width;
2174 width: @search-form-width;
2171 }
2175 }
2172 .fields{
2176 .fields{
2173 margin: 0 0 @space;
2177 margin: 0 0 @space;
2174 }
2178 }
2175
2179
2176 label{
2180 label{
2177 display: inline-block;
2181 display: inline-block;
2178 margin-right: @textmargin;
2182 margin-right: @textmargin;
2179 padding-top: 0.25em;
2183 padding-top: 0.25em;
2180 }
2184 }
2181
2185
2182
2186
2183 .results{
2187 .results{
2184 clear: both;
2188 clear: both;
2185 margin: 0 0 @padding;
2189 margin: 0 0 @padding;
2186 }
2190 }
2187 }
2191 }
2188
2192
2189 div.search-feedback-items {
2193 div.search-feedback-items {
2190 display: inline-block;
2194 display: inline-block;
2191 padding:0px 0px 0px 96px;
2195 padding:0px 0px 0px 96px;
2192 }
2196 }
2193
2197
2194 div.search-code-body {
2198 div.search-code-body {
2195 background-color: #ffffff; padding: 5px 0 5px 10px;
2199 background-color: #ffffff; padding: 5px 0 5px 10px;
2196 pre {
2200 pre {
2197 .match { background-color: #faffa6;}
2201 .match { background-color: #faffa6;}
2198 .break { display: block; width: 100%; background-color: #DDE7EF; color: #747474; }
2202 .break { display: block; width: 100%; background-color: #DDE7EF; color: #747474; }
2199 }
2203 }
2200 }
2204 }
2201
2205
2202 .expand_commit.search {
2206 .expand_commit.search {
2203 .show_more.open {
2207 .show_more.open {
2204 height: auto;
2208 height: auto;
2205 max-height: none;
2209 max-height: none;
2206 }
2210 }
2207 }
2211 }
2208
2212
2209 .search-results {
2213 .search-results {
2210
2214
2211 h2 {
2215 h2 {
2212 margin-bottom: 0;
2216 margin-bottom: 0;
2213 }
2217 }
2214 .codeblock {
2218 .codeblock {
2215 border: none;
2219 border: none;
2216 background: transparent;
2220 background: transparent;
2217 }
2221 }
2218
2222
2219 .codeblock-header {
2223 .codeblock-header {
2220 border: none;
2224 border: none;
2221 background: transparent;
2225 background: transparent;
2222 }
2226 }
2223
2227
2224 .code-body {
2228 .code-body {
2225 border: @border-thickness solid @border-default-color;
2229 border: @border-thickness solid @border-default-color;
2226 .border-radius(@border-radius);
2230 .border-radius(@border-radius);
2227 }
2231 }
2228
2232
2229 .td-commit {
2233 .td-commit {
2230 &:extend(pre);
2234 &:extend(pre);
2231 border-bottom: @border-thickness solid @border-default-color;
2235 border-bottom: @border-thickness solid @border-default-color;
2232 }
2236 }
2233
2237
2234 .message {
2238 .message {
2235 height: auto;
2239 height: auto;
2236 max-width: 350px;
2240 max-width: 350px;
2237 white-space: normal;
2241 white-space: normal;
2238 text-overflow: initial;
2242 text-overflow: initial;
2239 overflow: visible;
2243 overflow: visible;
2240
2244
2241 .match { background-color: #faffa6;}
2245 .match { background-color: #faffa6;}
2242 .break { background-color: #DDE7EF; width: 100%; color: #747474; display: block; }
2246 .break { background-color: #DDE7EF; width: 100%; color: #747474; display: block; }
2243 }
2247 }
2244
2248
2245 }
2249 }
2246
2250
2247 table.rctable td.td-search-results div {
2251 table.rctable td.td-search-results div {
2248 max-width: 100%;
2252 max-width: 100%;
2249 }
2253 }
2250
2254
2251 #tip-box, .tip-box{
2255 #tip-box, .tip-box{
2252 padding: @menupadding/2;
2256 padding: @menupadding/2;
2253 display: block;
2257 display: block;
2254 border: @border-thickness solid @border-highlight-color;
2258 border: @border-thickness solid @border-highlight-color;
2255 .border-radius(@border-radius);
2259 .border-radius(@border-radius);
2256 background-color: white;
2260 background-color: white;
2257 z-index: 99;
2261 z-index: 99;
2258 white-space: pre-wrap;
2262 white-space: pre-wrap;
2259 }
2263 }
2260
2264
2261 #linktt {
2265 #linktt {
2262 width: 79px;
2266 width: 79px;
2263 }
2267 }
2264
2268
2265 #help_kb .modal-content{
2269 #help_kb .modal-content{
2266 max-width: 750px;
2270 max-width: 750px;
2267 margin: 10% auto;
2271 margin: 10% auto;
2268
2272
2269 table{
2273 table{
2270 td,th{
2274 td,th{
2271 border-bottom: none;
2275 border-bottom: none;
2272 line-height: 2.5em;
2276 line-height: 2.5em;
2273 }
2277 }
2274 th{
2278 th{
2275 padding-bottom: @textmargin/2;
2279 padding-bottom: @textmargin/2;
2276 }
2280 }
2277 td.keys{
2281 td.keys{
2278 text-align: center;
2282 text-align: center;
2279 }
2283 }
2280 }
2284 }
2281
2285
2282 .block-left{
2286 .block-left{
2283 width: 45%;
2287 width: 45%;
2284 margin-right: 5%;
2288 margin-right: 5%;
2285 }
2289 }
2286 .modal-footer{
2290 .modal-footer{
2287 clear: both;
2291 clear: both;
2288 }
2292 }
2289 .key.tag{
2293 .key.tag{
2290 padding: 0.5em;
2294 padding: 0.5em;
2291 background-color: @rcblue;
2295 background-color: @rcblue;
2292 color: white;
2296 color: white;
2293 border-color: @rcblue;
2297 border-color: @rcblue;
2294 .box-shadow(none);
2298 .box-shadow(none);
2295 }
2299 }
2296 }
2300 }
2297
2301
2298
2302
2299
2303
2300 //--- IMPORTS FOR REFACTORED STYLES ------------------//
2304 //--- IMPORTS FOR REFACTORED STYLES ------------------//
2301
2305
2302 @import 'statistics-graph';
2306 @import 'statistics-graph';
2303 @import 'tables';
2307 @import 'tables';
2304 @import 'forms';
2308 @import 'forms';
2305 @import 'diff';
2309 @import 'diff';
2306 @import 'summary';
2310 @import 'summary';
2307 @import 'navigation';
2311 @import 'navigation';
2308
2312
2309 //--- SHOW/HIDE SECTIONS --//
2313 //--- SHOW/HIDE SECTIONS --//
2310
2314
2311 .btn-collapse {
2315 .btn-collapse {
2312 float: right;
2316 float: right;
2313 text-align: right;
2317 text-align: right;
2314 font-family: @text-light;
2318 font-family: @text-light;
2315 font-size: @basefontsize;
2319 font-size: @basefontsize;
2316 cursor: pointer;
2320 cursor: pointer;
2317 border: none;
2321 border: none;
2318 color: @rcblue;
2322 color: @rcblue;
2319 }
2323 }
2320
2324
2321 table.rctable,
2325 table.rctable,
2322 table.dataTable {
2326 table.dataTable {
2323 .btn-collapse {
2327 .btn-collapse {
2324 float: right;
2328 float: right;
2325 text-align: right;
2329 text-align: right;
2326 }
2330 }
2327 }
2331 }
2328
2332
2329
2333
2330 // TODO: johbo: Fix for IE10, this avoids that we see a border
2334 // TODO: johbo: Fix for IE10, this avoids that we see a border
2331 // and padding around checkboxes and radio boxes. Move to the right place,
2335 // and padding around checkboxes and radio boxes. Move to the right place,
2332 // or better: Remove this once we did the form refactoring.
2336 // or better: Remove this once we did the form refactoring.
2333 input[type=checkbox],
2337 input[type=checkbox],
2334 input[type=radio] {
2338 input[type=radio] {
2335 padding: 0;
2339 padding: 0;
2336 border: none;
2340 border: none;
2337 }
2341 }
2338
2342
2339 .toggle-ajax-spinner{
2343 .toggle-ajax-spinner{
2340 height: 16px;
2344 height: 16px;
2341 width: 16px;
2345 width: 16px;
2342 }
2346 }
@@ -1,232 +1,236 b''
1 <%inherit file="/base/base.mako"/>
1 <%inherit file="/base/base.mako"/>
2
2
3 <%def name="title()">
3 <%def name="title()">
4 ${_('%s Files Add') % c.repo_name}
4 ${_('%s Files Add') % c.repo_name}
5 %if c.rhodecode_name:
5 %if c.rhodecode_name:
6 &middot; ${h.branding(c.rhodecode_name)}
6 &middot; ${h.branding(c.rhodecode_name)}
7 %endif
7 %endif
8 </%def>
8 </%def>
9
9
10 <%def name="menu_bar_nav()">
10 <%def name="menu_bar_nav()">
11 ${self.menu_items(active='repositories')}
11 ${self.menu_items(active='repositories')}
12 </%def>
12 </%def>
13
13
14 <%def name="breadcrumbs_links()">
14 <%def name="breadcrumbs_links()">
15 ${_('Add new file')} @ ${h.show_id(c.commit)}
15 ${_('Add new file')} @ ${h.show_id(c.commit)}
16 </%def>
16 </%def>
17
17
18 <%def name="menu_bar_subnav()">
18 <%def name="menu_bar_subnav()">
19 ${self.repo_menu(active='files')}
19 ${self.repo_menu(active='files')}
20 </%def>
20 </%def>
21
21
22 <%def name="main()">
22 <%def name="main()">
23 <div class="box">
23 <div class="box">
24 <div class="title">
24 <div class="title">
25 ${self.repo_page_title(c.rhodecode_db_repo)}
25 ${self.repo_page_title(c.rhodecode_db_repo)}
26 </div>
26 </div>
27 <div class="edit-file-title">
27 <div class="edit-file-title">
28 ${self.breadcrumbs()}
28 ${self.breadcrumbs()}
29 </div>
29 </div>
30 ${h.secure_form(h.url.current(),method='post',id='eform',enctype="multipart/form-data", class_="form-horizontal")}
30 ${h.secure_form(h.url.current(),method='post',id='eform',enctype="multipart/form-data", class_="form-horizontal")}
31 <div class="edit-file-fieldset">
31 <div class="edit-file-fieldset">
32 <div class="fieldset">
32 <div class="fieldset">
33 <div id="destination-label" class="left-label">
33 <div id="destination-label" class="left-label">
34 ${_('Path')}:
34 ${_('Path')}:
35 </div>
35 </div>
36 <div class="right-content">
36 <div class="right-content">
37 <div id="specify-custom-path-container">
37 <div id="specify-custom-path-container">
38 <span id="path-breadcrumbs">${h.files_breadcrumbs(c.repo_name,c.commit.raw_id,c.f_path)}</span>
38 <span id="path-breadcrumbs">${h.files_breadcrumbs(c.repo_name,c.commit.raw_id,c.f_path)}</span>
39 <a class="custom-path-link" id="specify-custom-path" href="#">${_('Specify Custom Path')}</a>
39 <a class="custom-path-link" id="specify-custom-path" href="#">${_('Specify Custom Path')}</a>
40 </div>
40 </div>
41 <div id="remove-custom-path-container" style="display: none;">
41 <div id="remove-custom-path-container" style="display: none;">
42 ${c.repo_name}/
42 ${c.repo_name}/
43 <input type="input-small" value="${c.f_path}" size="46" name="location" id="location">
43 <input type="input-small" value="${c.f_path}" size="46" name="location" id="location">
44 <a class="custom-path-link" id="remove-custom-path" href="#">${_('Remove Custom Path')}</a>
44 <a class="custom-path-link" id="remove-custom-path" href="#">${_('Remove Custom Path')}</a>
45 </div>
45 </div>
46 </div>
46 </div>
47 </div>
47 </div>
48 <div id="filename_container" class="fieldset">
48 <div id="filename_container" class="fieldset">
49 <div class="filename-label left-label">
49 <div class="filename-label left-label">
50 ${_('Filename')}:
50 ${_('Filename')}:
51 </div>
51 </div>
52 <div class="right-content">
52 <div class="right-content">
53 <input class="input-small" type="text" value="" size="46" name="filename" id="filename">
53 <input class="input-small" type="text" value="" size="46" name="filename" id="filename">
54 <p>${_('or')} <a id="upload_file_enable" href="#">${_('Upload File')}</a></p>
54 <p>${_('or')} <a id="upload_file_enable" href="#">${_('Upload File')}</a></p>
55 </div>
55 </div>
56 </div>
56 </div>
57 <div id="upload_file_container" class="fieldset" style="display: none;">
57 <div id="upload_file_container" class="fieldset" style="display: none;">
58 <div class="filename-label left-label">
58 <div class="filename-label left-label">
59 ${_('Filename')}:
60 </div>
61 <div class="right-content">
62 <input class="input-small" type="text" value="" size="46" name="filename" id="filename_upload" placeholder="${_('No file selected')}">
63 </div>
64 <div class="filename-label left-label file-upload-label">
59 ${_('Upload file')}:
65 ${_('Upload file')}:
60 </div>
66 </div>
61 <div class="right-content file-upload-input">
67 <div class="right-content file-upload-input">
62 <label for="upload_file" class="btn btn-default">Browse</label>
68 <label for="upload_file" class="btn btn-default">Browse</label>
63 <span id="selected-file">${_('No file selected')}</span>
69
64 <input type="file" name="upload_file" id="upload_file">
70 <input type="file" name="upload_file" id="upload_file">
65 <p>${_('or')} <a id="file_enable" href="#">${_('Create New File')}</a></p>
71 <p>${_('or')} <a id="file_enable" href="#">${_('Create New File')}</a></p>
66 </div>
72 </div>
67 </div>
73 </div>
68 </div>
74 </div>
69 <div class="table">
75 <div class="table">
70 <div id="files_data">
76 <div id="files_data">
71 <div id="codeblock" class="codeblock">
77 <div id="codeblock" class="codeblock">
72 <div class="code-header form" id="set_mode_header">
78 <div class="code-header form" id="set_mode_header">
73 <div class="fields">
79 <div class="fields">
74 ${h.dropdownmenu('set_mode','plain',[('plain',_('plain'))],enable_filter=True)}
80 ${h.dropdownmenu('set_mode','plain',[('plain',_('plain'))],enable_filter=True)}
75 <label for="line_wrap">${_('line wraps')}</label>
81 <label for="line_wrap">${_('line wraps')}</label>
76 ${h.dropdownmenu('line_wrap', 'off', [('on', _('on')), ('off', _('off')),])}
82 ${h.dropdownmenu('line_wrap', 'off', [('on', _('on')), ('off', _('off')),])}
77
83
78 <div id="render_preview" class="btn btn-small preview hidden" >${_('Preview')}</div>
84 <div id="render_preview" class="btn btn-small preview hidden" >${_('Preview')}</div>
79 </div>
85 </div>
80 </div>
86 </div>
81 <div id="editor_container">
87 <div id="editor_container">
82 <pre id="editor_pre"></pre>
88 <pre id="editor_pre"></pre>
83 <textarea id="editor" name="content" ></textarea>
89 <textarea id="editor" name="content" ></textarea>
84 <div id="editor_preview"></div>
90 <div id="editor_preview"></div>
85 </div>
91 </div>
86 </div>
92 </div>
87 </div>
93 </div>
88 </div>
94 </div>
89
95
90 <div class="edit-file-fieldset">
96 <div class="edit-file-fieldset">
91 <div class="fieldset">
97 <div class="fieldset">
92 <div id="commit-message-label" class="commit-message-label left-label">
98 <div id="commit-message-label" class="commit-message-label left-label">
93 ${_('Commit Message')}:
99 ${_('Commit Message')}:
94 </div>
100 </div>
95 <div class="right-content">
101 <div class="right-content">
96 <div class="message">
102 <div class="message">
97 <textarea id="commit" name="message" placeholder="${c.default_message}"></textarea>
103 <textarea id="commit" name="message" placeholder="${c.default_message}"></textarea>
98 </div>
104 </div>
99 </div>
105 </div>
100 </div>
106 </div>
101 <div class="pull-right">
107 <div class="pull-right">
102 ${h.reset('reset',_('Cancel'),class_="btn btn-small")}
108 ${h.reset('reset',_('Cancel'),class_="btn btn-small")}
103 ${h.submit('commit_btn',_('Commit changes'),class_="btn btn-small btn-success")}
109 ${h.submit('commit_btn',_('Commit changes'),class_="btn btn-small btn-success")}
104 </div>
110 </div>
105 </div>
111 </div>
106 ${h.end_form()}
112 ${h.end_form()}
107 </div>
113 </div>
108 <script type="text/javascript">
114 <script type="text/javascript">
109
115
110 $('#commit_btn').on('click', function() {
116 $('#commit_btn').on('click', function() {
111 var button = $(this);
117 var button = $(this);
112 if (button.hasClass('clicked')) {
118 if (button.hasClass('clicked')) {
113 button.attr('disabled', true);
119 button.attr('disabled', true);
114 } else {
120 } else {
115 button.addClass('clicked');
121 button.addClass('clicked');
116 }
122 }
117 });
123 });
118
124
119 $('#specify-custom-path').on('click', function(e){
125 $('#specify-custom-path').on('click', function(e){
120 e.preventDefault();
126 e.preventDefault();
121 $('#specify-custom-path-container').hide();
127 $('#specify-custom-path-container').hide();
122 $('#remove-custom-path-container').show();
128 $('#remove-custom-path-container').show();
123 $('#destination-label').css('margin-top', '13px');
129 $('#destination-label').css('margin-top', '13px');
124 });
130 });
125
131
126 $('#remove-custom-path').on('click', function(e){
132 $('#remove-custom-path').on('click', function(e){
127 e.preventDefault();
133 e.preventDefault();
128 $('#specify-custom-path-container').show();
134 $('#specify-custom-path-container').show();
129 $('#remove-custom-path-container').hide();
135 $('#remove-custom-path-container').hide();
130 $('#location').val('${c.f_path}');
136 $('#location').val('${c.f_path}');
131 $('#destination-label').css('margin-top', '0');
137 $('#destination-label').css('margin-top', '0');
132 });
138 });
133
139
134 var hide_upload = function(){
140 var hide_upload = function(){
135 $('#files_data').show();
141 $('#files_data').show();
136 $('#upload_file_container').hide();
142 $('#upload_file_container').hide();
137 $('#filename_container').show();
143 $('#filename_container').show();
138 };
144 };
139
145
140 $('#file_enable').on('click', function(e){
146 $('#file_enable').on('click', function(e){
141 e.preventDefault();
147 e.preventDefault();
142 hide_upload();
148 hide_upload();
143 });
149 });
144
150
145 $('#upload_file_enable').on('click', function(e){
151 $('#upload_file_enable').on('click', function(e){
146 e.preventDefault();
152 e.preventDefault();
147 $('#files_data').hide();
153 $('#files_data').hide();
148 $('#upload_file_container').show();
154 $('#upload_file_container').show();
149 $('#filename_container').hide();
155 $('#filename_container').hide();
150 if (detectIE() && detectIE() <= 9) {
156 if (detectIE() && detectIE() <= 9) {
151 $('#upload_file_container .file-upload-input label').hide();
157 $('#upload_file_container .file-upload-input label').hide();
152 $('#upload_file_container .file-upload-input span').hide();
158 $('#upload_file_container .file-upload-input span').hide();
153 $('#upload_file_container .file-upload-input input').show();
159 $('#upload_file_container .file-upload-input input').show();
154 }
160 }
155 });
161 });
156
162
157 $('#upload_file').on('change', function() {
163 $('#upload_file').on('change', function() {
158 if (detectIE() && detectIE() <= 9) {
159 if (this.files && this.files[0]) {
164 if (this.files && this.files[0]) {
160 $('#selected-file').html(this.files[0].name);
165 $('#filename_upload').val(this.files[0].name);
161 }
162 }
166 }
163 });
167 });
164
168
165 hide_upload();
169 hide_upload();
166
170
167 var renderer = "";
171 var renderer = "";
168 var reset_url = "${h.url('files_home',repo_name=c.repo_name,revision=c.commit.raw_id,f_path=c.f_path)}";
172 var reset_url = "${h.url('files_home',repo_name=c.repo_name,revision=c.commit.raw_id,f_path=c.f_path)}";
169 var myCodeMirror = initCodeMirror('editor', reset_url, false);
173 var myCodeMirror = initCodeMirror('editor', reset_url, false);
170
174
171 var modes_select = $('#set_mode');
175 var modes_select = $('#set_mode');
172 fillCodeMirrorOptions(modes_select);
176 fillCodeMirrorOptions(modes_select);
173
177
174 var filename_selector = '#filename';
178 var filename_selector = '#filename';
175 var callback = function(filename, mimetype, mode){
179 var callback = function(filename, mimetype, mode){
176 CodeMirrorPreviewEnable(mode);
180 CodeMirrorPreviewEnable(mode);
177 };
181 };
178 // on change of select field set mode
182 // on change of select field set mode
179 setCodeMirrorModeFromSelect(
183 setCodeMirrorModeFromSelect(
180 modes_select, filename_selector, myCodeMirror, callback);
184 modes_select, filename_selector, myCodeMirror, callback);
181
185
182 // on entering the new filename set mode, from given extension
186 // on entering the new filename set mode, from given extension
183 setCodeMirrorModeFromInput(
187 setCodeMirrorModeFromInput(
184 modes_select, filename_selector, myCodeMirror, callback);
188 modes_select, filename_selector, myCodeMirror, callback);
185
189
186 // if the file is renderable set line wraps automatically
190 // if the file is renderable set line wraps automatically
187 if (renderer !== ""){
191 if (renderer !== ""){
188 var line_wrap = 'on';
192 var line_wrap = 'on';
189 $($('#line_wrap option[value="'+line_wrap+'"]')[0]).attr("selected", "selected");
193 $($('#line_wrap option[value="'+line_wrap+'"]')[0]).attr("selected", "selected");
190 setCodeMirrorLineWrap(myCodeMirror, true);
194 setCodeMirrorLineWrap(myCodeMirror, true);
191 }
195 }
192
196
193 // on select line wraps change the editor
197 // on select line wraps change the editor
194 $('#line_wrap').on('change', function(e){
198 $('#line_wrap').on('change', function(e){
195 var selected = e.currentTarget;
199 var selected = e.currentTarget;
196 var line_wraps = {'on': true, 'off': false}[selected.value];
200 var line_wraps = {'on': true, 'off': false}[selected.value];
197 setCodeMirrorLineWrap(myCodeMirror, line_wraps)
201 setCodeMirrorLineWrap(myCodeMirror, line_wraps)
198 });
202 });
199
203
200 // render preview/edit button
204 // render preview/edit button
201 $('#render_preview').on('click', function(e){
205 $('#render_preview').on('click', function(e){
202 if($(this).hasClass('preview')){
206 if($(this).hasClass('preview')){
203 $(this).removeClass('preview');
207 $(this).removeClass('preview');
204 $(this).html("${_('Edit')}");
208 $(this).html("${_('Edit')}");
205 $('#editor_preview').show();
209 $('#editor_preview').show();
206 $(myCodeMirror.getWrapperElement()).hide();
210 $(myCodeMirror.getWrapperElement()).hide();
207
211
208 var possible_renderer = {
212 var possible_renderer = {
209 'rst':'rst',
213 'rst':'rst',
210 'markdown':'markdown',
214 'markdown':'markdown',
211 'gfm': 'markdown'}[myCodeMirror.getMode().name];
215 'gfm': 'markdown'}[myCodeMirror.getMode().name];
212 var _text = myCodeMirror.getValue();
216 var _text = myCodeMirror.getValue();
213 var _renderer = possible_renderer || DEFAULT_RENDERER;
217 var _renderer = possible_renderer || DEFAULT_RENDERER;
214 var post_data = {'text': _text, 'renderer': _renderer, 'csrf_token': CSRF_TOKEN};
218 var post_data = {'text': _text, 'renderer': _renderer, 'csrf_token': CSRF_TOKEN};
215 $('#editor_preview').html(_gettext('Loading ...'));
219 $('#editor_preview').html(_gettext('Loading ...'));
216 var url = pyroutes.url('changeset_comment_preview', {'repo_name': '${c.repo_name}'});
220 var url = pyroutes.url('changeset_comment_preview', {'repo_name': '${c.repo_name}'});
217
221
218 ajaxPOST(url, post_data, function(o){
222 ajaxPOST(url, post_data, function(o){
219 $('#editor_preview').html(o);
223 $('#editor_preview').html(o);
220 })
224 })
221 }
225 }
222 else{
226 else{
223 $(this).addClass('preview');
227 $(this).addClass('preview');
224 $(this).html("${_('Preview')}");
228 $(this).html("${_('Preview')}");
225 $('#editor_preview').hide();
229 $('#editor_preview').hide();
226 $(myCodeMirror.getWrapperElement()).show();
230 $(myCodeMirror.getWrapperElement()).show();
227 }
231 }
228 });
232 });
229 $('#filename').focus();
233 $('#filename').focus();
230
234
231 </script>
235 </script>
232 </%def>
236 </%def>
General Comments 0
You need to be logged in to leave comments. Login now