Merge branch 'master' of gitlab.com:gitlab-org/gitlab-ce into fix/import-url-validator
# Conflicts: # spec/models/project_spec.rb
|
@ -18,6 +18,7 @@ variables:
|
|||
SIMPLECOV: "true"
|
||||
USE_DB: "true"
|
||||
USE_BUNDLE_INSTALL: "true"
|
||||
GIT_DEPTH: "20"
|
||||
|
||||
before_script:
|
||||
- source ./scripts/prepare_build.sh
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
require: rubocop-rspec
|
||||
require:
|
||||
- rubocop-rspec
|
||||
- ./rubocop/rubocop
|
||||
|
||||
AllCops:
|
||||
TargetRubyVersion: 2.1
|
||||
|
@ -532,11 +534,11 @@ Style/SingleLineMethods:
|
|||
|
||||
# Use spaces after colons.
|
||||
Style/SpaceAfterColon:
|
||||
Enabled: false
|
||||
Enabled: true
|
||||
|
||||
# Use spaces after commas.
|
||||
Style/SpaceAfterComma:
|
||||
Enabled: false
|
||||
Enabled: true
|
||||
|
||||
# Do not put a space between a method name and the opening parenthesis in a
|
||||
# method definition.
|
||||
|
|
44
CHANGELOG
|
@ -1,23 +1,56 @@
|
|||
Please view this file on the master branch, on stable branches it's out of date.
|
||||
|
||||
v 8.10.0 (unreleased)
|
||||
- Fix commit builds API, return all builds for all pipelines for given commit. !4849
|
||||
- Replace Haml with Hamlit to make view rendering faster. !3666
|
||||
- Refactor repository paths handling to allow multiple git mount points
|
||||
- Add Application Setting to configure default Repository Path for new projects
|
||||
- Wrap code blocks on Activies and Todos page. !4783 (winniehell)
|
||||
- Align flash messages with left side of page content !4959 (winniehell)
|
||||
- Display last commit of deleted branch in push events !4699 (winniehell)
|
||||
- Add Sidekiq queue duration to transaction metrics.
|
||||
- Let Workhorse serve format-patch diffs
|
||||
- Make images fit to the size of the viewport !4810
|
||||
- Fix check for New Branch button on Issue page !4630 (winniehell)
|
||||
- Fix MR-auto-close text added to description. !4836
|
||||
- Fix pagination when sorting by columns with lots of ties (like priority)
|
||||
- Exclude email check from the standard health check
|
||||
- Fix changing issue state columns in milestone view
|
||||
- Add notification settings dropdown for groups
|
||||
- Fix user creation with stronger minimum password requirements !4054 (nathan-pmt)
|
||||
- PipelinesFinder uses git cache data
|
||||
- Check for conflicts with existing Project's wiki path when creating a new project.
|
||||
- Remove unused front-end variable -> default_issues_tracker
|
||||
- Add API endpoint for a group issues !4520 (mahcsig)
|
||||
- Add Bugzilla integration !4930 (iamtjg)
|
||||
- Allow [ci skip] to be in any case and allow [skip ci]. !4785 (simon_w)
|
||||
- Set import_url validation to be more strict
|
||||
|
||||
v 8.9.3 (unreleased)
|
||||
- Add basic system information like memory and disk usage to the admin panel
|
||||
|
||||
v 8.9.4 (unreleased)
|
||||
- Fixes middle click and double request when navigating through the file browser !4891
|
||||
- Add sub nav to file page view
|
||||
|
||||
v 8.9.3
|
||||
- Fix encrypted data backwards compatibility after upgrading attr_encrypted gem. !4963
|
||||
- Fix rendering of commit notes. !4953
|
||||
- Resolve "Pin should show up at 1280px min". !4947
|
||||
- Switched mobile button icons to ellipsis and angle. !4944
|
||||
- Correctly returns todo ID after creating todo. !4941
|
||||
- Better debugging for memory killer middleware. !4936
|
||||
- Remove duplicate new page btn from edit wiki. !4904
|
||||
- Use clock_gettime for all performance timestamps. !4899
|
||||
- Use memorized tags array when searching tags by name. !4859
|
||||
- Fixed avatar alignment in new MR view. !4901
|
||||
- Removed fade when filtering results. !4932
|
||||
- Fix missing avatar on system notes. !4954
|
||||
- Reduce overhead and optimize ProjectTeam#max_member_access performance. !4973
|
||||
- Use update_columns to by_pass all the dirty code on active_record. !4985
|
||||
- Decreased min width of screen to 1280px for pinned sidebar
|
||||
- Fix encrypted data backwards compatibility after upgrading attr_encrypted gem
|
||||
- Update mobile button icons to be more inline with typical UI paradigms
|
||||
|
||||
v 8.9.2
|
||||
- Fix visibility of snippets when searching.
|
||||
|
@ -68,6 +101,8 @@ v 8.9.1
|
|||
- Remove duplicate 'New Page' button on edit wiki page
|
||||
|
||||
v 8.9.0
|
||||
v 8.9.0 (unreleased)
|
||||
- Fix group visibility form layout in application settings
|
||||
- Fix builds API response not including commit data
|
||||
- Fix error when CI job variables key specified but not defined
|
||||
- Fix pipeline status when there are no builds in pipeline
|
||||
|
@ -163,6 +198,7 @@ v 8.9.0
|
|||
- Add Application Setting to configure Container Registry token expire delay (default 5min)
|
||||
- Cache assigned issue and merge request counts in sidebar nav
|
||||
- Use Knapsack only in CI environment
|
||||
- Updated project creation page to match new UI #2542
|
||||
- Cache project build count in sidebar nav
|
||||
- Add milestone expire date to the right sidebar
|
||||
- Manually mark a issue or merge request as a todo
|
||||
|
@ -218,6 +254,10 @@ v 8.9.0
|
|||
- Add tooltip to pin/unpin navbar
|
||||
- Add new sub nav style to Wiki and Graphs sub navigation
|
||||
|
||||
v 8.8.6
|
||||
- Fix visibility of snippets when searching.
|
||||
- Update omniauth-saml to 1.6.0 !4951
|
||||
|
||||
v 8.8.5
|
||||
- Import GitHub repositories respecting the API rate limit !4166
|
||||
- Fix todos page throwing errors when you have a project pending deletion !4300
|
||||
|
@ -348,6 +388,10 @@ v 8.8.0
|
|||
- When creating a .gitignore file a dropdown with templates will be provided
|
||||
- Shows the issue/MR list search/filter form and corrects the mobile styling for guest users. #17562
|
||||
|
||||
v 8.7.8
|
||||
- Fix visibility of snippets when searching.
|
||||
- Update omniauth-saml to 1.6.0 !4951
|
||||
|
||||
v 8.7.7
|
||||
- Fix import by `Any Git URL` broken if the URL contains a space
|
||||
- Prevent unauthorized access to other projects build traces
|
||||
|
|
|
@ -1 +1 @@
|
|||
3.0.0
|
||||
3.1.0
|
||||
|
|
|
@ -1 +1 @@
|
|||
0.7.5
|
||||
0.7.7
|
||||
|
|
4
Gemfile
|
@ -91,6 +91,7 @@ gem 'fog-core', '~> 1.40'
|
|||
gem 'fog-local', '~> 0.3'
|
||||
gem 'fog-google', '~> 0.3'
|
||||
gem 'fog-openstack', '~> 0.1'
|
||||
gem 'fog-rackspace', '~> 0.1.1'
|
||||
|
||||
# for aws storage
|
||||
gem "unf", '~> 0.1.4'
|
||||
|
@ -346,3 +347,6 @@ gem "paranoia", "~> 2.0"
|
|||
|
||||
# Health check
|
||||
gem 'health_check', '~> 1.5.1'
|
||||
|
||||
# System information
|
||||
gem 'vmstat', '~> 2.1.0'
|
||||
|
|
|
@ -243,6 +243,11 @@ GEM
|
|||
fog-core (>= 1.39)
|
||||
fog-json (>= 1.0)
|
||||
ipaddress (>= 0.8)
|
||||
fog-rackspace (0.1.1)
|
||||
fog-core (>= 1.35)
|
||||
fog-json (>= 1.0)
|
||||
fog-xml (>= 0.1)
|
||||
ipaddress (>= 0.8)
|
||||
fog-xml (0.1.2)
|
||||
fog-core
|
||||
nokogiri (~> 1.5, >= 1.5.11)
|
||||
|
@ -780,6 +785,7 @@ GEM
|
|||
coercible (~> 1.0)
|
||||
descendants_tracker (~> 0.0, >= 0.0.3)
|
||||
equalizer (~> 0.0, >= 0.0.9)
|
||||
vmstat (2.1.0)
|
||||
warden (1.2.6)
|
||||
rack (>= 1.0)
|
||||
web-console (2.3.0)
|
||||
|
@ -857,6 +863,7 @@ DEPENDENCIES
|
|||
fog-google (~> 0.3)
|
||||
fog-local (~> 0.3)
|
||||
fog-openstack (~> 0.1)
|
||||
fog-rackspace (~> 0.1.1)
|
||||
font-awesome-rails (~> 4.6.1)
|
||||
foreman
|
||||
fuubar (~> 2.0.0)
|
||||
|
@ -984,6 +991,7 @@ DEPENDENCIES
|
|||
unicorn-worker-killer (~> 0.4.2)
|
||||
version_sorter (~> 2.0.0)
|
||||
virtus (~> 1.0.1)
|
||||
vmstat (~> 2.1.0)
|
||||
web-console (~> 2.0)
|
||||
webmock (~> 1.21.0)
|
||||
wikicloth (= 0.8.1)
|
||||
|
|
Before Width: | Height: | Size: 986 B After Width: | Height: | Size: 695 B |
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 2.1 KiB |
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 870 B |
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 2 KiB |
Before Width: | Height: | Size: 5.2 KiB After Width: | Height: | Size: 4.3 KiB |
Before Width: | Height: | Size: 4.7 KiB After Width: | Height: | Size: 3 KiB |
Before Width: | Height: | Size: 167 B After Width: | Height: | Size: 167 B |
Before Width: | Height: | Size: 3.9 KiB After Width: | Height: | Size: 3.9 KiB |
Before Width: | Height: | Size: 257 KiB After Width: | Height: | Size: 257 KiB |
Before Width: | Height: | Size: 674 KiB After Width: | Height: | Size: 673 KiB |
Before Width: | Height: | Size: 5.1 KiB After Width: | Height: | Size: 3.5 KiB |
Before Width: | Height: | Size: 809 B After Width: | Height: | Size: 631 B |
Before Width: | Height: | Size: 495 B After Width: | Height: | Size: 201 B |
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 729 B |
Before Width: | Height: | Size: 5.7 KiB After Width: | Height: | Size: 5.7 KiB |
Before Width: | Height: | Size: 3.6 KiB After Width: | Height: | Size: 3.6 KiB |
Before Width: | Height: | Size: 5.7 KiB After Width: | Height: | Size: 4.2 KiB |
Before Width: | Height: | Size: 621 B After Width: | Height: | Size: 621 B |
Before Width: | Height: | Size: 942 B After Width: | Height: | Size: 939 B |
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 7.9 KiB After Width: | Height: | Size: 5.5 KiB |
Before Width: | Height: | Size: 3.4 KiB After Width: | Height: | Size: 2.4 KiB |
Before Width: | Height: | Size: 4.9 KiB After Width: | Height: | Size: 3.4 KiB |
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 1.9 KiB |
|
@ -199,7 +199,6 @@ $ ->
|
|||
$('.header-content .header-logo').toggle()
|
||||
$('.header-content .navbar-collapse').toggle()
|
||||
$('.navbar-toggle').toggleClass('active')
|
||||
$('.navbar-toggle i').toggleClass("fa-angle-right fa-angle-left")
|
||||
|
||||
# Show/hide comments on diff
|
||||
$body.on "click", ".js-toggle-diff-comments", (e) ->
|
||||
|
@ -261,7 +260,7 @@ $ ->
|
|||
new Aside()
|
||||
|
||||
# Sidenav pinning
|
||||
if $window.width() < 1440 and $.cookie('pin_nav') is 'true'
|
||||
if $window.width() < 1280 and $.cookie('pin_nav') is 'true'
|
||||
$.cookie('pin_nav', 'false', { path: '/' })
|
||||
$('.page-with-sidebar')
|
||||
.toggleClass('page-sidebar-collapsed page-sidebar-expanded')
|
||||
|
|
|
@ -84,6 +84,8 @@ class Dispatcher
|
|||
new Activities()
|
||||
when 'groups:show'
|
||||
shortcut_handler = new ShortcutsNavigation()
|
||||
new NotificationsForm()
|
||||
new NotificationsDropdown()
|
||||
when 'groups:group_members:index'
|
||||
new GroupMembers()
|
||||
new UsersSelect()
|
||||
|
|
|
@ -4,11 +4,19 @@ class @Flash
|
|||
@flash.html("")
|
||||
|
||||
innerDiv = $('<div/>',
|
||||
class: "flash-#{type}",
|
||||
text: message
|
||||
class: "flash-#{type}"
|
||||
)
|
||||
innerDiv.appendTo(".flash-container")
|
||||
|
||||
textDiv = $("<div/>",
|
||||
class: "flash-text",
|
||||
text: message
|
||||
)
|
||||
textDiv.appendTo(innerDiv)
|
||||
|
||||
if @flash.parent().hasClass('content-wrapper')
|
||||
textDiv.addClass('container-fluid container-limited')
|
||||
|
||||
@flash.click -> $(@).fadeOut()
|
||||
@flash.show()
|
||||
|
||||
|
|
|
@ -186,6 +186,8 @@ class GitLabDropdown
|
|||
@fullData = data
|
||||
|
||||
@parseData @fullData
|
||||
|
||||
@filter.input.trigger('keyup') if @options.filterable and @filter and @filter.input
|
||||
}
|
||||
|
||||
# Init filterable
|
||||
|
@ -218,6 +220,13 @@ class GitLabDropdown
|
|||
@dropdown.on 'keyup', (e) =>
|
||||
if e.which is 27 # Escape key
|
||||
$('.dropdown-menu-close', @dropdown).trigger 'click'
|
||||
@dropdown.on 'blur', 'a', (e) =>
|
||||
if e.relatedTarget?
|
||||
$relatedTarget = $(e.relatedTarget)
|
||||
$dropdownMenu = $relatedTarget.closest('.dropdown-menu')
|
||||
|
||||
if $dropdownMenu.length is 0
|
||||
@dropdown.removeClass('open')
|
||||
|
||||
if @dropdown.find(".dropdown-toggle-page").length
|
||||
@dropdown.find(".dropdown-toggle-page, .dropdown-menu-back").on "click", (e) =>
|
||||
|
|
|
@ -59,13 +59,12 @@ issuable_created = false
|
|||
filterResults: (form) =>
|
||||
formData = form.serialize()
|
||||
|
||||
$('.issues-holder, .merge-requests-holder').css('opacity', '0.5')
|
||||
formAction = form.attr('action')
|
||||
issuesUrl = formAction
|
||||
issuesUrl += ("#{if formAction.indexOf('?') < 0 then '?' else '&'}")
|
||||
issuesUrl += formData
|
||||
|
||||
Turbolinks.visit(issuesUrl);
|
||||
Turbolinks.visit(issuesUrl)
|
||||
|
||||
initChecks: ->
|
||||
@issuableBulkActions = $('.bulk-update').data('bulkActions')
|
||||
|
|
|
@ -10,11 +10,35 @@
|
|||
gl.text.selectedText = (text, textarea) ->
|
||||
text.substring(textarea.selectionStart, textarea.selectionEnd)
|
||||
|
||||
gl.text.insertText = (textArea, text, tag, selected, wrap) ->
|
||||
gl.text.lineBefore = (text, textarea) ->
|
||||
split = text.substring(0, textarea.selectionStart).trim().split('\n')
|
||||
split[split.length - 1]
|
||||
|
||||
gl.text.lineAfter = (text, textarea) ->
|
||||
text.substring(textarea.selectionEnd).trim().split('\n')[0]
|
||||
|
||||
gl.text.blockTagText = (text, textArea, blockTag, selected) ->
|
||||
lineBefore = @lineBefore(text, textArea)
|
||||
lineAfter = @lineAfter(text, textArea)
|
||||
|
||||
if lineBefore is blockTag and lineAfter is blockTag
|
||||
# To remove the block tag we have to select the line before & after
|
||||
if blockTag?
|
||||
textArea.selectionStart = textArea.selectionStart - (blockTag.length + 1)
|
||||
textArea.selectionEnd = textArea.selectionEnd + (blockTag.length + 1)
|
||||
|
||||
selected
|
||||
else
|
||||
"#{blockTag}\n#{selected}\n#{blockTag}"
|
||||
|
||||
gl.text.insertText = (textArea, text, tag, blockTag, selected, wrap) ->
|
||||
selectedSplit = selected.split('\n')
|
||||
startChar = if not wrap and textArea.selectionStart > 0 then '\n' else ''
|
||||
|
||||
if selectedSplit.length > 1 and not wrap
|
||||
if selectedSplit.length > 1 and (not wrap or blockTag?)
|
||||
if blockTag?
|
||||
insertText = @blockTagText(text, textArea, blockTag, selected)
|
||||
else
|
||||
insertText = selectedSplit.map((val) ->
|
||||
if val.indexOf(tag) is 0
|
||||
"#{val.replace(tag, '')}"
|
||||
|
@ -51,7 +75,7 @@
|
|||
|
||||
textArea.setSelectionRange pos, pos
|
||||
|
||||
gl.text.updateText = (textArea, tag, wrap) ->
|
||||
gl.text.updateText = (textArea, tag, blockTag, wrap) ->
|
||||
$textArea = $(textArea)
|
||||
oldVal = $textArea.val()
|
||||
textArea = $textArea.get(0)
|
||||
|
@ -59,7 +83,7 @@
|
|||
selected = @selectedText(text, textArea)
|
||||
$textArea.focus()
|
||||
|
||||
@insertText(textArea, text, tag, selected, wrap)
|
||||
@insertText(textArea, text, tag, blockTag, selected, wrap)
|
||||
|
||||
gl.text.init = (form) ->
|
||||
self = @
|
||||
|
@ -70,6 +94,7 @@
|
|||
self.updateText(
|
||||
$this.closest('.md-area').find('textarea'),
|
||||
$this.data('md-tag'),
|
||||
$this.data('md-block'),
|
||||
not $this.data('md-prepend')
|
||||
)
|
||||
|
||||
|
|
|
@ -171,22 +171,15 @@ class @SearchAutocomplete
|
|||
}
|
||||
|
||||
bindEvents: ->
|
||||
$(document).on 'click', @onDocumentClick
|
||||
@searchInput.on 'keydown', @onSearchInputKeyDown
|
||||
@searchInput.on 'keyup', @onSearchInputKeyUp
|
||||
@searchInput.on 'click', @onSearchInputClick
|
||||
@searchInput.on 'focus', @onSearchInputFocus
|
||||
@searchInput.on 'blur', @onSearchInputBlur
|
||||
@clearInput.on 'click', @onClearInputClick
|
||||
@locationBadgeEl.on 'click', =>
|
||||
@searchInput.focus()
|
||||
|
||||
onDocumentClick: (e) =>
|
||||
# If clicking outside the search box
|
||||
# And search input is not focused
|
||||
# And we are not clicking inside a suggestion
|
||||
if not $.contains(@dropdown[0], e.target) and @isFocused and not $(e.target).closest('.search-form').length
|
||||
@onSearchInputBlur()
|
||||
|
||||
enableAutocomplete: ->
|
||||
# No need to enable anything if user is not logged in
|
||||
return if !gon.current_user_id
|
||||
|
@ -287,8 +280,6 @@ class @SearchAutocomplete
|
|||
value: @originalState._location
|
||||
)
|
||||
|
||||
@dropdown.removeClass 'open'
|
||||
|
||||
badgePresent: ->
|
||||
@locationBadgeEl.length
|
||||
|
||||
|
|
|
@ -9,12 +9,12 @@ class @Shortcuts
|
|||
|
||||
onToggleHelp: (e) =>
|
||||
e.preventDefault()
|
||||
@toggleHelp(@enabledHelp)
|
||||
Shortcuts.toggleHelp(@enabledHelp)
|
||||
|
||||
toggleMarkdownPreview: (e) =>
|
||||
toggleMarkdownPreview: (e) ->
|
||||
$(document).triggerHandler('markdown-preview:toggle', [e])
|
||||
|
||||
toggleHelp: (location) ->
|
||||
@toggleHelp: (location) ->
|
||||
$modal = $('#modal-shortcuts')
|
||||
|
||||
if $modal.length
|
||||
|
|
|
@ -5,9 +5,15 @@ class @TreeView
|
|||
# Code browser tree slider
|
||||
# Make the entire tree-item row clickable, but not if clicking another link (like a commit message)
|
||||
$(".tree-content-holder .tree-item").on 'click', (e) ->
|
||||
if (e.target.nodeName != "A")
|
||||
$clickedEl = $(e.target)
|
||||
path = $('.tree-item-file-name a', this).attr('href')
|
||||
Turbolinks.visit(path)
|
||||
|
||||
if not $clickedEl.is('a') and not $clickedEl.is('.str-truncated')
|
||||
if e.metaKey or e.which is 2
|
||||
e.preventDefault()
|
||||
window.open path, '_blank'
|
||||
else
|
||||
Turbolinks.visit path
|
||||
|
||||
# Show the "Loading commit data" for only the first element
|
||||
$('span.log_loading:first').removeClass('hide')
|
||||
|
|
|
@ -137,7 +137,7 @@
|
|||
margin: 0;
|
||||
font-size: 24px;
|
||||
font-weight: normal;
|
||||
margin-bottom: 5px;
|
||||
margin-bottom: 10px;
|
||||
color: #4c4e54;
|
||||
font-size: 23px;
|
||||
line-height: 1.1;
|
||||
|
|
|
@ -16,4 +16,11 @@
|
|||
@extend .alert-danger;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.flash-notice, .flash-alert {
|
||||
.container-fluid.flash-text {
|
||||
background: transparent;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -125,7 +125,8 @@
|
|||
border: 0;
|
||||
outline: 0;
|
||||
|
||||
&:hover {
|
||||
&:hover,
|
||||
&:focus {
|
||||
color: $gl-link-color;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,9 +21,8 @@
|
|||
|
||||
.fa {
|
||||
position: relative;
|
||||
top: 3px;
|
||||
font-size: 13px;
|
||||
color: $btn-placeholder-gray;
|
||||
top: 5px;
|
||||
font-size: 18px;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
.page-with-sidebar {
|
||||
padding-top: $header-height;
|
||||
padding-bottom: 25px;
|
||||
transition: padding $sidebar-transition-duration;
|
||||
|
||||
.sidebar-wrapper {
|
||||
|
|
|
@ -7,7 +7,7 @@ $gutter_collapsed_width: 62px;
|
|||
$gutter_width: 290px;
|
||||
$gutter_inner_width: 258px;
|
||||
$sidebar-transition-duration: .15s;
|
||||
$sidebar-breakpoint: 1440px;
|
||||
$sidebar-breakpoint: 1280px;
|
||||
|
||||
/*
|
||||
* UI elements
|
||||
|
|
|
@ -83,11 +83,7 @@
|
|||
position: relative;
|
||||
|
||||
@media (min-width: $screen-sm-min) {
|
||||
padding-left: 20px;
|
||||
|
||||
.commit-info-block {
|
||||
padding-left: 44px;
|
||||
}
|
||||
padding-left: 46px;
|
||||
}
|
||||
|
||||
&:not(:last-child) {
|
||||
|
@ -102,9 +98,7 @@
|
|||
|
||||
|
||||
.avatar {
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
left: 16px;
|
||||
margin-left: -46px;
|
||||
}
|
||||
|
||||
.item-title {
|
||||
|
|
|
@ -48,11 +48,7 @@
|
|||
|
||||
.access-request-button {
|
||||
@include btn-gray;
|
||||
position: absolute;
|
||||
right: 16px;
|
||||
bottom: 32px;
|
||||
padding: 3px 10px;
|
||||
margin-right: 10px;
|
||||
text-transform: none;
|
||||
background-color: $background-color;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -264,8 +264,15 @@
|
|||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.item-title {
|
||||
@media (min-width: $screen-sm-min) {
|
||||
width: 49%;
|
||||
}
|
||||
}
|
||||
|
||||
.avatar {
|
||||
margin-left: 0;
|
||||
left: 0;
|
||||
top: 2px;
|
||||
}
|
||||
|
||||
.commit-row-info {
|
||||
|
|
|
@ -41,6 +41,10 @@ ul.notes {
|
|||
.timeline-icon {
|
||||
.avatar {
|
||||
visibility: hidden;
|
||||
|
||||
.discussion-body & {
|
||||
visibility: visible;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,10 +13,53 @@
|
|||
|
||||
.new_project,
|
||||
.edit-project {
|
||||
fieldset.features {
|
||||
.control-label {
|
||||
fieldset {
|
||||
&.features .control-label {
|
||||
font-weight: normal;
|
||||
}
|
||||
.form-group {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
&> .form-group {
|
||||
padding-left: 0;
|
||||
}
|
||||
}
|
||||
.help-block {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.project-path {
|
||||
padding-right: 0;
|
||||
.form-control {
|
||||
border-radius: $border-radius-base;
|
||||
}
|
||||
}
|
||||
.input-group > div {
|
||||
&:last-child {
|
||||
padding-right: 0;
|
||||
}
|
||||
}
|
||||
@media (max-width: $screen-xs-max) {
|
||||
.input-group > div {
|
||||
margin-bottom: 14px;
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
fieldset > .form-group:first-child {
|
||||
padding-right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.input-group-addon {
|
||||
&.static-namespace {
|
||||
height: 35px;
|
||||
border-radius: 3px;
|
||||
border: 1px solid #e5e5e5;
|
||||
}
|
||||
&+ .select2 a {
|
||||
border-top-left-radius: 0;
|
||||
border-bottom-left-radius: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -365,10 +408,28 @@ a.deploy-project-label {
|
|||
}
|
||||
}
|
||||
|
||||
.project-import .btn {
|
||||
float: left;
|
||||
margin-bottom: 10px;
|
||||
.project-import {
|
||||
.form-group {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.import-buttons {
|
||||
padding-left: 0;
|
||||
display: -webkit-flex;
|
||||
display: flex;
|
||||
-webkit-flex-wrap: wrap;
|
||||
flex-wrap: wrap;
|
||||
.btn {
|
||||
margin-right: 10px;
|
||||
padding: 8px 12px;
|
||||
}
|
||||
&> div {
|
||||
margin-bottom: 14px;
|
||||
padding-left: 0;
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.project-stats {
|
||||
|
|
|
@ -101,7 +101,8 @@
|
|||
margin: 0;
|
||||
|
||||
.commit {
|
||||
padding: 0 0 0 55px;
|
||||
padding-top: 0;
|
||||
padding-bottom: 0;
|
||||
|
||||
.commit-row-title {
|
||||
.commit-row-message {
|
||||
|
|
|
@ -109,6 +109,7 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController
|
|||
:metrics_packet_size,
|
||||
:send_user_confirmation_email,
|
||||
:container_registry_token_expire_delay,
|
||||
:repository_storage,
|
||||
restricted_visibility_levels: [],
|
||||
import_sources: [],
|
||||
disabled_oauth_sign_in_sources: []
|
||||
|
|
13
app/controllers/admin/system_info_controller.rb
Normal file
|
@ -0,0 +1,13 @@
|
|||
class Admin::SystemInfoController < Admin::ApplicationController
|
||||
def show
|
||||
system_info = Vmstat.snapshot
|
||||
|
||||
@cpus = system_info.cpus.length
|
||||
|
||||
@mem_used = system_info.memory.active_bytes
|
||||
@mem_total = system_info.memory.total_bytes
|
||||
|
||||
@disk_used = system_info.disks[0].used_bytes
|
||||
@disk_total = system_info.disks[0].total_bytes
|
||||
end
|
||||
end
|
|
@ -37,15 +37,12 @@ class GroupsController < Groups::ApplicationController
|
|||
end
|
||||
|
||||
def show
|
||||
@last_push = current_user.recent_push if current_user
|
||||
if current_user
|
||||
@last_push = current_user.recent_push
|
||||
@notification_setting = current_user.notification_settings_for(group)
|
||||
end
|
||||
|
||||
@projects = @projects.includes(:namespace)
|
||||
@projects = @projects.sorted_by_activity
|
||||
@projects = filter_projects(@projects)
|
||||
@projects = @projects.sort(@sort = params[:sort])
|
||||
@projects = @projects.page(params[:page]) if params[:filter_projects].blank?
|
||||
|
||||
@shared_projects = GroupProjectsFinder.new(group, only_shared: true).execute(current_user)
|
||||
setup_projects
|
||||
|
||||
respond_to do |format|
|
||||
format.html
|
||||
|
@ -97,6 +94,16 @@ class GroupsController < Groups::ApplicationController
|
|||
|
||||
protected
|
||||
|
||||
def setup_projects
|
||||
@projects = @projects.includes(:namespace)
|
||||
@projects = @projects.sorted_by_activity
|
||||
@projects = filter_projects(@projects)
|
||||
@projects = @projects.sort(@sort = params[:sort])
|
||||
@projects = @projects.page(params[:page]) if params[:filter_projects].blank?
|
||||
|
||||
@shared_projects = GroupProjectsFinder.new(group, only_shared: true).execute(current_user)
|
||||
end
|
||||
|
||||
def authorize_create_group!
|
||||
unless can?(current_user, :create_group, nil)
|
||||
return render_404
|
||||
|
|
|
@ -2,11 +2,9 @@ class NotificationSettingsController < ApplicationController
|
|||
before_action :authenticate_user!
|
||||
|
||||
def create
|
||||
project = Project.find(params[:project][:id])
|
||||
return render_404 unless can_read?(resource)
|
||||
|
||||
return render_404 unless can?(current_user, :read_project, project)
|
||||
|
||||
@notification_setting = current_user.notification_settings_for(project)
|
||||
@notification_setting = current_user.notification_settings_for(resource)
|
||||
@saved = @notification_setting.update_attributes(notification_setting_params)
|
||||
|
||||
render_response
|
||||
|
@ -21,6 +19,22 @@ class NotificationSettingsController < ApplicationController
|
|||
|
||||
private
|
||||
|
||||
def resource
|
||||
@resource ||=
|
||||
if params[:project_id].present?
|
||||
Project.find(params[:project_id])
|
||||
elsif params[:namespace_id].present?
|
||||
Group.find(params[:namespace_id])
|
||||
end
|
||||
end
|
||||
|
||||
def can_read?(resource)
|
||||
ability_name = resource.class.name.downcase
|
||||
ability_name = "read_#{ability_name}".to_sym
|
||||
|
||||
can?(current_user, ability_name, resource)
|
||||
end
|
||||
|
||||
def render_response
|
||||
render json: {
|
||||
html: view_to_html_string("shared/notifications/_button", notification_setting: @notification_setting),
|
||||
|
|
|
@ -59,7 +59,13 @@ class Projects::MergeRequestsController < Projects::ApplicationController
|
|||
respond_to do |format|
|
||||
format.html
|
||||
format.json { render json: @merge_request }
|
||||
format.patch { render text: @merge_request.to_patch }
|
||||
format.patch do
|
||||
headers.store(*Gitlab::Workhorse.send_git_patch(@project.repository,
|
||||
@merge_request.diff_base_commit.id,
|
||||
@merge_request.last_commit.id))
|
||||
headers['Content-Disposition'] = 'inline'
|
||||
head :ok
|
||||
end
|
||||
format.diff do
|
||||
return render_404 unless @merge_request.diff_refs
|
||||
|
||||
|
|
|
@ -29,10 +29,10 @@ class PipelinesFinder
|
|||
end
|
||||
|
||||
def branches
|
||||
project.repository.branches.map(&:name)
|
||||
project.repository.branch_names
|
||||
end
|
||||
|
||||
def tags
|
||||
project.repository.tags.map(&:name)
|
||||
project.repository.tag_names
|
||||
end
|
||||
end
|
||||
|
|
|
@ -78,4 +78,12 @@ module ApplicationSettingsHelper
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
def repository_storage_options_for_select
|
||||
options = Gitlab.config.repositories.storages.map do |name, path|
|
||||
["#{name} - #{path}", name]
|
||||
end
|
||||
|
||||
options_for_select(options, @application_setting.repository_storage)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -69,7 +69,7 @@ module DropdownsHelper
|
|||
|
||||
def dropdown_filter(placeholder, search_id: nil)
|
||||
content_tag :div, class: "dropdown-input" do
|
||||
filter_output = search_field_tag search_id, nil, class: "dropdown-input-field", placeholder: placeholder
|
||||
filter_output = search_field_tag search_id, nil, class: "dropdown-input-field", placeholder: placeholder, autocomplete: 'off'
|
||||
filter_output << icon('search', class: "dropdown-input-search")
|
||||
filter_output << icon('times', class: "dropdown-input-clear js-dropdown-input-clear", role: "button")
|
||||
|
||||
|
|
|
@ -34,10 +34,7 @@ module LabelsHelper
|
|||
# Returns a String
|
||||
def link_to_label(label, project: nil, type: :issue, tooltip: true, css_class: nil, &block)
|
||||
project ||= @project || label.project
|
||||
link = send("namespace_project_#{type.to_s.pluralize}_path",
|
||||
project.namespace,
|
||||
project,
|
||||
label_name: [label.name])
|
||||
link = label_filter_path(project, label, type: type)
|
||||
|
||||
if block_given?
|
||||
link_to link, class: css_class, &block
|
||||
|
@ -46,6 +43,13 @@ module LabelsHelper
|
|||
end
|
||||
end
|
||||
|
||||
def label_filter_path(project, label, type: issue)
|
||||
send("namespace_project_#{type.to_s.pluralize}_path",
|
||||
project.namespace,
|
||||
project,
|
||||
label_name: [label.name])
|
||||
end
|
||||
|
||||
def project_label_names
|
||||
@project.labels.pluck(:title)
|
||||
end
|
||||
|
|
|
@ -69,4 +69,14 @@ module NotesHelper
|
|||
button_tag 'Reply...', class: 'btn btn-text-field js-discussion-reply-button',
|
||||
data: data, title: 'Add a reply'
|
||||
end
|
||||
|
||||
def note_max_access_for_user(note)
|
||||
@max_access_by_user_id ||= Hash.new do |hash, key|
|
||||
project = key[:project]
|
||||
hash[key] = project.team.human_max_access(key[:user_id])
|
||||
end
|
||||
|
||||
full_key = { project: note.project, user_id: note.author_id }
|
||||
@max_access_by_user_id[full_key]
|
||||
end
|
||||
end
|
||||
|
|
|
@ -72,6 +72,6 @@ module NotificationsHelper
|
|||
# Create hidden field to send notification setting source to controller
|
||||
def hidden_setting_source_input(notification_setting)
|
||||
return unless notification_setting.source_type
|
||||
hidden_field_tag "#{notification_setting.source_type.downcase}[id]", notification_setting.source_id
|
||||
hidden_field_tag "#{notification_setting.source_type.downcase}_id", notification_setting.source_id
|
||||
end
|
||||
end
|
||||
|
|
|
@ -327,9 +327,9 @@ module ProjectsHelper
|
|||
end
|
||||
end
|
||||
|
||||
def sanitize_repo_path(message)
|
||||
def sanitize_repo_path(project, message)
|
||||
return '' unless message.present?
|
||||
|
||||
message.strip.gsub(Gitlab.config.gitlab_shell.repos_path.chomp('/'), "[REPOS PATH]")
|
||||
message.strip.gsub(project.repository_storage_path.chomp('/'), "[REPOS PATH]")
|
||||
end
|
||||
end
|
||||
|
|
|
@ -55,6 +55,10 @@ class ApplicationSetting < ActiveRecord::Base
|
|||
presence: true,
|
||||
numericality: { only_integer: true, greater_than: 0 }
|
||||
|
||||
validates :repository_storage,
|
||||
presence: true,
|
||||
inclusion: { in: ->(_object) { Gitlab.config.repositories.storages.keys } }
|
||||
|
||||
validates_each :restricted_visibility_levels do |record, attr, value|
|
||||
unless value.nil?
|
||||
value.each do |level|
|
||||
|
@ -134,6 +138,7 @@ class ApplicationSetting < ActiveRecord::Base
|
|||
disabled_oauth_sign_in_sources: [],
|
||||
send_user_confirmation_email: false,
|
||||
container_registry_token_expire_delay: 5,
|
||||
repository_storage: 'default',
|
||||
)
|
||||
end
|
||||
|
||||
|
|
|
@ -315,7 +315,7 @@ class Event < ActiveRecord::Base
|
|||
|
||||
def body?
|
||||
if push?
|
||||
push_with_commits?
|
||||
push_with_commits? || rm_ref?
|
||||
elsif note?
|
||||
true
|
||||
else
|
||||
|
|
|
@ -32,6 +32,7 @@ class Member < ActiveRecord::Base
|
|||
scope :request, -> { where.not(requested_at: nil) }
|
||||
scope :non_request, -> { where(requested_at: nil) }
|
||||
scope :non_pending, -> { non_request.non_invite }
|
||||
scope :has_access, -> { where('access_level > 0') }
|
||||
|
||||
scope :guests, -> { where(access_level: GUEST) }
|
||||
scope :reporters, -> { where(access_level: REPORTER) }
|
||||
|
|
|
@ -319,13 +319,6 @@ class MergeRequest < ActiveRecord::Base
|
|||
)
|
||||
end
|
||||
|
||||
# Returns the commit as a series of email patches.
|
||||
#
|
||||
# see "git format-patch"
|
||||
def to_patch
|
||||
target_project.repository.format_patch(diff_base_commit.sha, source_sha)
|
||||
end
|
||||
|
||||
def hook_attrs
|
||||
attrs = {
|
||||
source: source_project.try(:hook_attrs),
|
||||
|
|
|
@ -108,44 +108,46 @@ class MergeRequestDiff < ActiveRecord::Base
|
|||
# Reload all commits related to current merge request from repo
|
||||
# and save it as array of hashes in st_commits db field
|
||||
def reload_commits
|
||||
new_attributes = {}
|
||||
|
||||
commit_objects = unmerged_commits
|
||||
|
||||
if commit_objects.present?
|
||||
self.st_commits = dump_commits(commit_objects)
|
||||
new_attributes[:st_commits] = dump_commits(commit_objects)
|
||||
end
|
||||
|
||||
save
|
||||
update_columns_serialized(new_attributes)
|
||||
end
|
||||
|
||||
# Reload diffs between branches related to current merge request from repo
|
||||
# and save it as array of hashes in st_diffs db field
|
||||
def reload_diffs
|
||||
new_attributes = {}
|
||||
new_diffs = []
|
||||
|
||||
if commits.size.zero?
|
||||
self.state = :empty
|
||||
new_attributes[:state] = :empty
|
||||
else
|
||||
diff_collection = unmerged_diffs
|
||||
|
||||
if diff_collection.overflow?
|
||||
# Set our state to 'overflow' to make the #empty? and #collected?
|
||||
# methods (generated by StateMachine) return false.
|
||||
self.state = :overflow
|
||||
new_attributes[:state] = :overflow
|
||||
end
|
||||
|
||||
self.real_size = diff_collection.real_size
|
||||
new_attributes[:real_size] = diff_collection.real_size
|
||||
|
||||
if diff_collection.any?
|
||||
new_diffs = dump_diffs(diff_collection)
|
||||
self.state = :collected
|
||||
new_attributes[:state] = :collected
|
||||
end
|
||||
end
|
||||
|
||||
self.st_diffs = new_diffs
|
||||
new_attributes[:st_diffs] = new_diffs
|
||||
new_attributes[:base_commit_sha] = self.repository.merge_base(self.head, self.base)
|
||||
|
||||
self.base_commit_sha = self.repository.merge_base(self.head, self.base)
|
||||
|
||||
self.save
|
||||
update_columns_serialized(new_attributes)
|
||||
end
|
||||
|
||||
# Collect array of Git::Diff objects
|
||||
|
@ -190,4 +192,29 @@ class MergeRequestDiff < ActiveRecord::Base
|
|||
)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
#
|
||||
# #save or #update_attributes providing changes on serialized attributes do a lot of
|
||||
# serialization and deserialization calls resulting in bad performance.
|
||||
# Using #update_columns solves the problem with just one YAML.dump per serialized attribute that we provide.
|
||||
# As a tradeoff we need to reload the current instance to properly manage time objects on those serialized
|
||||
# attributes. So to keep the same behaviour as the attribute assignment we reload the instance.
|
||||
# The difference is in the usage of
|
||||
# #write_attribute= (#update_attributes) and #raw_write_attribute= (#update_columns)
|
||||
#
|
||||
# Ex:
|
||||
#
|
||||
# new_attributes[:st_commits].first.slice(:committed_date)
|
||||
# => {:committed_date=>2014-02-27 11:01:38 +0200}
|
||||
# YAML.load(YAML.dump(new_attributes[:st_commits].first.slice(:committed_date)))
|
||||
# => {:committed_date=>2014-02-27 10:01:38 +0100}
|
||||
#
|
||||
def update_columns_serialized(new_attributes)
|
||||
return unless new_attributes.any?
|
||||
|
||||
update_columns(new_attributes.merge(updated_at: current_time_from_proper_timezone))
|
||||
reload
|
||||
end
|
||||
end
|
||||
|
|
|
@ -21,8 +21,10 @@ class Namespace < ActiveRecord::Base
|
|||
|
||||
delegate :name, to: :owner, allow_nil: true, prefix: true
|
||||
|
||||
after_create :ensure_dir_exist
|
||||
after_update :move_dir, if: :path_changed?
|
||||
|
||||
# Save the storage paths before the projects are destroyed to use them on after destroy
|
||||
before_destroy(prepend: true) { @old_repository_storage_paths = repository_storage_paths }
|
||||
after_destroy :rm_dir
|
||||
|
||||
scope :root, -> { where('type IS NULL') }
|
||||
|
@ -87,34 +89,23 @@ class Namespace < ActiveRecord::Base
|
|||
owner_name
|
||||
end
|
||||
|
||||
def ensure_dir_exist
|
||||
gitlab_shell.add_namespace(path)
|
||||
end
|
||||
|
||||
def rm_dir
|
||||
# Move namespace directory into trash.
|
||||
# We will remove it later async
|
||||
new_path = "#{path}+#{id}+deleted"
|
||||
|
||||
if gitlab_shell.mv_namespace(path, new_path)
|
||||
message = "Namespace directory \"#{path}\" moved to \"#{new_path}\""
|
||||
Gitlab::AppLogger.info message
|
||||
|
||||
# Remove namespace directroy async with delay so
|
||||
# GitLab has time to remove all projects first
|
||||
GitlabShellWorker.perform_in(5.minutes, :rm_namespace, new_path)
|
||||
end
|
||||
end
|
||||
|
||||
def move_dir
|
||||
# Ensure old directory exists before moving it
|
||||
gitlab_shell.add_namespace(path_was)
|
||||
|
||||
if any_project_has_container_registry_tags?
|
||||
raise Exception.new('Namespace cannot be moved, because at least one project has tags in container registry')
|
||||
end
|
||||
|
||||
if gitlab_shell.mv_namespace(path_was, path)
|
||||
# Move the namespace directory in all storages paths used by member projects
|
||||
repository_storage_paths.each do |repository_storage_path|
|
||||
# Ensure old directory exists before moving it
|
||||
gitlab_shell.add_namespace(repository_storage_path, path_was)
|
||||
|
||||
unless gitlab_shell.mv_namespace(repository_storage_path, path_was, path)
|
||||
# if we cannot move namespace directory we should rollback
|
||||
# db changes in order to prevent out of sync between db and fs
|
||||
raise Exception.new('namespace directory cannot be moved')
|
||||
end
|
||||
end
|
||||
|
||||
Gitlab::UploadsTransfer.new.rename_namespace(path_was, path)
|
||||
|
||||
# If repositories moved successfully we need to
|
||||
|
@ -128,11 +119,6 @@ class Namespace < ActiveRecord::Base
|
|||
# us information about failing some of tasks
|
||||
false
|
||||
end
|
||||
else
|
||||
# if we cannot move namespace directory we should rollback
|
||||
# db changes in order to prevent out of sync between db and fs
|
||||
raise Exception.new('namespace directory cannot be moved')
|
||||
end
|
||||
end
|
||||
|
||||
def any_project_has_container_registry_tags?
|
||||
|
@ -152,4 +138,33 @@ class Namespace < ActiveRecord::Base
|
|||
def find_fork_of(project)
|
||||
projects.joins(:forked_project_link).find_by('forked_project_links.forked_from_project_id = ?', project.id)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def repository_storage_paths
|
||||
# We need to get the storage paths for all the projects, even the ones that are
|
||||
# pending delete. Unscoping also get rids of the default order, which causes
|
||||
# problems with SELECT DISTINCT.
|
||||
Project.unscoped do
|
||||
projects.select('distinct(repository_storage)').to_a.map(&:repository_storage_path)
|
||||
end
|
||||
end
|
||||
|
||||
def rm_dir
|
||||
# Remove the namespace directory in all storages paths used by member projects
|
||||
@old_repository_storage_paths.each do |repository_storage_path|
|
||||
# Move namespace directory into trash.
|
||||
# We will remove it later async
|
||||
new_path = "#{path}+#{id}+deleted"
|
||||
|
||||
if gitlab_shell.mv_namespace(repository_storage_path, path, new_path)
|
||||
message = "Namespace directory \"#{path}\" moved to \"#{new_path}\""
|
||||
Gitlab::AppLogger.info message
|
||||
|
||||
# Remove namespace directroy async with delay so
|
||||
# GitLab has time to remove all projects first
|
||||
GitlabShellWorker.perform_in(5.minutes, :rm_namespace, repository_storage_path, new_path)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -24,8 +24,12 @@ class Project < ActiveRecord::Base
|
|||
default_value_for :wiki_enabled, gitlab_config_features.wiki
|
||||
default_value_for :snippets_enabled, gitlab_config_features.snippets
|
||||
default_value_for :container_registry_enabled, gitlab_config_features.container_registry
|
||||
default_value_for(:repository_storage) { current_application_settings.repository_storage }
|
||||
default_value_for(:shared_runners_enabled) { current_application_settings.shared_runners_enabled }
|
||||
|
||||
after_create :ensure_dir_exist
|
||||
after_save :ensure_dir_exist, if: :namespace_id_changed?
|
||||
|
||||
# set last_activity_at to the same as created_at
|
||||
after_create :set_last_activity_at
|
||||
def set_last_activity_at
|
||||
|
@ -81,6 +85,7 @@ class Project < ActiveRecord::Base
|
|||
has_one :jira_service, dependent: :destroy
|
||||
has_one :redmine_service, dependent: :destroy
|
||||
has_one :custom_issue_tracker_service, dependent: :destroy
|
||||
has_one :bugzilla_service, dependent: :destroy
|
||||
has_one :gitlab_issue_tracker_service, dependent: :destroy, inverse_of: :project
|
||||
has_one :external_wiki_service, dependent: :destroy
|
||||
|
||||
|
@ -162,6 +167,9 @@ class Project < ActiveRecord::Base
|
|||
validate :visibility_level_allowed_by_group
|
||||
validate :visibility_level_allowed_as_fork
|
||||
validate :check_wiki_path_conflict
|
||||
validates :repository_storage,
|
||||
presence: true,
|
||||
inclusion: { in: ->(_object) { Gitlab.config.repositories.storages.keys } }
|
||||
|
||||
add_authentication_token_field :runners_token
|
||||
before_save :ensure_runners_token
|
||||
|
@ -373,6 +381,10 @@ class Project < ActiveRecord::Base
|
|||
end
|
||||
end
|
||||
|
||||
def repository_storage_path
|
||||
Gitlab.config.repositories.storages[repository_storage]
|
||||
end
|
||||
|
||||
def team
|
||||
@team ||= ProjectTeam.new(self)
|
||||
end
|
||||
|
@ -841,12 +853,12 @@ class Project < ActiveRecord::Base
|
|||
raise Exception.new('Project cannot be renamed, because tags are present in its container registry')
|
||||
end
|
||||
|
||||
if gitlab_shell.mv_repository(old_path_with_namespace, new_path_with_namespace)
|
||||
if gitlab_shell.mv_repository(repository_storage_path, old_path_with_namespace, new_path_with_namespace)
|
||||
# If repository moved successfully we need to send update instructions to users.
|
||||
# However we cannot allow rollback since we moved repository
|
||||
# So we basically we mute exceptions in next actions
|
||||
begin
|
||||
gitlab_shell.mv_repository("#{old_path_with_namespace}.wiki", "#{new_path_with_namespace}.wiki")
|
||||
gitlab_shell.mv_repository(repository_storage_path, "#{old_path_with_namespace}.wiki", "#{new_path_with_namespace}.wiki")
|
||||
send_move_instructions(old_path_with_namespace)
|
||||
reset_events_cache
|
||||
|
||||
|
@ -987,7 +999,7 @@ class Project < ActiveRecord::Base
|
|||
def create_repository
|
||||
# Forked import is handled asynchronously
|
||||
unless forked?
|
||||
if gitlab_shell.add_repository(path_with_namespace)
|
||||
if gitlab_shell.add_repository(repository_storage_path, path_with_namespace)
|
||||
repository.after_create
|
||||
true
|
||||
else
|
||||
|
@ -1139,4 +1151,8 @@ class Project < ActiveRecord::Base
|
|||
_, status = Gitlab::Popen.popen(%W(find #{export_path} -not -path #{export_path} -delete))
|
||||
status.zero?
|
||||
end
|
||||
|
||||
def ensure_dir_exist
|
||||
gitlab_shell.add_namespace(repository_storage_path, namespace.path)
|
||||
end
|
||||
end
|
||||
|
|
25
app/models/project_services/bugzilla_service.rb
Normal file
|
@ -0,0 +1,25 @@
|
|||
class BugzillaService < IssueTrackerService
|
||||
|
||||
prop_accessor :title, :description, :project_url, :issues_url, :new_issue_url
|
||||
|
||||
def title
|
||||
if self.properties && self.properties['title'].present?
|
||||
self.properties['title']
|
||||
else
|
||||
'Bugzilla'
|
||||
end
|
||||
end
|
||||
|
||||
def description
|
||||
if self.properties && self.properties['description'].present?
|
||||
self.properties['description']
|
||||
else
|
||||
'Bugzilla issue tracker'
|
||||
end
|
||||
end
|
||||
|
||||
def to_param
|
||||
'bugzilla'
|
||||
end
|
||||
|
||||
end
|
|
@ -32,7 +32,4 @@ class CustomIssueTrackerService < IssueTrackerService
|
|||
]
|
||||
end
|
||||
|
||||
def initialize_properties
|
||||
self.properties = {} if properties.nil?
|
||||
end
|
||||
end
|
||||
|
|
|
@ -137,20 +137,10 @@ class ProjectTeam
|
|||
def max_member_access(user_id)
|
||||
access = []
|
||||
|
||||
project.members.non_request.each do |member|
|
||||
if member.user_id == user_id
|
||||
access << member.access_field if member.access_field
|
||||
break
|
||||
end
|
||||
end
|
||||
access += project.members.non_request.where(user_id: user_id).has_access.pluck(:access_level)
|
||||
|
||||
if group
|
||||
group.members.non_request.each do |member|
|
||||
if member.user_id == user_id
|
||||
access << member.access_field if member.access_field
|
||||
break
|
||||
end
|
||||
end
|
||||
access += group.members.non_request.where(user_id: user_id).has_access.pluck(:access_level)
|
||||
end
|
||||
|
||||
if project.invited_groups.any? && project.allowed_to_share_with_group?
|
||||
|
|
|
@ -159,7 +159,7 @@ class ProjectWiki
|
|||
private
|
||||
|
||||
def init_repo(path_with_namespace)
|
||||
gitlab_shell.add_repository(path_with_namespace)
|
||||
gitlab_shell.add_repository(project.repository_storage_path, path_with_namespace)
|
||||
end
|
||||
|
||||
def commit_details(action, message = nil, title = nil)
|
||||
|
@ -173,7 +173,7 @@ class ProjectWiki
|
|||
end
|
||||
|
||||
def path_to_repo
|
||||
@path_to_repo ||= File.join(Gitlab.config.gitlab_shell.repos_path, "#{path_with_namespace}.git")
|
||||
@path_to_repo ||= File.join(project.repository_storage_path, "#{path_with_namespace}.git")
|
||||
end
|
||||
|
||||
def update_project_activity
|
||||
|
|
|
@ -39,7 +39,7 @@ class Repository
|
|||
# Return absolute path to repository
|
||||
def path_to_repo
|
||||
@path_to_repo ||= File.expand_path(
|
||||
File.join(Gitlab.config.gitlab_shell.repos_path, path_with_namespace + ".git")
|
||||
File.join(@project.repository_storage_path, path_with_namespace + ".git")
|
||||
)
|
||||
end
|
||||
|
||||
|
|
|
@ -170,6 +170,7 @@ class Service < ActiveRecord::Base
|
|||
bamboo
|
||||
buildkite
|
||||
builds_email
|
||||
bugzilla
|
||||
campfire
|
||||
custom_issue_tracker
|
||||
drone_ci
|
||||
|
|
|
@ -51,13 +51,13 @@ module Projects
|
|||
return true if params[:skip_repo] == true
|
||||
|
||||
# There is a possibility project does not have repository or wiki
|
||||
return true unless gitlab_shell.exists?(path + '.git')
|
||||
return true unless gitlab_shell.exists?(project.repository_storage_path, path + '.git')
|
||||
|
||||
new_path = removal_path(path)
|
||||
|
||||
if gitlab_shell.mv_repository(path, new_path)
|
||||
if gitlab_shell.mv_repository(project.repository_storage_path, path, new_path)
|
||||
log_info("Repository \"#{path}\" moved to \"#{new_path}\"")
|
||||
GitlabShellWorker.perform_in(5.minutes, :remove_repository, new_path)
|
||||
GitlabShellWorker.perform_in(5.minutes, :remove_repository, project.repository_storage_path, new_path)
|
||||
else
|
||||
false
|
||||
end
|
||||
|
|
|
@ -24,7 +24,7 @@ module Projects
|
|||
def execute
|
||||
raise LeaseTaken unless try_obtain_lease
|
||||
|
||||
GitlabShellOneShotWorker.perform_async(:gc, @project.path_with_namespace)
|
||||
GitlabShellOneShotWorker.perform_async(:gc, @project.repository_storage_path, @project.path_with_namespace)
|
||||
ensure
|
||||
Gitlab::Metrics.measure(:reset_pushes_since_gc) do
|
||||
@project.update_column(:pushes_since_gc, 0)
|
||||
|
|
|
@ -42,7 +42,7 @@ module Projects
|
|||
|
||||
def import_repository
|
||||
begin
|
||||
gitlab_shell.import_repository(project.path_with_namespace, project.import_url)
|
||||
gitlab_shell.import_repository(project.repository_storage_path, project.path_with_namespace, project.import_url)
|
||||
rescue Gitlab::Shell::Error => e
|
||||
raise Error, "Error importing repository #{project.import_url} into #{project.path_with_namespace} - #{e.message}"
|
||||
end
|
||||
|
|
|
@ -50,12 +50,12 @@ module Projects
|
|||
project.send_move_instructions(old_path)
|
||||
|
||||
# Move main repository
|
||||
unless gitlab_shell.mv_repository(old_path, new_path)
|
||||
unless gitlab_shell.mv_repository(project.repository_storage_path, old_path, new_path)
|
||||
raise TransferError.new('Cannot move project')
|
||||
end
|
||||
|
||||
# Move wiki repo also if present
|
||||
gitlab_shell.mv_repository("#{old_path}.wiki", "#{new_path}.wiki")
|
||||
gitlab_shell.mv_repository(project.repository_storage_path, "#{old_path}.wiki", "#{new_path}.wiki")
|
||||
|
||||
# clear project cached events
|
||||
project.reset_events_cache
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
= f.label :default_snippet_visibility, class: 'control-label col-sm-2'
|
||||
.col-sm-10
|
||||
= render('shared/visibility_radios', model_method: :default_snippet_visibility, form: f, selected_level: @application_setting.default_snippet_visibility, form_model: ProjectSnippet.new)
|
||||
.form-group.group-visibility-level-holder
|
||||
.form-group.project-visibility-level-holder
|
||||
= f.label :default_group_visibility, class: 'control-label col-sm-2'
|
||||
.col-sm-10
|
||||
= render('shared/visibility_radios', model_method: :default_group_visibility, form: f, selected_level: @application_setting.default_group_visibility, form_model: Group.new)
|
||||
|
@ -310,6 +310,15 @@
|
|||
.col-sm-10
|
||||
= f.text_field :sentry_dsn, class: 'form-control'
|
||||
|
||||
%fieldset
|
||||
%legend Repository Storage
|
||||
.form-group
|
||||
= f.label :repository_storage, 'Storage path for new projects', class: 'control-label col-sm-2'
|
||||
.col-sm-10
|
||||
= f.select :repository_storage, repository_storage_options_for_select, {}, class: 'form-control'
|
||||
.help-block
|
||||
You can manage the repository storage paths in your gitlab.yml configuration file
|
||||
|
||||
%fieldset
|
||||
%legend Repository Checks
|
||||
.form-group
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
.nav-links.sub-nav
|
||||
%ul{ class: (container_class) }
|
||||
= nav_link(controller: :system_info) do
|
||||
= link_to admin_system_info_path, title: 'System Info' do
|
||||
%span
|
||||
System Info
|
||||
= nav_link(controller: :background_jobs) do
|
||||
= link_to admin_background_jobs_path, title: 'Background Jobs' do
|
||||
%span
|
||||
|
|
22
app/views/admin/system_info/show.html.haml
Normal file
|
@ -0,0 +1,22 @@
|
|||
- @no_container = true
|
||||
- page_title "System Info"
|
||||
= render 'admin/background_jobs/head'
|
||||
|
||||
%div{ class: (container_class) }
|
||||
.prepend-top-default
|
||||
.row
|
||||
.col-sm-4
|
||||
.light-well
|
||||
%h4 CPU
|
||||
.data
|
||||
%h1= "#{@cpus} cores"
|
||||
.col-sm-4
|
||||
.light-well
|
||||
%h4 Memory
|
||||
.data
|
||||
%h1= "#{number_to_human_size(@mem_used)} / #{number_to_human_size(@mem_total)}"
|
||||
.col-sm-4
|
||||
.light-well
|
||||
%h4 Disk
|
||||
.data
|
||||
%h1= "#{number_to_human_size(@disk_used)} / #{number_to_human_size(@disk_total)}"
|
|
@ -1,3 +1,5 @@
|
|||
- project = event.project
|
||||
|
||||
.event-title
|
||||
%span.author_name= link_to_author event
|
||||
%span.event_label.pushed #{event.action_name} #{event.ref_type}
|
||||
|
@ -5,19 +7,18 @@
|
|||
%strong= event.ref_name
|
||||
- else
|
||||
%strong
|
||||
= link_to event.ref_name, namespace_project_commits_path(event.project.namespace, event.project, event.ref_name), title: h(event.target_title)
|
||||
= link_to event.ref_name, namespace_project_commits_path(project.namespace, project, event.ref_name), title: h(event.target_title)
|
||||
at
|
||||
= link_to_project event.project
|
||||
= link_to_project project
|
||||
|
||||
- if event.push_with_commits?
|
||||
- project = event.project
|
||||
.event-body
|
||||
%ul.well-list.event_commits
|
||||
- few_commits = event.commits[0...2]
|
||||
- few_commits.each do |commit|
|
||||
= render "events/commit", commit: commit, project: project, event: event
|
||||
|
||||
- create_mr = event.new_ref? && create_mr_button?(event.project.default_branch, event.ref_name, event.project)
|
||||
- create_mr = event.new_ref? && create_mr_button?(project.default_branch, event.ref_name, project)
|
||||
- if event.commits_count > 1
|
||||
%li.commits-stat
|
||||
- if event.commits_count > 2
|
||||
|
@ -27,18 +28,26 @@
|
|||
- from = event.commit_from
|
||||
- from_label = truncate_sha(from)
|
||||
- else
|
||||
- from = event.project.default_branch
|
||||
- from = project.default_branch
|
||||
- from_label = from
|
||||
|
||||
= link_to namespace_project_compare_path(event.project.namespace, event.project, from: from, to: event.commit_to) do
|
||||
= link_to namespace_project_compare_path(project.namespace, project, from: from, to: event.commit_to) do
|
||||
Compare #{from_label}...#{truncate_sha(event.commit_to)}
|
||||
|
||||
- if create_mr
|
||||
%span{"data-user-is" => event.author_id, "data-display" => "inline"}
|
||||
or
|
||||
= link_to create_mr_path(event.project.default_branch, event.ref_name, event.project) do
|
||||
= link_to create_mr_path(project.default_branch, event.ref_name, project) do
|
||||
create a merge request
|
||||
- elsif create_mr
|
||||
%li.commits-stat{"data-user-is" => event.author_id}
|
||||
= link_to create_mr_path(event.project.default_branch, event.ref_name, event.project) do
|
||||
= link_to create_mr_path(project.default_branch, event.ref_name, project) do
|
||||
Create Merge Request
|
||||
- elsif event.rm_ref?
|
||||
- repository = project.repository
|
||||
- last_commit = repository.commit(event.commit_from)
|
||||
- if last_commit
|
||||
.event-body
|
||||
%ul.well-list.event_commits
|
||||
= render "events/commit", commit: last_commit, project: project, event: event
|
||||
|
||||
|
|
|
@ -15,12 +15,17 @@
|
|||
%span.visibility-icon.has-tooltip{ data: { container: 'body' }, title: visibility_icon_description(@group) }
|
||||
= visibility_level_icon(@group.visibility_level, fw: false)
|
||||
|
||||
%span.hidden-xs
|
||||
= render 'shared/notifications/button', notification_setting: @notification_setting
|
||||
|
||||
- if current_user
|
||||
.pull-right
|
||||
= render 'shared/members/access_request_buttons', source: @group
|
||||
|
||||
- if @group.description.present?
|
||||
.cover-desc.description
|
||||
= markdown(@group.description, pipeline: :description)
|
||||
|
||||
- if current_user
|
||||
= render 'shared/members/access_request_buttons', source: @group
|
||||
|
||||
%div{ class: container_class }
|
||||
.top-area
|
||||
|
|
|
@ -36,6 +36,6 @@
|
|||
%ul.well-list
|
||||
%li= link_to 'See our website for getting help', promo_url + '/getting-help/'
|
||||
%li= link_to 'Use the search bar on the top of this page', '#', onclick: 'Shortcuts.focusSearch(event)'
|
||||
%li= link_to 'Use shortcuts', '#', onclick: 'Shortcuts.showHelp(event)'
|
||||
%li= link_to 'Use shortcuts', '#', onclick: 'Shortcuts.toggleHelp()'
|
||||
%li= link_to 'Get a support subscription', 'https://about.gitlab.com/pricing/'
|
||||
%li= link_to 'Compare GitLab editions', 'https://about.gitlab.com/features/#compare'
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
= icon('bars')
|
||||
%button.navbar-toggle{type: 'button'}
|
||||
%span.sr-only Toggle navigation
|
||||
= icon('angle-left')
|
||||
= icon('ellipsis-v')
|
||||
|
||||
.navbar-collapse.collapse
|
||||
%ul.nav.navbar-nav
|
||||
|
|
|
@ -1,16 +1,16 @@
|
|||
.scrolling-tabs-container{ class: nav_control_class }
|
||||
= render 'layouts/nav/admin_settings'
|
||||
.fade-left
|
||||
= icon('arrow-left')
|
||||
= icon('angle-left')
|
||||
.fade-right
|
||||
= icon('arrow-right')
|
||||
= icon('angle-right')
|
||||
%ul.nav-links.scrolling-tabs
|
||||
= nav_link(controller: %w(dashboard admin projects users groups builds runners), html_options: {class: 'home'}) do
|
||||
= link_to admin_root_path, title: 'Overview', class: 'shortcuts-tree' do
|
||||
%span
|
||||
Overview
|
||||
= nav_link(controller: %w(background_jobs logs health_check)) do
|
||||
= link_to admin_background_jobs_path, title: 'Monitoring' do
|
||||
= nav_link(controller: %w(system_info background_jobs logs health_check)) do
|
||||
= link_to admin_system_info_path, title: 'Monitoring' do
|
||||
%span
|
||||
Monitoring
|
||||
= nav_link(controller: :broadcast_messages) do
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
.scrolling-tabs-container{ class: nav_control_class }
|
||||
= render 'layouts/nav/group_settings'
|
||||
.fade-left
|
||||
= icon('arrow-left')
|
||||
= icon('angle-left')
|
||||
.fade-right
|
||||
= icon('arrow-right')
|
||||
= icon('angle-right')
|
||||
%ul.nav-links.scrolling-tabs
|
||||
= nav_link(path: 'groups#show', html_options: {class: 'home'}) do
|
||||
= link_to group_path(@group), title: 'Home' do
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
.scrolling-tabs-container
|
||||
.fade-left
|
||||
= icon('arrow-left')
|
||||
= icon('angle-left')
|
||||
.fade-right
|
||||
= icon('arrow-right')
|
||||
= icon('angle-right')
|
||||
%ul.nav-links.scrolling-tabs
|
||||
= nav_link(path: 'profiles#show', html_options: {class: 'home'}) do
|
||||
= link_to profile_path, title: 'Profile Settings' do
|
||||
|
|
|
@ -26,9 +26,9 @@
|
|||
|
||||
.scrolling-tabs-container{ class: nav_control_class }
|
||||
.fade-left
|
||||
= icon('arrow-left')
|
||||
= icon('angle-left')
|
||||
.fade-right
|
||||
= icon('arrow-right')
|
||||
= icon('angle-right')
|
||||
%ul.nav-links.scrolling-tabs
|
||||
= nav_link(path: 'projects#show', html_options: {class: 'home'}) do
|
||||
= link_to project_path(@project), title: 'Project', class: 'shortcuts-project' do
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
= markdown_toolbar_button({icon: "bold fw", data: { "md-tag" => "**" }, title: "Add bold text" })
|
||||
= markdown_toolbar_button({icon: "italic fw", data: { "md-tag" => "*" }, title: "Add italic text" })
|
||||
= markdown_toolbar_button({icon: "quote-right fw", data: { "md-tag" => "> ", "md-prepend" => true }, title: "Insert a quote" })
|
||||
= markdown_toolbar_button({icon: "code fw", data: { "md-tag" => "`" }, title: "Insert code" })
|
||||
= markdown_toolbar_button({icon: "code fw", data: { "md-tag" => "`", "md-block" => "```" }, title: "Insert code" })
|
||||
= markdown_toolbar_button({icon: "list-ul fw", data: { "md-tag" => "* ", "md-prepend" => true }, title: "Add a bullet list" })
|
||||
= markdown_toolbar_button({icon: "list-ol fw", data: { "md-tag" => "1. ", "md-prepend" => true }, title: "Add a numbered list" })
|
||||
= markdown_toolbar_button({icon: "check-square-o fw", data: { "md-tag" => "* [ ] ", "md-prepend" => true }, title: "Add a task list" })
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
- @no_container = true
|
||||
- page_title @blob.path, @ref
|
||||
= render "projects/commits/head"
|
||||
|
||||
%div{ class: (container_class) }
|
||||
= render 'projects/last_push'
|
||||
|
||||
%div#tree-holder.tree-holder
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
.scrolling-tabs-container.sub-nav-scroll
|
||||
.fade-left
|
||||
= icon('arrow-left')
|
||||
= icon('angle-left')
|
||||
.fade-right
|
||||
= icon('arrow-right')
|
||||
= icon('angle-right')
|
||||
.nav-links.sub-nav.scrolling-tabs
|
||||
%ul{ class: (container_class) }
|
||||
= nav_link(controller: %w(tree blob blame edit_tree new_tree find_file)) do
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
.panel-body
|
||||
%pre
|
||||
:preserve
|
||||
#{sanitize_repo_path(@project.import_error)}
|
||||
#{sanitize_repo_path(@project, @project.import_error)}
|
||||
|
||||
= form_for @project, url: namespace_project_import_path(@project.namespace, @project), method: :post, html: { class: 'form-horizontal' } do |f|
|
||||
= render "shared/import_form", f: f
|
||||
|
|
|
@ -1,38 +1,48 @@
|
|||
- page_title 'New Project'
|
||||
- header_title "Projects", dashboard_projects_path
|
||||
|
||||
%h3.page-title
|
||||
New Project
|
||||
%hr
|
||||
|
||||
.project-edit-container
|
||||
.project-edit-errors
|
||||
= render 'projects/errors'
|
||||
.project-edit-content
|
||||
|
||||
= form_for @project, html: { class: 'new_project form-horizontal js-requires-input' } do |f|
|
||||
.row.prepend-top-default
|
||||
.col-lg-3.profile-settings-sidebar
|
||||
%h4.prepend-top-0
|
||||
New project
|
||||
%p
|
||||
Create or Import your project from popular Git services
|
||||
.col-lg-9
|
||||
= form_for @project, html: { class: 'new_project' } do |f|
|
||||
%fieldset.append-bottom-0
|
||||
.form-group.col-xs-12.col-sm-6
|
||||
= f.label :namespace_id, class: 'label-light' do
|
||||
%span
|
||||
Project path
|
||||
.form-group
|
||||
= f.label :path, class: 'control-label' do
|
||||
Project owner
|
||||
.col-sm-10
|
||||
= f.select :namespace_id, namespaces_options(:current_user), {}, {class: 'select2 js-select-namespace', tabindex: 1}
|
||||
|
||||
.input-group
|
||||
- if current_user.can_select_namespace?
|
||||
.input-group-addon
|
||||
= root_url
|
||||
= f.select :namespace_id, namespaces_options(params[:namespace_id] || :current_user, display_path: true), {}, {class: 'select2 js-select-namespace', tabindex: 1}
|
||||
- else
|
||||
.input-group-addon.static-namespace
|
||||
#{root_url}#{current_user.username}/
|
||||
.form-group.col-xs-12.col-sm-6.project-path
|
||||
= f.label :namespace_id, class: 'label-light' do
|
||||
%span
|
||||
Project name
|
||||
= f.text_field :path, placeholder: "my-awesome-project", class: "form-control", tabindex: 2, autofocus: true, required: true
|
||||
- if current_user.can_create_group?
|
||||
.help-block
|
||||
Want to house several dependent projects under the same namespace?
|
||||
= link_to "Create a group", new_group_path
|
||||
|
||||
.form-group
|
||||
= f.label :path, class: 'control-label' do
|
||||
Project name
|
||||
.col-sm-10
|
||||
= f.text_field :path, placeholder: "my-awesome-project", class: "form-control", tabindex: 2, autofocus: true, required: true
|
||||
|
||||
- if import_sources_enabled?
|
||||
.project-import.js-toggle-container
|
||||
.form-group
|
||||
%label.control-label Import project from
|
||||
.col-sm-10
|
||||
.form-group.clearfix
|
||||
= f.label :visibility_level, class: 'label-light' do
|
||||
Import project from
|
||||
.col-sm-12.import-buttons
|
||||
%div
|
||||
- if github_import_enabled?
|
||||
- if github_import_configured?
|
||||
= link_to status_import_github_path, class: 'btn import_github' do
|
||||
|
@ -43,7 +53,7 @@
|
|||
%i.fa.fa-github
|
||||
GitHub
|
||||
= render 'github_import_modal'
|
||||
|
||||
%div
|
||||
- if bitbucket_import_enabled?
|
||||
- if bitbucket_import_configured?
|
||||
= link_to status_import_bitbucket_path, class: 'btn import_bitbucket', "data-no-turbolink" => "true" do
|
||||
|
@ -54,7 +64,7 @@
|
|||
%i.fa.fa-bitbucket
|
||||
Bitbucket
|
||||
= render 'bitbucket_import_modal'
|
||||
|
||||
%div
|
||||
- if gitlab_import_enabled?
|
||||
- if gitlab_import_configured?
|
||||
= link_to status_import_gitlab_path, class: 'btn import_gitlab' do
|
||||
|
@ -65,27 +75,27 @@
|
|||
%i.fa.fa-heart
|
||||
GitLab.com
|
||||
= render 'gitlab_import_modal'
|
||||
|
||||
%div
|
||||
- if gitorious_import_enabled?
|
||||
= link_to new_import_gitorious_path, class: 'btn import_gitorious' do
|
||||
%i.icon-gitorious.icon-gitorious-small
|
||||
Gitorious.org
|
||||
|
||||
%div
|
||||
- if google_code_import_enabled?
|
||||
= link_to new_import_google_code_path, class: 'btn import_google_code' do
|
||||
%i.fa.fa-google
|
||||
Google Code
|
||||
|
||||
%div
|
||||
- if fogbugz_import_enabled?
|
||||
= link_to new_import_fogbugz_path, class: 'btn import_fogbugz' do
|
||||
%i.fa.fa-bug
|
||||
Fogbugz
|
||||
|
||||
%div
|
||||
- if git_import_enabled?
|
||||
= link_to "#", class: 'btn js-toggle-button import_git' do
|
||||
%i.fa.fa-git
|
||||
%span Repo by URL
|
||||
|
||||
%div
|
||||
- if gitlab_project_import_enabled?
|
||||
= link_to new_import_gitlab_project_path, class: 'btn import_gitlab_project project-submit' do
|
||||
%i.fa.fa-gitlab
|
||||
|
@ -94,17 +104,18 @@
|
|||
.js-toggle-content.hide
|
||||
= render "shared/import_form", f: f
|
||||
|
||||
.prepend-botton-10
|
||||
|
||||
.form-group
|
||||
= f.label :description, class: 'control-label' do
|
||||
Description
|
||||
= f.label :description, class: 'label-light' do
|
||||
Project description
|
||||
%span.light (optional)
|
||||
.col-sm-10
|
||||
= f.text_area :description, class: "form-control", rows: 3, maxlength: 250, tabindex: 3
|
||||
= render 'shared/visibility_level', f: f, visibility_level: default_project_visibility, can_change_visibility_level: true, form_model: @project
|
||||
= f.text_area :description, placeholder: 'Description format', class: "form-control", rows: 3, maxlength: 250
|
||||
|
||||
.form-group.project-visibility-level-holder
|
||||
= f.label :visibility_level, class: 'label-light' do
|
||||
Visibility Level
|
||||
= link_to "(?)", help_page_path("public_access", "public_access")
|
||||
= render('shared/visibility_radios', model_method: :visibility_level, form: f, selected_level: @project.visibility_level, form_model: @project)
|
||||
|
||||
.form-actions
|
||||
= f.submit 'Create project', class: "btn btn-create project-submit", tabindex: 4
|
||||
= link_to 'Cancel', dashboard_projects_path, class: 'btn btn-cancel'
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
%a{ href: "##{dom_id(note)}" }
|
||||
= time_ago_with_tooltip(note.created_at, placement: 'bottom', html_class: 'note-created-ago')
|
||||
.note-actions
|
||||
- access = note.project.team.human_max_access(note.author.id)
|
||||
- access = note_max_access_for_user(note)
|
||||
- if access and not note.system
|
||||
%span.note-role.hidden-xs= access
|
||||
- if current_user and not note.system
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
- labels.each do |label|
|
||||
%span.label-row.btn-group{ role: "group", aria: { label: escape_once(label.name) }, style: "color: #{text_color_for_bg(label.color)}" }
|
||||
= link_to namespace_project_label_path(@project.namespace, @project, label),
|
||||
= link_to label_filter_path(@project, label, type: controller.controller_name),
|
||||
class: "btn btn-transparent has-tooltip",
|
||||
style: "background-color: #{label.color};",
|
||||
title: escape_once(label.description),
|
||||
|
|