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