Merge branch 'master' of gitlab.com:gitlab-org/gitlab-ce into fix/import-url-validator
This commit is contained in:
commit
26b612258f
324 changed files with 3806 additions and 1799 deletions
20
CHANGELOG
20
CHANGELOG
|
@ -2,6 +2,7 @@ Please view this file on the master branch, on stable branches it's out of date.
|
|||
|
||||
v 8.9.0 (unreleased)
|
||||
- Set import_url validation to be more strict
|
||||
- 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
|
||||
- Fix Error 500 when using closes_issues API with an external issue tracker
|
||||
|
@ -9,17 +10,22 @@ v 8.9.0 (unreleased)
|
|||
- Bulk assign/unassign labels to issues.
|
||||
- Ability to prioritize labels !4009 / !3205 (Thijs Wouters)
|
||||
- Show Star and Fork buttons on mobile.
|
||||
- Performance improvements on RelativeLinkFilter
|
||||
- Fix endless redirections when accessing user OAuth applications when they are disabled
|
||||
- Allow enabling wiki page events from Webhook management UI
|
||||
- Bump rouge to 1.11.0
|
||||
- Fix MR-auto-close text added to description
|
||||
- Fix issue with arrow keys not working in search autocomplete dropdown
|
||||
- Fix an issue where note polling stopped working if a window was in the
|
||||
background during a refresh.
|
||||
- Pre-processing Markdown now only happens when needed
|
||||
- Make EmailsOnPushWorker use Sidekiq mailers queue
|
||||
- Redesign all Devise emails. !4297
|
||||
- Don't show 'Leave Project' to group members
|
||||
- Fix wiki page events' webhook to point to the wiki repository
|
||||
- Add a border around images to differentiate them from the background.
|
||||
- Don't show tags for revert and cherry-pick operations
|
||||
- Show image ID on registry page
|
||||
- Fix issue todo not remove when leave project !4150 (Long Nguyen)
|
||||
- Allow customisable text on the 'nearly there' page after a user signs up
|
||||
- Bump recaptcha gem to 3.0.0 to remove deprecated stoken support
|
||||
|
@ -32,6 +38,7 @@ v 8.9.0 (unreleased)
|
|||
- Implement a fair usage of shared runners
|
||||
- Remove project notification settings associated with deleted projects
|
||||
- Fix 404 page when viewing TODOs that contain milestones or labels in different projects
|
||||
- Wrap code blocks on Activies and Todos page !4783 (winniehell)
|
||||
- Add a metric for the number of new Redis connections created by a transaction
|
||||
- Fix Error 500 when viewing a blob with binary characters after the 1024-byte mark
|
||||
- Redesign navigation for project pages
|
||||
|
@ -56,6 +63,7 @@ v 8.9.0 (unreleased)
|
|||
- Add `sha` parameter to MR merge API, to ensure only reviewed changes are merged
|
||||
- Don't allow MRs to be merged when commits were added since the last review / page load
|
||||
- Add DB index on users.state
|
||||
- Limit email on push diff size to 30 files / 150 KB
|
||||
- Add rake task 'gitlab:db:configure' for conditionally seeding or migrating the database
|
||||
- Changed the Slack build message to use the singular duration if necessary (Aran Koning)
|
||||
- Fix race condition on merge when build succeeds
|
||||
|
@ -70,6 +78,7 @@ v 8.9.0 (unreleased)
|
|||
- Todos will display target state if issuable target is 'Closed' or 'Merged'
|
||||
- Validate only and except regexp
|
||||
- Fix bug when sorting issues by milestone due date and filtering by two or more labels
|
||||
- POST to API /projects/:id/runners/:runner_id would give 409 if the runner was already enabled for this project
|
||||
- Add support for using Yubikeys (U2F) for two-factor authentication
|
||||
- Link to blank group icon doesn't throw a 404 anymore
|
||||
- Remove 'main language' feature
|
||||
|
@ -77,13 +86,16 @@ v 8.9.0 (unreleased)
|
|||
- Pipelines can be canceled only when there are running builds
|
||||
- Allow authentication using personal access tokens
|
||||
- Use downcased path to container repository as this is expected path by Docker
|
||||
- Allow to use CI token to fetch LFS objects
|
||||
- Custom notification settings
|
||||
- Projects pending deletion will render a 404 page
|
||||
- Measure queue duration between gitlab-workhorse and Rails
|
||||
- Added Gfm autocomplete for labels
|
||||
- Added edit note 'up' shortcut documentation to the help panel and docs screenshot #18114
|
||||
- Make Omniauth providers specs to not modify global configuration
|
||||
- Remove unused JiraIssue class and replace references with ExternalIssue. !4659 (Ilan Shamir)
|
||||
- Make authentication service for Container Registry to be compatible with < Docker 1.11
|
||||
- Make it possible to lock a runner from being enabled for other projects
|
||||
- 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
|
||||
|
@ -101,6 +113,7 @@ v 8.9.0 (unreleased)
|
|||
- An indicator is now displayed at the top of the comment field for confidential issues.
|
||||
- Show categorised search queries in the search autocomplete
|
||||
- RepositoryCheck::SingleRepositoryWorker public and private methods are now instrumented
|
||||
- Dropdown for `.gitlab-ci.yml` templates
|
||||
- Improve issuables APIs performance when accessing notes !4471
|
||||
- Add sorting dropdown to tags page !4423
|
||||
- External links now open in a new tab
|
||||
|
@ -129,10 +142,17 @@ v 8.9.0 (unreleased)
|
|||
- Various associations are now eager loaded when parsing issue references to reduce the number of queries executed
|
||||
- Set inverse_of for Project/Service association to reduce the number of queries
|
||||
- Update tanuki logo highlight/loading colors
|
||||
- Remove explicit Gitlab::Metrics.action assignments, are already automatic.
|
||||
- Use Git cached counters for branches and tags on project page
|
||||
- Cache participable participants in an instance variable.
|
||||
- Filter parameters for request_uri value on instrumented transactions.
|
||||
- Remove duplicated keys add UNIQUE index to keys fingerprint column
|
||||
- ExtractsPath get ref_names from repository cache, if not there access git.
|
||||
- Cache user todo counts from TodoService
|
||||
- Ensure Todos counters doesn't count Todos for projects pending delete
|
||||
- Add left/right arrows horizontal navigation
|
||||
- Add tooltip to pin/unpin navbar
|
||||
- Add new sub nav style to Wiki and Graphs sub navigation
|
||||
|
||||
v 8.8.5
|
||||
- Import GitHub repositories respecting the API rate limit !4166
|
||||
|
|
4
Gemfile
4
Gemfile
|
@ -48,7 +48,7 @@ gem 'attr_encrypted', '~> 3.0.0'
|
|||
gem 'u2f', '~> 0.2.1'
|
||||
|
||||
# Browser detection
|
||||
gem "browser", '~> 2.0.3'
|
||||
gem "browser", '~> 2.2'
|
||||
|
||||
# Extracting information from a git repository
|
||||
# Provide access to Gitlab::Git library
|
||||
|
@ -330,7 +330,7 @@ gem "newrelic_rpm", '~> 3.14'
|
|||
|
||||
gem 'octokit', '~> 4.3.0'
|
||||
|
||||
gem "mail_room", "~> 0.7"
|
||||
gem "mail_room", "~> 0.8"
|
||||
|
||||
gem 'email_reply_parser', '~> 0.5.8'
|
||||
|
||||
|
|
|
@ -98,7 +98,7 @@ GEM
|
|||
autoprefixer-rails (>= 5.2.1)
|
||||
sass (>= 3.3.4)
|
||||
brakeman (3.3.2)
|
||||
browser (2.0.3)
|
||||
browser (2.2.0)
|
||||
builder (3.2.2)
|
||||
bullet (5.0.0)
|
||||
activesupport (>= 3.0.0)
|
||||
|
@ -398,7 +398,7 @@ GEM
|
|||
systemu (~> 2.6.2)
|
||||
mail (2.6.4)
|
||||
mime-types (>= 1.16, < 4)
|
||||
mail_room (0.7.0)
|
||||
mail_room (0.8.0)
|
||||
method_source (0.8.2)
|
||||
mime-types (2.99.2)
|
||||
mimemagic (0.3.0)
|
||||
|
@ -833,7 +833,7 @@ DEPENDENCIES
|
|||
binding_of_caller (~> 0.7.2)
|
||||
bootstrap-sass (~> 3.3.0)
|
||||
brakeman (~> 3.3.0)
|
||||
browser (~> 2.0.3)
|
||||
browser (~> 2.2)
|
||||
bullet
|
||||
bundler-audit
|
||||
byebug
|
||||
|
@ -899,7 +899,7 @@ DEPENDENCIES
|
|||
license_finder
|
||||
licensee (~> 8.0.0)
|
||||
loofah (~> 2.0.3)
|
||||
mail_room (~> 0.7)
|
||||
mail_room (~> 0.8)
|
||||
method_source (~> 0.8)
|
||||
minitest (~> 5.7.0)
|
||||
mousetrap-rails (~> 1.4.6)
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
labelsPath: "/api/:version/projects/:id/labels"
|
||||
licensePath: "/api/:version/licenses/:key"
|
||||
gitignorePath: "/api/:version/gitignores/:key"
|
||||
gitlabCiYmlPath: "/api/:version/gitlab_ci_ymls/:key"
|
||||
|
||||
group: (group_id, callback) ->
|
||||
url = Api.buildUrl(Api.groupPath)
|
||||
|
@ -110,6 +111,12 @@
|
|||
$.get url, (gitignore) ->
|
||||
callback(gitignore)
|
||||
|
||||
gitlabCiYml: (key, callback) ->
|
||||
url = Api.buildUrl(Api.gitlabCiYmlPath).replace(':key', key)
|
||||
|
||||
$.get url, (file) ->
|
||||
callback(file)
|
||||
|
||||
buildUrl: (url) ->
|
||||
url = gon.relative_url_root + url if gon.relative_url_root?
|
||||
return url.replace(':version', gon.api_version)
|
||||
|
|
|
@ -121,6 +121,11 @@ window.onload = ->
|
|||
setTimeout shiftWindow, 100
|
||||
|
||||
$ ->
|
||||
|
||||
$document = $(document)
|
||||
$window = $(window)
|
||||
$body = $('body')
|
||||
|
||||
gl.utils.preventDisabledButtons()
|
||||
bootstrapBreakpoint = bp.getBreakpointSize()
|
||||
|
||||
|
@ -152,7 +157,7 @@ $ ->
|
|||
), 1
|
||||
|
||||
# Initialize tooltips
|
||||
$('body').tooltip(
|
||||
$body.tooltip(
|
||||
selector: '.has-tooltip, [data-toggle="tooltip"]'
|
||||
placement: (_, el) ->
|
||||
$el = $(el)
|
||||
|
@ -171,7 +176,7 @@ $ ->
|
|||
flash.show()
|
||||
|
||||
# Disable form buttons while a form is submitting
|
||||
$('body').on 'ajax:complete, ajax:beforeSend, submit', 'form', (e) ->
|
||||
$body.on 'ajax:complete, ajax:beforeSend, submit', 'form', (e) ->
|
||||
buttons = $('[type="submit"]', @)
|
||||
|
||||
switch e.type
|
||||
|
@ -184,7 +189,7 @@ $ ->
|
|||
$('.account-box').hover -> $(@).toggleClass('hover')
|
||||
|
||||
# Commit show suppressed diff
|
||||
$(document).on 'click', '.diff-content .js-show-suppressed-diff', ->
|
||||
$document.on 'click', '.diff-content .js-show-suppressed-diff', ->
|
||||
$container = $(@).parent()
|
||||
$container.next('table').show()
|
||||
$container.remove()
|
||||
|
@ -197,13 +202,13 @@ $ ->
|
|||
$('.navbar-toggle i').toggleClass("fa-angle-right fa-angle-left")
|
||||
|
||||
# Show/hide comments on diff
|
||||
$("body").on "click", ".js-toggle-diff-comments", (e) ->
|
||||
$body.on "click", ".js-toggle-diff-comments", (e) ->
|
||||
$(@).toggleClass('active')
|
||||
$(@).closest(".diff-file").find(".notes_holder").toggle()
|
||||
e.preventDefault()
|
||||
|
||||
$(document).off "click", '.js-confirm-danger'
|
||||
$(document).on "click", '.js-confirm-danger', (e) ->
|
||||
$document.off "click", '.js-confirm-danger'
|
||||
$document.on "click", '.js-confirm-danger', (e) ->
|
||||
e.preventDefault()
|
||||
btn = $(e.target)
|
||||
text = btn.data("confirm-danger-message")
|
||||
|
@ -211,7 +216,7 @@ $ ->
|
|||
new ConfirmDangerModal(form, text)
|
||||
|
||||
|
||||
$(document).on 'click', 'button', ->
|
||||
$document.on 'click', 'button', ->
|
||||
$(this).blur()
|
||||
|
||||
$('input[type="search"]').each ->
|
||||
|
@ -219,7 +224,7 @@ $ ->
|
|||
$this.attr 'value', $this.val()
|
||||
return
|
||||
|
||||
$(document)
|
||||
$document
|
||||
.off 'keyup', 'input[type="search"]'
|
||||
.on 'keyup', 'input[type="search"]' , (e) ->
|
||||
$this = $(this)
|
||||
|
@ -227,7 +232,7 @@ $ ->
|
|||
|
||||
$sidebarGutterToggle = $('.js-sidebar-toggle')
|
||||
|
||||
$(document)
|
||||
$document
|
||||
.off 'breakpoint:change'
|
||||
.on 'breakpoint:change', (e, breakpoint) ->
|
||||
if breakpoint is 'sm' or breakpoint is 'xs'
|
||||
|
@ -239,14 +244,14 @@ $ ->
|
|||
oldBootstrapBreakpoint = bootstrapBreakpoint
|
||||
bootstrapBreakpoint = bp.getBreakpointSize()
|
||||
if bootstrapBreakpoint != oldBootstrapBreakpoint
|
||||
$(document).trigger('breakpoint:change', [bootstrapBreakpoint])
|
||||
$document.trigger('breakpoint:change', [bootstrapBreakpoint])
|
||||
|
||||
checkInitialSidebarSize = ->
|
||||
bootstrapBreakpoint = bp.getBreakpointSize()
|
||||
if bootstrapBreakpoint is "xs" or "sm"
|
||||
$(document).trigger('breakpoint:change', [bootstrapBreakpoint])
|
||||
$document.trigger('breakpoint:change', [bootstrapBreakpoint])
|
||||
|
||||
$(window)
|
||||
$window
|
||||
.off "resize.app"
|
||||
.on "resize.app", (e) ->
|
||||
fitSidebarForSize()
|
||||
|
@ -256,29 +261,45 @@ $ ->
|
|||
new Aside()
|
||||
|
||||
# Sidenav pinning
|
||||
if $(window).width() < 1440 and $.cookie('pin_nav') is 'true'
|
||||
$.cookie('pin_nav', 'false')
|
||||
if $window.width() < 1440 and $.cookie('pin_nav') is 'true'
|
||||
$.cookie('pin_nav', 'false', { path: '/' })
|
||||
$('.page-with-sidebar')
|
||||
.toggleClass('page-sidebar-collapsed page-sidebar-expanded')
|
||||
.removeClass('page-sidebar-pinned')
|
||||
$('.navbar-fixed-top').removeClass('header-pinned-nav')
|
||||
|
||||
$(document)
|
||||
$document
|
||||
.off 'click', '.js-nav-pin'
|
||||
.on 'click', '.js-nav-pin', (e) ->
|
||||
e.preventDefault()
|
||||
|
||||
$pinBtn = $(e.currentTarget)
|
||||
$page = $ '.page-with-sidebar'
|
||||
$topNav = $ '.navbar-fixed-top'
|
||||
$tooltip = $ "##{$pinBtn.attr('aria-describedby')}"
|
||||
doPinNav = not $page.is('.page-sidebar-pinned')
|
||||
tooltipText = 'Pin navigation'
|
||||
|
||||
$(this).toggleClass 'is-active'
|
||||
|
||||
if $.cookie('pin_nav') is 'true'
|
||||
$.cookie 'pin_nav', 'false'
|
||||
$('.page-with-sidebar')
|
||||
.removeClass('page-sidebar-pinned')
|
||||
.toggleClass('page-sidebar-collapsed page-sidebar-expanded')
|
||||
$('.navbar-fixed-top')
|
||||
.removeClass('header-pinned-nav')
|
||||
.toggleClass('header-collapsed header-expanded')
|
||||
if doPinNav
|
||||
$page.addClass('page-sidebar-pinned')
|
||||
$topNav.addClass('header-pinned-nav')
|
||||
else
|
||||
$.cookie 'pin_nav', 'true'
|
||||
$('.page-with-sidebar').addClass('page-sidebar-pinned')
|
||||
$('.navbar-fixed-top').addClass('header-pinned-nav')
|
||||
$tooltip.remove() # Remove it immediately when collapsing the sidebar
|
||||
$page.removeClass('page-sidebar-pinned')
|
||||
.toggleClass('page-sidebar-collapsed page-sidebar-expanded')
|
||||
$topNav.removeClass('header-pinned-nav')
|
||||
.toggleClass('header-collapsed header-expanded')
|
||||
|
||||
# Save settings
|
||||
$.cookie 'pin_nav', doPinNav, { path: '/' }
|
||||
|
||||
if $.cookie('pin_nav') is 'true' or doPinNav
|
||||
tooltipText = 'Unpin navigation'
|
||||
|
||||
# Update tooltip text immediately
|
||||
$tooltip.find('.tooltip-inner').text(tooltipText)
|
||||
|
||||
# Persist tooltip title
|
||||
$pinBtn.attr('title', tooltipText).tooltip('fixTitle')
|
||||
|
|
23
app/assets/javascripts/blob/blob_ci_yaml.js.coffee
Normal file
23
app/assets/javascripts/blob/blob_ci_yaml.js.coffee
Normal file
|
@ -0,0 +1,23 @@
|
|||
#= require blob/template_selector
|
||||
|
||||
class @BlobCiYamlSelector extends TemplateSelector
|
||||
requestFile: (query) ->
|
||||
Api.gitlabCiYml query.name, @requestFileSuccess.bind(@)
|
||||
|
||||
class @BlobCiYamlSelectors
|
||||
constructor: (opts) ->
|
||||
{
|
||||
@$dropdowns = $('.js-gitlab-ci-yml-selector')
|
||||
@editor
|
||||
} = opts
|
||||
|
||||
@$dropdowns.each (i, dropdown) =>
|
||||
$dropdown = $(dropdown)
|
||||
|
||||
new BlobCiYamlSelector(
|
||||
pattern: /(.gitlab-ci.yml)/,
|
||||
data: $dropdown.data('data'),
|
||||
wrapper: $dropdown.closest('.js-gitlab-ci-yml-selector-wrap'),
|
||||
dropdown: $dropdown,
|
||||
editor: @editor
|
||||
)
|
|
@ -15,6 +15,7 @@ class @EditBlob
|
|||
|
||||
new BlobLicenseSelectors { @editor }
|
||||
new BlobGitignoreSelectors { @editor }
|
||||
new BlobCiYamlSelectors { @editor }
|
||||
|
||||
initModePanesAndLinks: ->
|
||||
@$editModePanes = $(".js-edit-mode-pane")
|
||||
|
|
|
@ -58,7 +58,7 @@ class GitLabDropdownFilter
|
|||
filter: (search_text) ->
|
||||
data = @options.data()
|
||||
|
||||
if data?
|
||||
if data? and not @options.filterByText
|
||||
results = data
|
||||
|
||||
if search_text isnt ''
|
||||
|
@ -102,10 +102,11 @@ class GitLabDropdownFilter
|
|||
$el = $(@)
|
||||
matches = fuzzaldrinPlus.match($el.text().trim(), search_text)
|
||||
|
||||
if matches.length
|
||||
$el.show()
|
||||
else
|
||||
$el.hide()
|
||||
unless $el.is('.dropdown-header')
|
||||
if matches.length
|
||||
$el.show()
|
||||
else
|
||||
$el.hide()
|
||||
else
|
||||
elements.show()
|
||||
|
||||
|
@ -191,6 +192,7 @@ class GitLabDropdown
|
|||
if @options.filterable
|
||||
@filter = new GitLabDropdownFilter @filterInput,
|
||||
filterInputBlur: @filterInputBlur
|
||||
filterByText: @options.filterByText
|
||||
remote: @options.filterRemote
|
||||
query: @options.data
|
||||
keys: searchFields
|
||||
|
|
|
@ -34,6 +34,8 @@ class @GLForm
|
|||
# form and textarea event listeners
|
||||
@addEventListeners()
|
||||
|
||||
gl.text.init(@form)
|
||||
|
||||
# hide discard button
|
||||
@form.find('.js-note-discard').hide()
|
||||
|
||||
|
@ -42,6 +44,7 @@ class @GLForm
|
|||
clearEventListeners: ->
|
||||
@textarea.off 'focus'
|
||||
@textarea.off 'blur'
|
||||
gl.text.removeListeners(@form)
|
||||
|
||||
addEventListeners: ->
|
||||
@textarea.on 'focus', ->
|
||||
|
|
|
@ -121,7 +121,11 @@ class @ContributorsMasterGraph extends ContributorsGraph
|
|||
|
||||
class @ContributorsAuthorGraph extends ContributorsGraph
|
||||
constructor: (@data) ->
|
||||
@width = $('.content').width()/2 - 100
|
||||
# Don't split graph size in half for mobile devices.
|
||||
if $(window).width() < 768
|
||||
@width = $('.content').width() - 80
|
||||
else
|
||||
@width = ($('.content').width() / 2) - 100
|
||||
@height = 200
|
||||
@x = null
|
||||
@y = null
|
||||
|
|
79
app/assets/javascripts/lib/text_utility.js.coffee
Normal file
79
app/assets/javascripts/lib/text_utility.js.coffee
Normal file
|
@ -0,0 +1,79 @@
|
|||
((w) ->
|
||||
w.gl ?= {}
|
||||
w.gl.text ?= {}
|
||||
|
||||
gl.text.randomString = -> Math.random().toString(36).substring(7)
|
||||
|
||||
gl.text.replaceRange = (s, start, end, substitute) ->
|
||||
s.substring(0, start) + substitute + s.substring(end);
|
||||
|
||||
gl.text.selectedText = (text, textarea) ->
|
||||
text.substring(textarea.selectionStart, textarea.selectionEnd)
|
||||
|
||||
gl.text.insertText = (textArea, text, tag, selected, wrap) ->
|
||||
selectedSplit = selected.split('\n')
|
||||
startChar = if not wrap and textArea.selectionStart > 0 then '\n' else ''
|
||||
|
||||
if selectedSplit.length > 1 and not wrap
|
||||
insertText = selectedSplit.map((val) ->
|
||||
if val.indexOf(tag) is 0
|
||||
"#{val.replace(tag, '')}"
|
||||
else
|
||||
"#{tag}#{val}"
|
||||
).join('\n')
|
||||
else
|
||||
insertText = "#{startChar}#{tag}#{selected}#{if wrap then tag else ' '}"
|
||||
|
||||
if document.queryCommandSupported('insertText')
|
||||
document.execCommand 'insertText', false, insertText
|
||||
else
|
||||
try
|
||||
document.execCommand("ms-beginUndoUnit")
|
||||
|
||||
textArea.value = @replaceRange(
|
||||
text,
|
||||
textArea.selectionStart,
|
||||
textArea.selectionEnd,
|
||||
insertText)
|
||||
try
|
||||
document.execCommand("ms-endUndoUnit")
|
||||
|
||||
@moveCursor(textArea, tag, wrap)
|
||||
|
||||
gl.text.moveCursor = (textArea, tag, wrapped) ->
|
||||
return unless textArea.setSelectionRange
|
||||
|
||||
if textArea.selectionStart is textArea.selectionEnd
|
||||
if wrapped
|
||||
pos = textArea.selectionStart - tag.length
|
||||
else
|
||||
pos = textArea.selectionStart
|
||||
|
||||
textArea.setSelectionRange pos, pos
|
||||
|
||||
gl.text.updateText = (textArea, tag, wrap) ->
|
||||
$textArea = $(textArea)
|
||||
oldVal = $textArea.val()
|
||||
textArea = $textArea.get(0)
|
||||
text = $textArea.val()
|
||||
selected = @selectedText(text, textArea)
|
||||
$textArea.focus()
|
||||
|
||||
@insertText(textArea, text, tag, selected, wrap)
|
||||
|
||||
gl.text.init = (form) ->
|
||||
self = @
|
||||
$('.js-md', form)
|
||||
.off 'click'
|
||||
.on 'click', ->
|
||||
$this = $(@)
|
||||
self.updateText(
|
||||
$this.closest('.md-area').find('textarea'),
|
||||
$this.data('md-tag'),
|
||||
not $this.data('md-prepend')
|
||||
)
|
||||
|
||||
gl.text.removeListeners = (form) ->
|
||||
$('.js-md', form).off()
|
||||
|
||||
) window
|
|
@ -102,12 +102,15 @@ class @Notes
|
|||
|
||||
keydownNoteText: (e) ->
|
||||
$this = $(this)
|
||||
if $this.val() is '' and e.which is 38 #aka the up key
|
||||
if $this.val() is '' and e.which is 38 and not isMetaKey e
|
||||
myLastNote = $("li.note[data-author-id='#{gon.current_user_id}'][data-editable]:last")
|
||||
if myLastNote.length
|
||||
myLastNoteEditBtn = myLastNote.find('.js-note-edit')
|
||||
myLastNoteEditBtn.trigger('click', [true, myLastNote])
|
||||
|
||||
isMetaKey = (e) ->
|
||||
(e.metaKey or e.ctrlKey or e.altKey or e.shiftKey)
|
||||
|
||||
initRefresh: ->
|
||||
clearInterval(Notes.interval)
|
||||
Notes.interval = setInterval =>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
class @NotificationsDropdown
|
||||
$ ->
|
||||
constructor: ->
|
||||
$(document)
|
||||
.off 'click', '.update-notification'
|
||||
.on 'click', '.update-notification', (e) ->
|
||||
|
@ -18,7 +18,8 @@ class @NotificationsDropdown
|
|||
.off 'ajax:success', '.notification-form'
|
||||
.on 'ajax:success', '.notification-form', (e, data) ->
|
||||
if data.saved
|
||||
new Flash('Notification settings saved', 'notice')
|
||||
$(e.currentTarget).closest('.notification-dropdown').replaceWith(data.html)
|
||||
$(e.currentTarget)
|
||||
.closest('.notification-dropdown')
|
||||
.replaceWith(data.html)
|
||||
else
|
||||
new Flash('Failed to save new settings', 'alert')
|
||||
|
|
|
@ -19,6 +19,7 @@ class @Project
|
|||
$('.clone').text(url)
|
||||
|
||||
# Ref switcher
|
||||
@initRefSwitcher()
|
||||
$('.project-refs-select').on 'change', ->
|
||||
$(@).parents('form').submit()
|
||||
|
||||
|
@ -34,7 +35,6 @@ class @Project
|
|||
$(@).parents('.no-password-message').remove()
|
||||
e.preventDefault()
|
||||
|
||||
|
||||
@projectSelectDropdown()
|
||||
|
||||
projectSelectDropdown: ->
|
||||
|
@ -50,3 +50,39 @@ class @Project
|
|||
|
||||
changeProject: (url) ->
|
||||
window.location = url
|
||||
|
||||
initRefSwitcher: ->
|
||||
$('.js-project-refs-dropdown').each ->
|
||||
$dropdown = $(@)
|
||||
selected = $dropdown.data('selected')
|
||||
|
||||
$dropdown.glDropdown(
|
||||
data: (term, callback) ->
|
||||
$.ajax(
|
||||
url: $dropdown.data('refs-url')
|
||||
data:
|
||||
ref: $dropdown.data('ref')
|
||||
).done (refs) ->
|
||||
callback(refs)
|
||||
selectable: true
|
||||
filterable: true
|
||||
filterByText: true
|
||||
fieldName: 'ref'
|
||||
renderRow: (ref) ->
|
||||
if ref.header?
|
||||
"<li class='dropdown-header'>#{ref.header}</li>"
|
||||
else
|
||||
isActiveClass = if ref is selected then 'is-active' else ''
|
||||
|
||||
"<li>
|
||||
<a href='#' data-ref='#{escape(ref)}' class='#{isActiveClass}'>
|
||||
#{ref}
|
||||
</a>
|
||||
</li>"
|
||||
id: (obj, $el) ->
|
||||
$el.data('ref')
|
||||
toggleLabel: (obj, $el) ->
|
||||
$el.text().trim()
|
||||
clicked: (e) ->
|
||||
$dropdown.closest('form').submit()
|
||||
)
|
||||
|
|
|
@ -37,3 +37,4 @@
|
|||
@import "framework/timeline.scss";
|
||||
@import "framework/typography.scss";
|
||||
@import "framework/zen.scss";
|
||||
@import "framework/blank";
|
||||
|
|
23
app/assets/stylesheets/framework/blank.scss
Normal file
23
app/assets/stylesheets/framework/blank.scss
Normal file
|
@ -0,0 +1,23 @@
|
|||
.blank-state {
|
||||
padding-top: 20px;
|
||||
padding-bottom: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.blank-state-no-icon {
|
||||
padding-top: 40px;
|
||||
padding-bottom: 40px;
|
||||
}
|
||||
|
||||
.blank-state-title {
|
||||
margin-top: 0;
|
||||
margin-bottom: 5px;
|
||||
font-size: 19px;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.blank-state-text {
|
||||
margin-top: 0;
|
||||
margin-bottom: $gl-padding;
|
||||
font-size: 15px;
|
||||
}
|
|
@ -97,6 +97,22 @@
|
|||
}
|
||||
}
|
||||
|
||||
.sub-header-block {
|
||||
background-color: $white-light;
|
||||
border-bottom: 1px solid $white-dark;
|
||||
padding: 11px 0;
|
||||
margin-bottom: 11px;
|
||||
|
||||
.oneline {
|
||||
line-height: 35px;
|
||||
}
|
||||
|
||||
&.no-bottom-space {
|
||||
border-bottom: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.cover-block {
|
||||
text-align: center;
|
||||
background: $background-color;
|
||||
|
|
|
@ -461,10 +461,12 @@
|
|||
}
|
||||
}
|
||||
|
||||
.ui-state-active,
|
||||
.ui-state-hover {
|
||||
color: $md-link-color;
|
||||
background-color: $calendar-hover-bg;
|
||||
.ui-datepicker-calendar {
|
||||
.ui-state-hover,
|
||||
.ui-state-active {
|
||||
color: #fff;
|
||||
border: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.ui-datepicker-prev,
|
||||
|
|
|
@ -65,6 +65,11 @@
|
|||
a {
|
||||
padding-top: 0;
|
||||
line-height: 1;
|
||||
border-bottom: 1px solid $border-color;
|
||||
|
||||
&.btn.btn-xs {
|
||||
padding: 2px 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -97,5 +102,30 @@
|
|||
white-space: pre-wrap;
|
||||
word-break: keep-all;
|
||||
}
|
||||
|
||||
@include bulleted-list;
|
||||
}
|
||||
}
|
||||
|
||||
.toolbar-group {
|
||||
float: left;
|
||||
margin-right: -5px;
|
||||
margin-left: $gl-padding;
|
||||
|
||||
&:first-child {
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.toolbar-btn {
|
||||
float: left;
|
||||
padding: 0 5px;
|
||||
color: #959494;
|
||||
background: transparent;
|
||||
border: 0;
|
||||
outline: 0;
|
||||
|
||||
&:hover {
|
||||
color: $gl-link-color;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -110,3 +110,17 @@
|
|||
font-size: 16px;
|
||||
line-height: 24px;
|
||||
}
|
||||
|
||||
@mixin bulleted-list {
|
||||
> ul {
|
||||
list-style-type: disc;
|
||||
|
||||
ul {
|
||||
list-style-type: circle;
|
||||
|
||||
ul {
|
||||
list-style-type: square;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -18,6 +18,13 @@
|
|||
opacity: 0;
|
||||
transition-duration: .3s;
|
||||
}
|
||||
|
||||
.fa {
|
||||
position: relative;
|
||||
top: 3px;
|
||||
font-size: 13px;
|
||||
color: $btn-placeholder-gray;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin scrolling-links() {
|
||||
|
@ -104,10 +111,6 @@
|
|||
width: 50%;
|
||||
line-height: 28px;
|
||||
|
||||
&.wiki-page {
|
||||
padding: 16px 10px 11px;
|
||||
}
|
||||
|
||||
/* Small devices (phones, tablets, 768px and lower) */
|
||||
@media (max-width: $screen-sm-min) {
|
||||
width: 100%;
|
||||
|
@ -136,7 +139,7 @@
|
|||
}
|
||||
|
||||
/* Small devices (phones, tablets, 768px and lower) */
|
||||
@media (max-width: $screen-sm-max) {
|
||||
@media (max-width: $screen-xs-max) {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
@ -220,6 +223,7 @@
|
|||
form {
|
||||
display: block;
|
||||
height: auto;
|
||||
margin-bottom: 14px;
|
||||
|
||||
input {
|
||||
width: 100%;
|
||||
|
@ -319,11 +323,19 @@
|
|||
.fade-right {
|
||||
@include fade(left, rgba(250, 250, 250, 0.4), $background-color);
|
||||
right: 0;
|
||||
|
||||
.fa {
|
||||
right: -7px;
|
||||
}
|
||||
}
|
||||
|
||||
.fade-left {
|
||||
@include fade(right, rgba(250, 250, 250, 0.4), $background-color);
|
||||
left: 0;
|
||||
|
||||
.fa {
|
||||
left: -7px;
|
||||
}
|
||||
}
|
||||
|
||||
li {
|
||||
|
|
|
@ -9,6 +9,10 @@
|
|||
margin-top: -2px;
|
||||
float: right;
|
||||
}
|
||||
|
||||
.dropdown-menu-toggle {
|
||||
line-height: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.panel-body {
|
||||
|
|
|
@ -165,11 +165,6 @@
|
|||
background-size: 16px 16px !important;
|
||||
}
|
||||
|
||||
/** Branch/tag selector **/
|
||||
.project-refs-form .select2-container {
|
||||
width: 160px !important;
|
||||
}
|
||||
|
||||
.select2-results .select2-no-results,
|
||||
.select2-results .select2-searching,
|
||||
.select2-results .select2-ajax-error,
|
||||
|
|
|
@ -91,7 +91,6 @@
|
|||
text-decoration: none;
|
||||
font-weight: normal;
|
||||
outline: none;
|
||||
white-space: nowrap;
|
||||
|
||||
&:hover,
|
||||
&:active,
|
||||
|
|
|
@ -80,9 +80,14 @@
|
|||
|
||||
.commit {
|
||||
padding: 10px 0;
|
||||
position: relative;
|
||||
|
||||
@media (min-width: $screen-sm-min) {
|
||||
padding-left: 46px;
|
||||
padding-left: 20px;
|
||||
|
||||
.commit-info-block {
|
||||
padding-left: 44px;
|
||||
}
|
||||
}
|
||||
|
||||
&:not(:last-child) {
|
||||
|
@ -95,8 +100,11 @@
|
|||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
|
||||
.avatar {
|
||||
margin-left: -46px;
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
left: 16px;
|
||||
}
|
||||
|
||||
.item-title {
|
||||
|
|
|
@ -4,6 +4,11 @@
|
|||
margin-bottom: $gl-padding;
|
||||
border-radius: 3px;
|
||||
|
||||
.commit-short-id {
|
||||
font-family: $regular_font;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.diff-header {
|
||||
position: relative;
|
||||
background: $background-color;
|
||||
|
|
|
@ -60,13 +60,14 @@
|
|||
|
||||
.encoding-selector,
|
||||
.license-selector,
|
||||
.gitignore-selector {
|
||||
.gitignore-selector,
|
||||
.gitlab-ci-yml-selector {
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
font-family: $regular_font;
|
||||
}
|
||||
|
||||
.gitignore-selector, .license-selector {
|
||||
.gitignore-selector, .license-selector, .gitlab-ci-yml-selector {
|
||||
.dropdown {
|
||||
line-height: 21px;
|
||||
}
|
||||
|
@ -76,4 +77,10 @@
|
|||
width: 220px;
|
||||
}
|
||||
}
|
||||
|
||||
.gitlab-ci-yml-selector {
|
||||
.dropdown-menu-toggle {
|
||||
width: 250px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -54,6 +54,10 @@
|
|||
}
|
||||
}
|
||||
|
||||
code {
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
pre {
|
||||
border: none;
|
||||
background: #f9f9f9;
|
||||
|
|
|
@ -57,4 +57,11 @@
|
|||
|
||||
.documentation {
|
||||
padding: 7px;
|
||||
|
||||
// Border around images in the help pages.
|
||||
img:not(.emoji) {
|
||||
border: 1px solid $table-border-gray;
|
||||
padding: 5px;
|
||||
margin: 5px;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,6 +4,13 @@
|
|||
margin-right: 1px;
|
||||
}
|
||||
}
|
||||
|
||||
// Border around images in issue and MR descriptions.
|
||||
.description img:not(.emoji) {
|
||||
border: 1px solid $table-border-gray;
|
||||
padding: 5px;
|
||||
margin: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.issuable-filter-count {
|
||||
|
|
|
@ -50,11 +50,10 @@
|
|||
|
||||
.label-row {
|
||||
.label-name {
|
||||
display: block;
|
||||
display: inline-block;
|
||||
margin-bottom: 10px;
|
||||
|
||||
@media (min-width: $screen-sm-min) {
|
||||
display: inline-block;
|
||||
width: 200px;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
@ -63,6 +62,7 @@
|
|||
.label-description {
|
||||
display: block;
|
||||
margin-bottom: 10px;
|
||||
margin-left: 50px;
|
||||
|
||||
@media (min-width: $screen-sm-min) {
|
||||
display: inline-block;
|
||||
|
|
|
@ -119,7 +119,12 @@
|
|||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
@media (max-width: $screen-sm-max) {
|
||||
.btn-grouped {
|
||||
margin-left: 0;
|
||||
margin-right: 7px;
|
||||
}
|
||||
|
||||
@media (max-width: $screen-xs-max) {
|
||||
h4 {
|
||||
font-size: 15px;
|
||||
}
|
||||
|
@ -131,10 +136,14 @@
|
|||
.btn,
|
||||
.btn-group,
|
||||
.accept-action {
|
||||
width: 100%;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.accept-action {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.accept-control {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
|
@ -284,7 +293,7 @@
|
|||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
@media (min-width: $screen-sm-min) {
|
||||
@media (min-width: $screen-xs-min) {
|
||||
float: left;
|
||||
width: 50%;
|
||||
margin-bottom: 0;
|
||||
|
|
|
@ -179,6 +179,10 @@
|
|||
border-top: 1px solid $border-color;
|
||||
}
|
||||
|
||||
.md-helper {
|
||||
padding-top: 10px;
|
||||
}
|
||||
|
||||
.toolbar-button {
|
||||
padding: 0;
|
||||
background: none;
|
||||
|
@ -219,3 +223,16 @@
|
|||
float: left;
|
||||
}
|
||||
}
|
||||
|
||||
.note-form-actions {
|
||||
@media (max-width: $screen-xs-max) {
|
||||
.btn {
|
||||
float: none;
|
||||
width: 100%;
|
||||
|
||||
&:not(:last-child) {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -84,24 +84,14 @@ ul.notes {
|
|||
word-wrap: break-word;
|
||||
@include md-typography;
|
||||
|
||||
// Reset ul style types since we're nested inside a ul already
|
||||
@include bulleted-list;
|
||||
|
||||
// On diffs code should wrap nicely and not overflow
|
||||
code {
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
// Reset ul style types since we're nested inside a ul already
|
||||
& > ul {
|
||||
list-style-type: disc;
|
||||
|
||||
ul {
|
||||
list-style-type: circle;
|
||||
|
||||
ul {
|
||||
list-style-type: square;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ul.task-list {
|
||||
ul:not(.task-list) {
|
||||
padding-left: 1.3em;
|
||||
|
@ -117,6 +107,13 @@ ul.notes {
|
|||
code {
|
||||
word-break: keep-all;
|
||||
}
|
||||
|
||||
// Border around images in issue and MR comments.
|
||||
img:not(.emoji) {
|
||||
border: 1px solid $table-border-gray;
|
||||
padding: 5px;
|
||||
margin: 5px 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -101,7 +101,8 @@
|
|||
|
||||
.notifications-btn {
|
||||
|
||||
.fa-bell {
|
||||
.fa-bell,
|
||||
.fa-spinner {
|
||||
margin-right: 6px;
|
||||
}
|
||||
|
||||
|
@ -373,7 +374,7 @@ a.deploy-project-label {
|
|||
.project-stats {
|
||||
margin-top: $gl-padding;
|
||||
margin-bottom: 0;
|
||||
padding: 16px 0;
|
||||
padding: 0;
|
||||
background-color: $white-light;
|
||||
font-size: 0;
|
||||
|
||||
|
@ -382,13 +383,14 @@ a.deploy-project-label {
|
|||
}
|
||||
|
||||
.nav li {
|
||||
display: inline;
|
||||
display: inline-block;
|
||||
margin: 16px 0;
|
||||
margin-right: 16px;
|
||||
}
|
||||
|
||||
.nav > li > a {
|
||||
background-color: transparent;
|
||||
margin-right: 12px;
|
||||
padding: 0 10px;
|
||||
padding: 5px 10px;
|
||||
font-size: 15px;
|
||||
color: $notes-light-color;
|
||||
}
|
||||
|
@ -402,12 +404,17 @@ a.deploy-project-label {
|
|||
font-size: 17px;
|
||||
}
|
||||
|
||||
li.missing a {
|
||||
color: #5a6069;
|
||||
border: 1px dashed #dce0e5;
|
||||
li.missing {
|
||||
border: 1px dashed $border-gray-light;
|
||||
border-radius: $border-radius-default;
|
||||
|
||||
a {
|
||||
color: $notes-light-color;
|
||||
display: block;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: #f0f2f5;
|
||||
background-color: $gray-normal;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -616,3 +623,9 @@ pre.light-well {
|
|||
color: $gl-text-green;
|
||||
}
|
||||
}
|
||||
|
||||
.project-refs-form {
|
||||
.dropdown-menu {
|
||||
width: 300px;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,24 +14,38 @@
|
|||
font-size: 10px;
|
||||
}
|
||||
|
||||
#contributors-master {
|
||||
@include make-md-column(12);
|
||||
|
||||
svg {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
#contributors {
|
||||
.contributors-list {
|
||||
margin: 0 0 10px;
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
|
||||
svg {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.person {
|
||||
&:nth-child(even) {
|
||||
float: right;
|
||||
}
|
||||
float: left;
|
||||
@include make-md-column(6);
|
||||
margin-top: 10px;
|
||||
|
||||
@media (max-width: $screen-sm-min) {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.person .spark {
|
||||
display: block;
|
||||
background: #f3f3f3;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.person .area-contributor {
|
||||
|
|
|
@ -62,6 +62,10 @@
|
|||
}
|
||||
}
|
||||
|
||||
code {
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
pre {
|
||||
border: none;
|
||||
background: #f9f9f9;
|
||||
|
|
|
@ -5,6 +5,7 @@ class Admin::AppearancesController < Admin::ApplicationController
|
|||
end
|
||||
|
||||
def preview
|
||||
render 'preview', layout: 'devise'
|
||||
end
|
||||
|
||||
def create
|
||||
|
|
|
@ -1,15 +1,14 @@
|
|||
class Admin::RunnerProjectsController < Admin::ApplicationController
|
||||
before_action :project, only: [:create]
|
||||
|
||||
def index
|
||||
@runner_projects = project.runner_projects.all
|
||||
@runner_project = project.runner_projects.new
|
||||
end
|
||||
|
||||
def create
|
||||
@runner = Ci::Runner.find(params[:runner_project][:runner_id])
|
||||
|
||||
if @runner.assign_to(@project, current_user)
|
||||
return head(403) if @runner.is_shared? || @runner.locked?
|
||||
|
||||
runner_project = @runner.assign_to(@project, current_user)
|
||||
|
||||
if runner_project.persisted?
|
||||
redirect_to admin_runner_path(@runner)
|
||||
else
|
||||
redirect_to admin_runner_path(@runner), alert: 'Failed adding runner to project'
|
||||
|
|
|
@ -36,6 +36,10 @@ class ApplicationController < ActionController::Base
|
|||
render_404
|
||||
end
|
||||
|
||||
rescue_from Gitlab::Access::AccessDeniedError do |exception|
|
||||
render_403
|
||||
end
|
||||
|
||||
def redirect_back_or_default(default: root_path, options: {})
|
||||
redirect_to request.referer.present? ? :back : default, options
|
||||
end
|
||||
|
|
|
@ -21,29 +21,18 @@ module MembershipActions
|
|||
|
||||
def leave
|
||||
@member = membershipable.members.find_by(user_id: current_user)
|
||||
return render_403 unless @member
|
||||
Members::DestroyService.new(@member, current_user).execute
|
||||
|
||||
source_type = @member.real_source_type.humanize(capitalize: false)
|
||||
|
||||
if can?(current_user, action_member_permission(:destroy, @member), @member)
|
||||
notice =
|
||||
if @member.request?
|
||||
"Your access request to the #{source_type} has been withdrawn."
|
||||
else
|
||||
"You left the \"#{@member.source.human_name}\" #{source_type}."
|
||||
end
|
||||
@member.destroy
|
||||
|
||||
redirect_to [:dashboard, @member.real_source_type.tableize], notice: notice
|
||||
else
|
||||
if cannot_leave?
|
||||
alert = "You can not leave the \"#{@member.source.human_name}\" #{source_type}."
|
||||
alert << " Transfer or delete the #{source_type}."
|
||||
redirect_to polymorphic_url(membershipable), alert: alert
|
||||
notice =
|
||||
if @member.request?
|
||||
"Your access request to the #{source_type} has been withdrawn."
|
||||
else
|
||||
render_403
|
||||
"You left the \"#{@member.source.human_name}\" #{source_type}."
|
||||
end
|
||||
end
|
||||
redirect_path = @member.request? ? @member.source : [:dashboard, @member.real_source_type.tableize]
|
||||
|
||||
redirect_to redirect_path, notice: notice
|
||||
end
|
||||
|
||||
protected
|
||||
|
@ -51,8 +40,4 @@ module MembershipActions
|
|||
def membershipable
|
||||
raise NotImplementedError
|
||||
end
|
||||
|
||||
def cannot_leave?
|
||||
raise NotImplementedError
|
||||
end
|
||||
end
|
||||
|
|
|
@ -36,9 +36,7 @@ class Groups::GroupMembersController < Groups::ApplicationController
|
|||
def destroy
|
||||
@group_member = @group.group_members.find(params[:id])
|
||||
|
||||
return render_403 unless can?(current_user, :destroy_group_member, @group_member)
|
||||
|
||||
@group_member.destroy
|
||||
Members::DestroyService.new(@group_member, current_user).execute
|
||||
|
||||
respond_to do |format|
|
||||
format.html { redirect_to group_group_members_path(@group), notice: 'User was successfully removed from group.' }
|
||||
|
@ -68,8 +66,4 @@ class Groups::GroupMembersController < Groups::ApplicationController
|
|||
|
||||
# MembershipActions concern
|
||||
alias_method :membershipable, :group
|
||||
|
||||
def cannot_leave?
|
||||
@group.last_owner?(current_user)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -74,7 +74,7 @@ class Projects::ApplicationController < ApplicationController
|
|||
end
|
||||
|
||||
def require_branch_head
|
||||
unless @repository.branch_names.include?(@ref)
|
||||
unless @repository.branch_exists?(@ref)
|
||||
redirect_to(
|
||||
namespace_project_tree_path(@project.namespace, @project, @ref),
|
||||
notice: "This action is not allowed unless you are on a branch"
|
||||
|
|
|
@ -54,6 +54,6 @@ class Projects::PipelinesController < Projects::ApplicationController
|
|||
end
|
||||
|
||||
def commit
|
||||
@commit ||= @pipeline.commit_data
|
||||
@commit ||= @pipeline.commit
|
||||
end
|
||||
end
|
||||
|
|
|
@ -50,9 +50,7 @@ class Projects::ProjectMembersController < Projects::ApplicationController
|
|||
def destroy
|
||||
@project_member = @project.project_members.find(params[:id])
|
||||
|
||||
return render_403 unless can?(current_user, :destroy_project_member, @project_member)
|
||||
|
||||
@project_member.destroy
|
||||
Members::DestroyService.new(@project_member, current_user).execute
|
||||
|
||||
respond_to do |format|
|
||||
format.html do
|
||||
|
@ -98,8 +96,4 @@ class Projects::ProjectMembersController < Projects::ApplicationController
|
|||
|
||||
# MembershipActions concern
|
||||
alias_method :membershipable, :project
|
||||
|
||||
def cannot_leave?
|
||||
current_user == @project.owner
|
||||
end
|
||||
end
|
||||
|
|
|
@ -6,11 +6,13 @@ class Projects::RunnerProjectsController < Projects::ApplicationController
|
|||
def create
|
||||
@runner = Ci::Runner.find(params[:runner_project][:runner_id])
|
||||
|
||||
return head(403) if @runner.is_shared? || @runner.locked?
|
||||
return head(403) unless current_user.ci_authorized_runners.include?(@runner)
|
||||
|
||||
path = runners_path(project)
|
||||
runner_project = @runner.assign_to(project, current_user)
|
||||
|
||||
if @runner.assign_to(project, current_user)
|
||||
if runner_project.persisted?
|
||||
redirect_to path
|
||||
else
|
||||
redirect_to path, alert: 'Failed adding runner to project'
|
||||
|
|
|
@ -5,10 +5,9 @@ class Projects::RunnersController < Projects::ApplicationController
|
|||
layout 'project_settings'
|
||||
|
||||
def index
|
||||
@runners = project.runners.ordered
|
||||
@specific_runners = current_user.ci_authorized_runners.
|
||||
where.not(id: project.runners).
|
||||
ordered.page(params[:page]).per(20)
|
||||
@project_runners = project.runners.ordered
|
||||
@assignable_runners = current_user.ci_authorized_runners.
|
||||
assignable_for(project).ordered.page(params[:page]).per(20)
|
||||
@shared_runners = Ci::Runner.shared.active
|
||||
@shared_runners_count = @shared_runners.count(:all)
|
||||
end
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
class ProjectsController < Projects::ApplicationController
|
||||
include ExtractsPath
|
||||
|
||||
before_action :authenticate_user!, except: [:show, :activity]
|
||||
before_action :authenticate_user!, except: [:show, :activity, :refs]
|
||||
before_action :project, except: [:new, :create]
|
||||
before_action :repository, except: [:new, :create]
|
||||
before_action :assign_ref_vars, :tree, only: [:show], if: :repo_exists?
|
||||
|
@ -251,6 +251,24 @@ class ProjectsController < Projects::ApplicationController
|
|||
}
|
||||
end
|
||||
|
||||
def refs
|
||||
options = {
|
||||
'Branches' => @repository.branch_names,
|
||||
}
|
||||
|
||||
unless @repository.tag_count.zero?
|
||||
options['Tags'] = VersionSorter.rsort(@repository.tag_names)
|
||||
end
|
||||
|
||||
# If reference is commit id - we should add it to branch/tag selectbox
|
||||
ref = Addressable::URI.unescape(params[:ref])
|
||||
if ref && options.flatten(2).exclude?(ref) && ref =~ /\A[0-9a-zA-Z]{6,52}\z/
|
||||
options['Commits'] = [ref]
|
||||
end
|
||||
|
||||
render json: options.to_json
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def determine_layout
|
||||
|
@ -285,8 +303,14 @@ class ProjectsController < Projects::ApplicationController
|
|||
project.repository_exists? && !project.empty_repo?
|
||||
end
|
||||
|
||||
# Override get_id from ExtractsPath, which returns the branch and file path
|
||||
# Override extract_ref from ExtractsPath, which returns the branch and file path
|
||||
# for the blob/tree, which in this case is just the root of the default branch.
|
||||
# This way we avoid to access the repository.ref_names.
|
||||
def extract_ref(_id)
|
||||
[get_id, '']
|
||||
end
|
||||
|
||||
# Override get_id from ExtractsPath in this case is just the root of the default branch.
|
||||
def get_id
|
||||
project.repository.root_ref
|
||||
end
|
||||
|
|
|
@ -101,22 +101,6 @@ module ApplicationHelper
|
|||
'Never'
|
||||
end
|
||||
|
||||
def grouped_options_refs
|
||||
repository = @project.repository
|
||||
|
||||
options = [
|
||||
['Branches', repository.branch_names],
|
||||
['Tags', VersionSorter.rsort(repository.tag_names)]
|
||||
]
|
||||
|
||||
# If reference is commit id - we should add it to branch/tag selectbox
|
||||
if @ref && !options.flatten.include?(@ref) && @ref =~ /\A[0-9a-zA-Z]{6,52}\z/
|
||||
options << ['Commit', [@ref]]
|
||||
end
|
||||
|
||||
grouped_options_for_select(options, @ref || @project.default_branch)
|
||||
end
|
||||
|
||||
# Define whenever show last push event
|
||||
# with suggestion to create MR
|
||||
def show_last_push_widget?(event)
|
||||
|
@ -132,7 +116,7 @@ module ApplicationHelper
|
|||
return false if project.merge_requests.where(source_branch: event.branch_name).opened.any?
|
||||
|
||||
# Skip if user removed branch right after that
|
||||
return false unless project.repository.branch_names.include?(event.branch_name)
|
||||
return false unless project.repository.branch_exists?(event.branch_name)
|
||||
|
||||
true
|
||||
end
|
||||
|
|
|
@ -29,7 +29,7 @@ module BlobHelper
|
|||
if !on_top_of_branch?(project, ref)
|
||||
button_tag "Edit", class: "btn disabled has-tooltip btn-file-option", title: "You can only edit files when you are on a branch", data: { container: 'body' }
|
||||
elsif can_edit_blob?(blob, project, ref)
|
||||
link_to "Edit", edit_path, class: 'btn btn-file-option'
|
||||
link_to "Edit", edit_path, class: 'btn btn-sm'
|
||||
elsif can?(current_user, :fork_project, project)
|
||||
continue_params = {
|
||||
to: edit_path,
|
||||
|
@ -186,12 +186,16 @@ module BlobHelper
|
|||
end
|
||||
|
||||
def gitignore_names
|
||||
return @gitignore_names if defined?(@gitignore_names)
|
||||
@gitignore_names ||=
|
||||
Gitlab::Template::Gitignore.categories.keys.map do |k|
|
||||
[k, Gitlab::Template::Gitignore.by_category(k).map { |t| { name: t.name } }]
|
||||
end.to_h
|
||||
end
|
||||
|
||||
@gitignore_names = {
|
||||
Global: Gitlab::Gitignore.global.map { |gitignore| { name: gitignore.name } },
|
||||
# Note that the key here doesn't cover it really
|
||||
Languages: Gitlab::Gitignore.languages_frameworks.map{ |gitignore| { name: gitignore.name } }
|
||||
}
|
||||
def gitlab_ci_ymls
|
||||
@gitlab_ci_ymls ||=
|
||||
Gitlab::Template::GitlabCiYml.categories.keys.map do |k|
|
||||
[k, Gitlab::Template::GitlabCiYml.by_category(k).map { |t| { name: t.name } }]
|
||||
end.to_h
|
||||
end
|
||||
end
|
||||
|
|
|
@ -10,7 +10,7 @@ module BranchesHelper
|
|||
end
|
||||
|
||||
def can_push_branch?(project, branch_name)
|
||||
return false unless project.repository.branch_names.include?(branch_name)
|
||||
return false unless project.repository.branch_exists?(branch_name)
|
||||
|
||||
::Gitlab::GitAccess.new(current_user, project).can_push_to_branch?(branch_name)
|
||||
end
|
||||
|
|
|
@ -50,8 +50,6 @@ module GitlabMarkdownHelper
|
|||
|
||||
context[:project] ||= @project
|
||||
|
||||
text = Banzai.pre_process(text, context)
|
||||
|
||||
html = Banzai.render(text, context)
|
||||
|
||||
context.merge!(
|
||||
|
@ -185,4 +183,17 @@ module GitlabMarkdownHelper
|
|||
''
|
||||
end
|
||||
end
|
||||
|
||||
def markdown_toolbar_button(options = {})
|
||||
data = options[:data].merge({ container: "body" })
|
||||
content_tag :button,
|
||||
type: "button",
|
||||
class: "toolbar-btn js-md has-tooltip hidden-xs",
|
||||
tabindex: -1,
|
||||
data: data,
|
||||
title: options[:title],
|
||||
aria: { label: options[:title] } do
|
||||
icon(options[:icon])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -12,6 +12,11 @@ module Emails
|
|||
@member_id = member_id
|
||||
|
||||
admins = member_source.members.owners_and_masters.includes(:user).pluck(:notification_email)
|
||||
# A project in a group can have no explicit owners/masters, in that case
|
||||
# we fallbacks to the group's owners/masters.
|
||||
if admins.empty? && member_source.respond_to?(:group) && member_source.group
|
||||
admins = member_source.group.members.owners_and_masters.includes(:user).pluck(:notification_email)
|
||||
end
|
||||
|
||||
mail(to: admins,
|
||||
subject: subject("Request to join the #{member_source.human_name} #{member_source.model_name.singular}"))
|
||||
|
|
|
@ -300,18 +300,12 @@ module Ci
|
|||
project.valid_runners_token? token
|
||||
end
|
||||
|
||||
def can_be_served?(runner)
|
||||
return false unless has_tags? || runner.run_untagged?
|
||||
|
||||
(tag_list - runner.tag_list).empty?
|
||||
end
|
||||
|
||||
def has_tags?
|
||||
tag_list.any?
|
||||
end
|
||||
|
||||
def any_runners_online?
|
||||
project.any_runners? { |runner| runner.active? && runner.online? && can_be_served?(runner) }
|
||||
project.any_runners? { |runner| runner.active? && runner.online? && runner.can_pick?(self) }
|
||||
end
|
||||
|
||||
def stuck?
|
||||
|
|
|
@ -37,22 +37,22 @@ module Ci
|
|||
end
|
||||
|
||||
def git_author_name
|
||||
commit_data.author_name if commit_data
|
||||
commit.try(:author_name)
|
||||
end
|
||||
|
||||
def git_author_email
|
||||
commit_data.author_email if commit_data
|
||||
commit.try(:author_email)
|
||||
end
|
||||
|
||||
def git_commit_message
|
||||
commit_data.message if commit_data
|
||||
commit.try(:message)
|
||||
end
|
||||
|
||||
def short_sha
|
||||
Ci::Pipeline.truncate_sha(sha)
|
||||
end
|
||||
|
||||
def commit_data
|
||||
def commit
|
||||
@commit ||= project.commit(sha)
|
||||
rescue
|
||||
nil
|
||||
|
|
|
@ -4,7 +4,7 @@ module Ci
|
|||
|
||||
LAST_CONTACT_TIME = 5.minutes.ago
|
||||
AVAILABLE_SCOPES = %w[specific shared active paused online]
|
||||
FORM_EDITABLE = %i[description tag_list active run_untagged]
|
||||
FORM_EDITABLE = %i[description tag_list active run_untagged locked]
|
||||
|
||||
has_many :builds, class_name: 'Ci::Build'
|
||||
has_many :runner_projects, dependent: :destroy, class_name: 'Ci::RunnerProject'
|
||||
|
@ -26,6 +26,13 @@ module Ci
|
|||
.where("ci_runner_projects.gl_project_id = :project_id OR ci_runners.is_shared = true", project_id: project_id)
|
||||
end
|
||||
|
||||
scope :assignable_for, ->(project) do
|
||||
# FIXME: That `to_sql` is needed to workaround a weird Rails bug.
|
||||
# Without that, placeholders would miss one and couldn't match.
|
||||
where(locked: false).
|
||||
where.not("id IN (#{project.runners.select(:id).to_sql})").specific
|
||||
end
|
||||
|
||||
validate :tag_constraints
|
||||
|
||||
acts_as_taggable
|
||||
|
@ -56,7 +63,7 @@ module Ci
|
|||
def assign_to(project, current_user = nil)
|
||||
self.is_shared = false if shared?
|
||||
self.save
|
||||
project.runner_projects.create!(runner_id: self.id)
|
||||
project.runner_projects.create(runner_id: self.id)
|
||||
end
|
||||
|
||||
def display_name
|
||||
|
@ -91,6 +98,10 @@ module Ci
|
|||
!shared?
|
||||
end
|
||||
|
||||
def can_pick?(build)
|
||||
assignable_for?(build.project) && accepting_tags?(build)
|
||||
end
|
||||
|
||||
def only_for?(project)
|
||||
projects == [project]
|
||||
end
|
||||
|
@ -111,5 +122,13 @@ module Ci
|
|||
'can not be empty when runner is not allowed to pick untagged jobs')
|
||||
end
|
||||
end
|
||||
|
||||
def assignable_for?(project)
|
||||
!locked? || projects.exists?(id: project.id)
|
||||
end
|
||||
|
||||
def accepting_tags?(build)
|
||||
(run_untagged? || build.has_tags?) && (build.tag_list - tag_list).empty?
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -271,6 +271,32 @@ class Commit
|
|||
merged_merge_request ? 'merge request' : 'commit'
|
||||
end
|
||||
|
||||
# Get the URI type of the given path
|
||||
#
|
||||
# Used to build URLs to files in the repository in GFM.
|
||||
#
|
||||
# path - String path to check
|
||||
#
|
||||
# Examples:
|
||||
#
|
||||
# uri_type('doc/README.md') # => :blob
|
||||
# uri_type('doc/logo.png') # => :raw
|
||||
# uri_type('doc/api') # => :tree
|
||||
# uri_type('not/found') # => :nil
|
||||
#
|
||||
# Returns a symbol
|
||||
def uri_type(path)
|
||||
entry = @raw.tree.path(path)
|
||||
if entry[:type] == :blob
|
||||
blob = Gitlab::Git::Blob.new(name: entry[:name])
|
||||
blob.image? ? :raw : :blob
|
||||
else
|
||||
entry[:type]
|
||||
end
|
||||
rescue Rugged::TreeError
|
||||
nil
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def repo_changes
|
||||
|
|
|
@ -8,6 +8,8 @@ class CommitStatus < ActiveRecord::Base
|
|||
belongs_to :pipeline, class_name: 'Ci::Pipeline', foreign_key: :commit_id, touch: true
|
||||
belongs_to :user
|
||||
|
||||
delegate :commit, to: :pipeline
|
||||
|
||||
validates :pipeline, presence: true, unless: :importing?
|
||||
|
||||
validates_presence_of :name
|
||||
|
|
|
@ -53,6 +53,16 @@ module Participable
|
|||
#
|
||||
# Returns an Array of User instances.
|
||||
def participants(current_user = nil)
|
||||
@participants ||= Hash.new do |hash, user|
|
||||
hash[user] = raw_participants(user)
|
||||
end
|
||||
|
||||
@participants[current_user]
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def raw_participants(current_user = nil)
|
||||
current_user ||= author
|
||||
ext = Gitlab::ReferenceExtractor.new(project, current_user)
|
||||
participants = Set.new
|
||||
|
|
|
@ -9,7 +9,7 @@ class Key < ActiveRecord::Base
|
|||
before_validation :strip_white_space, :generate_fingerprint
|
||||
|
||||
validates :title, presence: true, length: { within: 0..255 }
|
||||
validates :key, presence: true, length: { within: 0..5000 }, format: { with: /\A(ssh|ecdsa)-.*\Z/ }, uniqueness: true
|
||||
validates :key, presence: true, length: { within: 0..5000 }, format: { with: /\A(ssh|ecdsa)-.*\Z/ }
|
||||
validates :key, format: { without: /\n|\r/, message: 'should be a single line' }
|
||||
validates :fingerprint, uniqueness: true, presence: { message: 'cannot be generated' }
|
||||
|
||||
|
|
|
@ -48,7 +48,6 @@ class Member < ActiveRecord::Base
|
|||
after_create :post_create_hook, unless: [:pending?, :importing?]
|
||||
after_update :post_update_hook, unless: [:pending?, :importing?]
|
||||
after_destroy :post_destroy_hook, unless: :pending?
|
||||
after_destroy :post_decline_request, if: :request?
|
||||
|
||||
delegate :name, :username, :email, to: :user, prefix: true
|
||||
|
||||
|
@ -188,7 +187,7 @@ class Member < ActiveRecord::Base
|
|||
end
|
||||
|
||||
def send_request
|
||||
# override in subclass
|
||||
notification_service.new_access_request(self)
|
||||
end
|
||||
|
||||
def post_create_hook
|
||||
|
@ -215,10 +214,6 @@ class Member < ActiveRecord::Base
|
|||
post_create_hook
|
||||
end
|
||||
|
||||
def post_decline_request
|
||||
# override in subclass
|
||||
end
|
||||
|
||||
def system_hook_service
|
||||
SystemHooksService.new
|
||||
end
|
||||
|
|
|
@ -33,12 +33,6 @@ class GroupMember < Member
|
|||
super
|
||||
end
|
||||
|
||||
def send_request
|
||||
notification_service.new_group_access_request(self)
|
||||
|
||||
super
|
||||
end
|
||||
|
||||
def post_create_hook
|
||||
notification_service.new_group_member(self)
|
||||
|
||||
|
@ -64,10 +58,4 @@ class GroupMember < Member
|
|||
|
||||
super
|
||||
end
|
||||
|
||||
def post_decline_request
|
||||
notification_service.decline_group_access_request(self)
|
||||
|
||||
super
|
||||
end
|
||||
end
|
||||
|
|
|
@ -111,12 +111,6 @@ class ProjectMember < Member
|
|||
super
|
||||
end
|
||||
|
||||
def send_request
|
||||
notification_service.new_project_access_request(self)
|
||||
|
||||
super
|
||||
end
|
||||
|
||||
def post_create_hook
|
||||
unless owner?
|
||||
event_service.join_project(self.project, self.user)
|
||||
|
@ -152,12 +146,6 @@ class ProjectMember < Member
|
|||
super
|
||||
end
|
||||
|
||||
def post_decline_request
|
||||
notification_service.decline_project_access_request(self)
|
||||
|
||||
super
|
||||
end
|
||||
|
||||
def event_service
|
||||
EventCreateService.new
|
||||
end
|
||||
|
|
|
@ -191,8 +191,12 @@ class Repository
|
|||
end
|
||||
end
|
||||
|
||||
def ref_names
|
||||
branch_names + tag_names
|
||||
end
|
||||
|
||||
def branch_names
|
||||
cache.fetch(:branch_names) { branches.map(&:name) }
|
||||
@branch_names ||= cache.fetch(:branch_names) { branches.map(&:name) }
|
||||
end
|
||||
|
||||
def branch_exists?(branch_name)
|
||||
|
@ -267,6 +271,7 @@ class Repository
|
|||
|
||||
def expire_branches_cache
|
||||
cache.expire(:branch_names)
|
||||
@branch_names = nil
|
||||
@local_branches = nil
|
||||
end
|
||||
|
||||
|
@ -332,10 +337,6 @@ class Repository
|
|||
@lookup_cache ||= {}
|
||||
end
|
||||
|
||||
def expire_branch_names
|
||||
cache.expire(:branch_names)
|
||||
end
|
||||
|
||||
def expire_avatar_cache(branch_name = nil, revision = nil)
|
||||
# Avatars are pulled from the default branch, thus if somebody pushes to a
|
||||
# different branch there's no need to expire anything.
|
||||
|
|
|
@ -487,9 +487,8 @@ class User < ActiveRecord::Base
|
|||
events.recent.find do |event|
|
||||
project = Project.find_by_id(event.project_id)
|
||||
next unless project
|
||||
repo = project.repository
|
||||
|
||||
if repo.branch_names.include?(event.branch_name)
|
||||
if project.repository.branch_exists?(event.branch_name)
|
||||
merge_requests = MergeRequest.where("created_at >= ?", event.created_at).
|
||||
where(source_project_id: project.id,
|
||||
source_branch: event.branch_name)
|
||||
|
|
|
@ -21,7 +21,7 @@ module Ci
|
|||
end
|
||||
|
||||
build = builds.find do |build|
|
||||
build.can_be_served?(current_runner)
|
||||
current_runner.can_pick?(build)
|
||||
end
|
||||
|
||||
if build
|
||||
|
|
21
app/services/members/destroy_service.rb
Normal file
21
app/services/members/destroy_service.rb
Normal file
|
@ -0,0 +1,21 @@
|
|||
module Members
|
||||
class DestroyService < BaseService
|
||||
attr_accessor :member, :current_user
|
||||
|
||||
def initialize(member, user)
|
||||
@member, @current_user = member, user
|
||||
end
|
||||
|
||||
def execute
|
||||
unless member && can?(current_user, "destroy_#{member.type.underscore}".to_sym, member)
|
||||
raise Gitlab::Access::AccessDeniedError
|
||||
end
|
||||
|
||||
member.destroy
|
||||
|
||||
if member.request? && member.user != current_user
|
||||
notification_service.decline_access_request(member)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -83,7 +83,7 @@ module MergeRequests
|
|||
closes_issue = "Closes ##{iid}"
|
||||
|
||||
if merge_request.description.present?
|
||||
merge_request.description << closes_issue.prepend("\n")
|
||||
merge_request.description += closes_issue.prepend("\n")
|
||||
else
|
||||
merge_request.description = closes_issue
|
||||
end
|
||||
|
|
|
@ -181,15 +181,16 @@ class NotificationService
|
|||
end
|
||||
end
|
||||
|
||||
# Project access request
|
||||
def new_project_access_request(project_member)
|
||||
mailer.member_access_requested_email(project_member.real_source_type, project_member.id).deliver_later
|
||||
# Members
|
||||
def new_access_request(member)
|
||||
mailer.member_access_requested_email(member.real_source_type, member.id).deliver_later
|
||||
end
|
||||
|
||||
def decline_project_access_request(project_member)
|
||||
mailer.member_access_denied_email(project_member.real_source_type, project_member.project.id, project_member.user.id).deliver_later
|
||||
def decline_access_request(member)
|
||||
mailer.member_access_denied_email(member.real_source_type, member.source_id, member.user_id).deliver_later
|
||||
end
|
||||
|
||||
# Project invite
|
||||
def invite_project_member(project_member, token)
|
||||
mailer.member_invited_email(project_member.real_source_type, project_member.id, token).deliver_later
|
||||
end
|
||||
|
@ -216,15 +217,7 @@ class NotificationService
|
|||
mailer.member_access_granted_email(project_member.real_source_type, project_member.id).deliver_later
|
||||
end
|
||||
|
||||
# Group access request
|
||||
def new_group_access_request(group_member)
|
||||
mailer.member_access_requested_email(group_member.real_source_type, group_member.id).deliver_later
|
||||
end
|
||||
|
||||
def decline_group_access_request(group_member)
|
||||
mailer.member_access_denied_email(group_member.real_source_type, group_member.group.id, group_member.user.id).deliver_later
|
||||
end
|
||||
|
||||
# Group invite
|
||||
def invite_group_member(group_member, token)
|
||||
mailer.member_invited_email(group_member.real_source_type, group_member.id, token).deliver_later
|
||||
end
|
||||
|
|
|
@ -46,7 +46,7 @@
|
|||
Maximum file size is 1MB. Pages are optimized for a 72x72 px header logo
|
||||
|
||||
.form-actions
|
||||
= f.submit 'Save', class: 'btn btn-save'
|
||||
= f.submit 'Save', class: 'btn btn-save append-right-10'
|
||||
- if @appearance.persisted?
|
||||
= link_to 'Preview last save', preview_admin_appearances_path, class: 'btn', target: '_blank'
|
||||
|
||||
|
|
|
@ -1,29 +1,9 @@
|
|||
- page_title "Preview | Appearance"
|
||||
%h3.page-title
|
||||
Appearance settings - Preview
|
||||
%hr
|
||||
.login-box
|
||||
.login-heading
|
||||
%h3 Existing user? Sign in
|
||||
%form
|
||||
= text_field_tag :login, nil, class: "form-control top", placeholder: "Username or Email"
|
||||
= password_field_tag :password, nil, class: "form-control bottom", placeholder: "Password"
|
||||
= button_tag "Sign in", class: "btn-create btn"
|
||||
|
||||
.ui-box
|
||||
.title
|
||||
Sign-in page
|
||||
%div
|
||||
.login-page
|
||||
.container
|
||||
.content
|
||||
.login-title
|
||||
%h1= brand_title
|
||||
%hr
|
||||
.container
|
||||
.content
|
||||
.row
|
||||
.col-sm-7
|
||||
.brand-image
|
||||
= brand_image
|
||||
.brand_text
|
||||
= brand_text
|
||||
.col-sm-4
|
||||
.login-box
|
||||
%h3.page-title Sign in
|
||||
= text_field_tag :login, nil, class: "form-control top", placeholder: "Username or Email"
|
||||
= password_field_tag :password, nil, class: "form-control bottom", placeholder: "Password"
|
||||
= button_tag "Sign in", class: "btn-create btn"
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
- page_title "Appearance"
|
||||
|
||||
%h3.page-title
|
||||
Appearance settings
|
||||
%p.light
|
||||
You can modify the look and feel of GitLab here
|
||||
%hr
|
||||
|
||||
= render 'form'
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
- page_title "Settings"
|
||||
|
||||
%h3.page-title Settings
|
||||
%hr
|
||||
= render 'form'
|
||||
|
|
|
@ -88,28 +88,17 @@
|
|||
= select_tag :access_level, options_for_select(GroupMember.access_level_roles), class: "project-access-select select2"
|
||||
%hr
|
||||
= button_tag 'Add users to group', class: "btn btn-create"
|
||||
|
||||
= render 'shared/members/requests', membership_source: @group, members: @members.request
|
||||
|
||||
.panel.panel-default
|
||||
.panel-heading
|
||||
%h3.panel-title
|
||||
Members
|
||||
%span.badge
|
||||
#{@group.group_members.count}
|
||||
%ul.well-list.group-users-list
|
||||
- @members.each do |member|
|
||||
- user = member.user
|
||||
%li{class: dom_class(member), id: (dom_id(user) if user)}
|
||||
.list-item-name
|
||||
- if user
|
||||
%strong
|
||||
= link_to user.name, admin_user_path(user)
|
||||
- else
|
||||
%strong
|
||||
= member.invite_email
|
||||
(invited)
|
||||
%span.pull-right.light
|
||||
= member.human_access
|
||||
- if can?(current_user, :destroy_group_member, member)
|
||||
= link_to group_group_member_path(@group, member), data: { confirm: remove_member_message(member) }, method: :delete, remote: true, class: "btn-xs btn btn-remove", title: 'Remove user from group' do
|
||||
%i.fa.fa-minus.fa-inverse
|
||||
%strong= @group.name
|
||||
group members
|
||||
%span.badge= @group.members.non_request.size
|
||||
.pull-right
|
||||
= link_to icon('pencil-square-o', text: 'Manage Access'), polymorphic_url([@group, :members]), class: "btn btn-xs"
|
||||
%ul.well-list.group-users-list.content-list
|
||||
= render partial: 'shared/members/member', collection: @members.non_request, as: :member, locals: { show_controls: false }
|
||||
.panel-footer
|
||||
= paginate @members, param_name: 'members_page', theme: 'gitlab'
|
||||
= paginate @members.non_request, param_name: 'members_page', theme: 'gitlab'
|
||||
|
|
|
@ -135,44 +135,27 @@
|
|||
- if @group
|
||||
.panel.panel-default
|
||||
.panel-heading
|
||||
%strong #{@group.name}
|
||||
group members (#{@group.group_members.count})
|
||||
%strong= @group.name
|
||||
group members
|
||||
%span.badge= @group_members.non_request.size
|
||||
.pull-right
|
||||
= link_to admin_group_path(@group), class: 'btn btn-xs' do
|
||||
%i.fa.fa-pencil-square-o
|
||||
%ul.well-list
|
||||
- @group_members.each do |member|
|
||||
= render 'shared/members/member', member: member, show_controls: false
|
||||
= icon('pencil-square-o', text: 'Manage Access')
|
||||
%ul.well-list.content-list
|
||||
= render partial: 'shared/members/member', collection: @group_members.non_request, as: :member, locals: { show_controls: false }
|
||||
.panel-footer
|
||||
= paginate @group_members, param_name: 'group_members_page', theme: 'gitlab'
|
||||
= paginate @group_members.non_request, param_name: 'group_members_page', theme: 'gitlab'
|
||||
|
||||
= render 'shared/members/requests', membership_source: @project, members: @project_members.request
|
||||
|
||||
.panel.panel-default
|
||||
.panel-heading
|
||||
Project members
|
||||
%small
|
||||
(#{@project.users.count})
|
||||
%strong= @project.name
|
||||
project members
|
||||
%span.badge= @project.users.size
|
||||
.pull-right
|
||||
= link_to namespace_project_project_members_path(@project.namespace, @project), class: "btn btn-xs" do
|
||||
%i.fa.fa-pencil-square-o
|
||||
Manage Access
|
||||
%ul.well-list.project_members
|
||||
- @project_members.each do |project_member|
|
||||
- user = project_member.user
|
||||
%li.project_member
|
||||
.list-item-name
|
||||
- if user
|
||||
%strong
|
||||
= link_to user.name, admin_user_path(user)
|
||||
- else
|
||||
%strong
|
||||
= project_member.invite_email
|
||||
(invited)
|
||||
.pull-right
|
||||
- if project_member.owner?
|
||||
%span.light Owner
|
||||
- else
|
||||
%span.light= project_member.human_access
|
||||
= link_to namespace_project_project_member_path(@project.namespace, @project, project_member), data: { confirm: remove_member_message(project_member)}, method: :delete, remote: true, class: "btn btn-sm btn-remove" do
|
||||
%i.fa.fa-times
|
||||
= link_to icon('pencil-square-o', text: 'Manage Access'), polymorphic_url([@project, :members]), class: "btn btn-xs"
|
||||
%ul.well-list.project_members.content-list
|
||||
= render partial: 'shared/members/member', collection: @project_members.non_request, as: :member, locals: { show_controls: false }
|
||||
.panel-footer
|
||||
= paginate @project_members, param_name: 'project_members_page', theme: 'gitlab'
|
||||
= paginate @project_members.non_request, param_name: 'project_members_page', theme: 'gitlab'
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
.col-md-6
|
||||
%h4 Restrict projects for this runner
|
||||
- if @runner.projects.any?
|
||||
%table.table
|
||||
%table.table.assigned-projects
|
||||
%thead
|
||||
%tr
|
||||
%th Assigned projects
|
||||
|
@ -44,7 +44,7 @@
|
|||
.pull-right
|
||||
= link_to 'Disable', [:admin, project.namespace.becomes(Namespace), project, runner_project], method: :delete, class: 'btn btn-danger btn-xs'
|
||||
|
||||
%table.table
|
||||
%table.table.unassigned-projects
|
||||
%thead
|
||||
%tr
|
||||
%th Project
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
Explore Projects
|
||||
|
||||
.nav-controls
|
||||
= form_tag request.original_url, method: :get, class: 'project-filter-form', id: 'project-filter-form' do |f|
|
||||
= form_tag request.path, method: :get, class: 'project-filter-form', id: 'project-filter-form' do |f|
|
||||
= search_field_tag :filter_projects, params[:filter_projects], placeholder: 'Filter by name...', class: 'project-filter-form-field form-control input-short projects-list-filter', spellcheck: false, id: 'project-filter-form-field', tabindex: "2"
|
||||
= render 'shared/projects/dropdown'
|
||||
- if current_user.can_create_project?
|
||||
|
|
|
@ -17,8 +17,7 @@
|
|||
.panel-heading
|
||||
%strong #{@group.name}
|
||||
group members
|
||||
%small
|
||||
(#{@members.total_count})
|
||||
%span.badge= @members.non_request.size
|
||||
.controls
|
||||
= form_tag group_group_members_path(@group), method: :get, class: 'form-inline member-search-form' do
|
||||
.form-group
|
||||
|
|
|
@ -33,7 +33,7 @@
|
|||
= link_to "#shared", 'data-toggle' => 'tab' do
|
||||
Shared Projects
|
||||
.nav-controls
|
||||
= form_tag request.original_url, method: :get, class: 'project-filter-form', id: 'project-filter-form' do |f|
|
||||
= form_tag request.path, method: :get, class: 'project-filter-form', id: 'project-filter-form' do |f|
|
||||
= search_field_tag :filter_projects, nil, placeholder: 'Filter by name', class: 'projects-list-filter form-control', spellcheck: false
|
||||
= render 'shared/projects/dropdown'
|
||||
- if can? current_user, :create_projects, @group
|
||||
|
|
|
@ -28,8 +28,12 @@
|
|||
.key ⌘ shift p
|
||||
- else
|
||||
.key ctrl shift p
|
||||
|
||||
%td Toggle Markdown preview
|
||||
%tr
|
||||
%td.shortcut
|
||||
.key
|
||||
%i.fa.fa-arrow-up
|
||||
%td Edit last comment (when focused on an empty textarea)
|
||||
%tbody
|
||||
%tr
|
||||
%th
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
= image_tag avatar_icon(current_user, 60), alt: 'Profile', class: 'avatar avatar s36'
|
||||
.username
|
||||
= current_user.username
|
||||
= link_to '#', class: "nav-header-btn text-center pin-nav-btn #{'is-active' if pinned_nav?} js-nav-pin", title: 'Pin/Unpin navigation' do
|
||||
= link_to '#', class: "nav-header-btn text-center pin-nav-btn has-tooltip #{'is-active' if pinned_nav?} js-nav-pin", title: pinned_nav? ? "Unpin navigation" : "Pin Navigation", data: {placement: 'right', container: 'body'} do
|
||||
%span.sr-only Toggle navigation pinning
|
||||
= icon('thumb-tack')
|
||||
- if defined?(nav) && nav
|
||||
|
|
|
@ -1,60 +1,41 @@
|
|||
%ul.nav-links.scrolling-tabs
|
||||
.fade-left
|
||||
= 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
|
||||
%span
|
||||
Monitoring
|
||||
= nav_link(controller: :deploy_keys) do
|
||||
= link_to admin_deploy_keys_path, title: 'Deploy Keys' do
|
||||
%span
|
||||
Deploy Keys
|
||||
= nav_link(controller: :broadcast_messages) do
|
||||
= link_to admin_broadcast_messages_path, title: 'Messages' do
|
||||
%span
|
||||
Messages
|
||||
= nav_link(controller: :hooks) do
|
||||
= link_to admin_hooks_path, title: 'Hooks' do
|
||||
%span
|
||||
Hooks
|
||||
%div{ class: nav_control_class }
|
||||
= render 'layouts/nav/admin_settings'
|
||||
|
||||
= nav_link(controller: :appearances) do
|
||||
= link_to admin_appearances_path, title: 'Appearances' do
|
||||
%span
|
||||
Appearance
|
||||
|
||||
= nav_link(controller: :applications) do
|
||||
= link_to admin_applications_path, title: 'Applications' do
|
||||
%span
|
||||
Applications
|
||||
|
||||
= nav_link(controller: :services) do
|
||||
= link_to admin_application_settings_services_path, title: 'Service Templates' do
|
||||
%span
|
||||
Service Templates
|
||||
|
||||
= nav_link(controller: :labels) do
|
||||
= link_to admin_labels_path, title: 'Labels' do
|
||||
%span
|
||||
Labels
|
||||
|
||||
= nav_link(controller: :abuse_reports) do
|
||||
= link_to admin_abuse_reports_path, title: "Abuse Reports" do
|
||||
%span
|
||||
Abuse Reports
|
||||
%span.badge.count= number_with_delimiter(AbuseReport.count(:all))
|
||||
|
||||
- if askimet_enabled?
|
||||
= nav_link(controller: :spam_logs) do
|
||||
= link_to admin_spam_logs_path, title: "Spam Logs" do
|
||||
%ul.nav-links.scrolling-tabs
|
||||
%li.fade-left
|
||||
= icon('arrow-left')
|
||||
= 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
|
||||
Spam Logs
|
||||
Overview
|
||||
= nav_link(controller: %w(background_jobs logs health_check)) do
|
||||
= link_to admin_background_jobs_path, title: 'Monitoring' do
|
||||
%span
|
||||
Monitoring
|
||||
= nav_link(controller: :broadcast_messages) do
|
||||
= link_to admin_broadcast_messages_path, title: 'Messages' do
|
||||
%span
|
||||
Messages
|
||||
= nav_link(controller: :hooks) do
|
||||
= link_to admin_hooks_path, title: 'Hooks' do
|
||||
%span
|
||||
System Hooks
|
||||
|
||||
= nav_link(controller: :application_settings, html_options: { class: 'separate-item'}) do
|
||||
= link_to admin_application_settings_path, title: 'Settings' do
|
||||
%span
|
||||
Settings
|
||||
.fade-right
|
||||
= nav_link(controller: :applications) do
|
||||
= link_to admin_applications_path, title: 'Applications' do
|
||||
%span
|
||||
Applications
|
||||
|
||||
= nav_link(controller: :abuse_reports) do
|
||||
= link_to admin_abuse_reports_path, title: "Abuse Reports" do
|
||||
%span
|
||||
Abuse Reports
|
||||
%span.badge.count= number_with_delimiter(AbuseReport.count(:all))
|
||||
|
||||
- if askimet_enabled?
|
||||
= nav_link(controller: :spam_logs) do
|
||||
= link_to admin_spam_logs_path, title: "Spam Logs" do
|
||||
%span
|
||||
Spam Logs
|
||||
%li.fade-right
|
||||
= icon('arrow-right')
|
||||
|
|
31
app/views/layouts/nav/_admin_settings.html.haml
Normal file
31
app/views/layouts/nav/_admin_settings.html.haml
Normal file
|
@ -0,0 +1,31 @@
|
|||
.controls
|
||||
.dropdown.admin-settings-dropdown
|
||||
%a.dropdown-new.btn.btn-default{href: '#', 'data-toggle' => 'dropdown'}
|
||||
= icon('cog')
|
||||
= icon('caret-down')
|
||||
%ul.dropdown-menu.dropdown-menu-align-right
|
||||
= nav_link(controller: :deploy_keys) do
|
||||
= link_to admin_deploy_keys_path, title: 'Deploy Keys' do
|
||||
%span
|
||||
Deploy Keys
|
||||
|
||||
= nav_link(controller: :services) do
|
||||
= link_to admin_application_settings_services_path, title: 'Service Templates' do
|
||||
%span
|
||||
Service Templates
|
||||
|
||||
= nav_link(controller: :labels) do
|
||||
= link_to admin_labels_path, title: 'Labels' do
|
||||
%span
|
||||
Labels
|
||||
|
||||
= nav_link(controller: :appearances) do
|
||||
= link_to admin_appearances_path, title: 'Appearances' do
|
||||
%span
|
||||
Appearance
|
||||
|
||||
%li.divider
|
||||
= nav_link(controller: :application_settings) do
|
||||
= link_to admin_application_settings_path, title: 'Settings' do
|
||||
%span
|
||||
Settings
|
|
@ -2,7 +2,8 @@
|
|||
= render 'layouts/nav/group_settings'
|
||||
|
||||
%ul.nav-links.scrolling-tabs
|
||||
.fade-left
|
||||
%li.fade-left
|
||||
= icon('arrow-left')
|
||||
= nav_link(path: 'groups#show', html_options: {class: 'home'}) do
|
||||
= link_to group_path(@group), title: 'Home' do
|
||||
%span
|
||||
|
@ -31,4 +32,5 @@
|
|||
= link_to group_group_members_path(@group), title: 'Members' do
|
||||
%span
|
||||
Members
|
||||
.fade-right
|
||||
%li.fade-right
|
||||
= icon('arrow-right')
|
||||
|
|
|
@ -1,16 +1,22 @@
|
|||
- if current_user
|
||||
- if access = @group.users.find_by(id: current_user.id)
|
||||
.controls
|
||||
.dropdown.group-settings-dropdown
|
||||
%a.dropdown-new.btn.btn-default#group-settings-button{href: '#', 'data-toggle' => 'dropdown'}
|
||||
= icon('cog')
|
||||
= icon('caret-down')
|
||||
%ul.dropdown-menu.dropdown-menu-align-right
|
||||
- if can?(current_user, :admin_group, @group)
|
||||
= nav_link(path: 'groups#projects') do
|
||||
= link_to projects_group_path(@group), title: 'Projects' do
|
||||
Projects
|
||||
%li.divider
|
||||
%li
|
||||
= link_to edit_group_path(@group) do
|
||||
Edit Group
|
||||
- can_edit = can?(current_user, :admin_group, @group)
|
||||
- member = @group.members.non_request.find_by(user_id: current_user.id)
|
||||
- can_leave = member && can?(current_user, :destroy_group_member, member)
|
||||
|
||||
.controls
|
||||
.dropdown.group-settings-dropdown
|
||||
%a.dropdown-new.btn.btn-default#group-settings-button{href: '#', 'data-toggle' => 'dropdown'}
|
||||
= icon('cog')
|
||||
= icon('caret-down')
|
||||
%ul.dropdown-menu.dropdown-menu-align-right
|
||||
= nav_link(path: 'groups#projects') do
|
||||
= link_to 'Projects', projects_group_path(@group), title: 'Projects'
|
||||
%li.divider
|
||||
- if can_edit
|
||||
%li
|
||||
= link_to 'Edit Group', edit_group_path(@group)
|
||||
- if can_leave
|
||||
%li
|
||||
= link_to polymorphic_path([:leave, @group, :members]),
|
||||
data: { confirm: leave_confirmation_message(@group) }, method: :delete, title: 'Leave group' do
|
||||
Leave Group
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
%ul.nav-links.scrolling-tabs
|
||||
.fade-left
|
||||
%li.fade-left
|
||||
= icon('arrow-left')
|
||||
= nav_link(path: 'profiles#show', html_options: {class: 'home'}) do
|
||||
= link_to profile_path, title: 'Profile Settings' do
|
||||
%span
|
||||
|
@ -43,4 +44,5 @@
|
|||
= link_to audit_log_profile_path, title: 'Audit Log' do
|
||||
%span
|
||||
Audit Log
|
||||
.fade-right
|
||||
%li.fade-right
|
||||
= icon('arrow-right')
|
||||
|
|
|
@ -5,19 +5,20 @@
|
|||
= icon('cog')
|
||||
= icon('caret-down')
|
||||
%ul.dropdown-menu.dropdown-menu-align-right
|
||||
- is_project_member = @project.users.exists?(current_user.id)
|
||||
- access = @project.team.max_member_access(current_user.id)
|
||||
- can_edit = can?(current_user, :admin_project, @project)
|
||||
-# We don't use @project.team.find_member because it searches for group members too...
|
||||
- member = @project.members.non_request.find_by(user_id: current_user.id)
|
||||
- can_leave = member && can?(current_user, :destroy_project_member, member)
|
||||
|
||||
= render 'layouts/nav/project_settings', access: access, can_edit: can_edit
|
||||
= render 'layouts/nav/project_settings', can_edit: can_edit
|
||||
|
||||
- if can_edit || is_project_member
|
||||
- if can_edit || can_leave
|
||||
%li.divider
|
||||
- if can_edit
|
||||
%li
|
||||
= link_to edit_project_path(@project) do
|
||||
Edit Project
|
||||
- if is_project_member
|
||||
- if can_leave
|
||||
%li
|
||||
= link_to polymorphic_path([:leave, @project, :members]),
|
||||
data: { confirm: leave_confirmation_message(@project) }, method: :delete, title: 'Leave project' do
|
||||
|
@ -25,7 +26,8 @@
|
|||
|
||||
%div{ class: nav_control_class }
|
||||
%ul.nav-links.scrolling-tabs
|
||||
.fade-left
|
||||
%li.fade-left
|
||||
= icon('arrow-left')
|
||||
= nav_link(path: 'projects#show', html_options: {class: 'home'}) do
|
||||
= link_to project_path(@project), title: 'Project', class: 'shortcuts-project' do
|
||||
%span
|
||||
|
@ -38,9 +40,9 @@
|
|||
|
||||
- if project_nav_tab? :files
|
||||
= nav_link(controller: %w(tree blob blame edit_tree new_tree find_file commit commits compare repositories tags branches releases network)) do
|
||||
= link_to project_files_path(@project), title: 'Code', class: 'shortcuts-tree' do
|
||||
= link_to project_files_path(@project), title: 'Repository', class: 'shortcuts-tree' do
|
||||
%span
|
||||
Code
|
||||
Repository
|
||||
|
||||
- if project_nav_tab? :pipelines
|
||||
= nav_link(controller: [:pipelines, :builds, :environments]) do
|
||||
|
@ -109,4 +111,5 @@
|
|||
%li.hidden
|
||||
= link_to project_commits_path(@project), title: 'Commits', class: 'shortcuts-commits' do
|
||||
Commits
|
||||
.fade-right
|
||||
%li.fade-right
|
||||
= icon('arrow-right')
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
= link_to namespace_project_project_members_path(@project.namespace, @project), title: 'Members', class: 'team-tab tab' do
|
||||
%span
|
||||
Members
|
||||
- if access && can_edit
|
||||
- if can_edit
|
||||
- if @project.allowed_to_share_with_group?
|
||||
= nav_link(controller: :group_links) do
|
||||
= link_to namespace_project_group_links_path(@project.namespace, @project), title: "Groups" do
|
||||
|
|
|
@ -6,4 +6,4 @@
|
|||
%ul
|
||||
- @errors.each do |error|
|
||||
%li
|
||||
error
|
||||
#{error}
|
||||
|
|
|
@ -1,6 +0,0 @@
|
|||
Project <%= @project.name %> couldn't be exported.
|
||||
|
||||
The errors we encountered were:
|
||||
|
||||
- @errors.each do |error|
|
||||
<%= error %>
|
|
@ -0,0 +1,6 @@
|
|||
= "Project #{@project.name} couldn't be exported."
|
||||
|
||||
= "The errors we encountered were:"
|
||||
|
||||
- @errors.each do |error|
|
||||
#{error}
|
|
@ -28,7 +28,7 @@
|
|||
= label_tag :global_notification_level, "Global notification level", class: "label-light"
|
||||
%br
|
||||
.clearfix
|
||||
.form-group.pull-left
|
||||
.form-group.pull-left.global-notification-setting
|
||||
= render 'shared/notifications/button', notification_setting: @global_notification_setting, left_align: true
|
||||
|
||||
.clearfix
|
||||
|
|
|
@ -14,8 +14,17 @@
|
|||
%span This is a confidential issue. Your comment will not be visible to the public.
|
||||
|
||||
%li.pull-right
|
||||
%button.zen-control.zen-control-full.js-zen-enter{ type: 'button', tabindex: -1 }
|
||||
Go full screen
|
||||
.toolbar-group
|
||||
= 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: "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" })
|
||||
.toolbar-group
|
||||
%button.toolbar-btn.js-zen-enter.has-tooltip.hidden-xs{ type: "button", tabindex: -1, aria: { label: "Go full screen" }, title: "Go full screen", data: { container: "body" } }
|
||||
=icon("arrows-alt fw")
|
||||
|
||||
.md-write-holder
|
||||
= yield
|
||||
|
@ -24,7 +33,7 @@
|
|||
- if defined?(referenced_users) && referenced_users
|
||||
%div.referenced-users.hide
|
||||
%span
|
||||
= icon('exclamation-triangle')
|
||||
= icon("exclamation-triangle")
|
||||
You are about to add
|
||||
%strong
|
||||
%span.js-referenced-users-count 0
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
%b Builds badge ·
|
||||
= @build_badge.to_html
|
||||
.pull-right
|
||||
= render 'shared/ref_switcher', destination: 'badges'
|
||||
= render 'shared/ref_switcher', destination: 'badges', align_right: true
|
||||
.panel-body
|
||||
.row
|
||||
.col-md-2.text-center
|
||||
|
|
|
@ -17,6 +17,8 @@
|
|||
= dropdown_tag("Choose a License template", options: { toggle_class: 'js-license-selector', title: "Choose a license", filter: true, placeholder: "Filter", data: { data: licenses_for_select, project: @project.name, fullname: @project.namespace.human_name } } )
|
||||
.gitignore-selector.js-gitignore-selector-wrap.hidden
|
||||
= dropdown_tag("Choose a .gitignore template", options: { toggle_class: 'js-gitignore-selector', title: "Choose a template", filter: true, placeholder: "Filter", data: { data: gitignore_names } } )
|
||||
.gitlab-ci-yml-selector.js-gitlab-ci-yml-selector-wrap.hidden
|
||||
= dropdown_tag("Choose a GitLab CI Yaml template", options: { toggle_class: 'js-gitlab-ci-yml-selector', title: "Choose a template", filter: true, placeholder: "Filter", data: { data: gitlab_ci_ymls } } )
|
||||
.encoding-selector
|
||||
= select_tag :encoding, options_for_select([ "base64", "text" ], "text"), class: 'select2'
|
||||
|
||||
|
|
|
@ -24,8 +24,8 @@
|
|||
%span.label.label-warning stuck
|
||||
|
||||
%p.commit-title
|
||||
- if commit_data = pipeline.commit_data
|
||||
= link_to_gfm truncate(commit_data.title, length: 60), namespace_project_commit_path(@project.namespace, @project, commit_data.id), class: "commit-row-message"
|
||||
- if commit = pipeline.commit
|
||||
= link_to_gfm truncate(commit.title, length: 60), namespace_project_commit_path(@project.namespace, @project, commit.id), class: "commit-row-message"
|
||||
- else
|
||||
Cant find HEAD commit for this branch
|
||||
|
||||
|
|
|
@ -10,29 +10,30 @@
|
|||
= cache(cache_key) do
|
||||
%li.commit.js-toggle-container{ id: "commit-#{commit.short_id}" }
|
||||
= commit_author_avatar(commit, size: 36)
|
||||
.commit-row-title
|
||||
%span.item-title
|
||||
= link_to_gfm commit.title, namespace_project_commit_path(project.namespace, project, commit.id), class: "commit-row-message"
|
||||
%span.commit-row-message.visible-xs-inline
|
||||
·
|
||||
= commit.short_id
|
||||
- if commit.status
|
||||
= render_commit_status(commit, cssclass: 'visible-xs-inline')
|
||||
- if commit.description?
|
||||
%a.text-expander.hidden-xs.js-toggle-button ...
|
||||
.commit-info-block
|
||||
.commit-row-title
|
||||
%span.item-title
|
||||
= link_to_gfm commit.title, namespace_project_commit_path(project.namespace, project, commit.id), class: "commit-row-message"
|
||||
%span.commit-row-message.visible-xs-inline
|
||||
·
|
||||
= commit.short_id
|
||||
- if commit.status
|
||||
= render_commit_status(commit, cssclass: 'visible-xs-inline')
|
||||
- if commit.description?
|
||||
%a.text-expander.hidden-xs.js-toggle-button ...
|
||||
|
||||
.commit-actions.hidden-xs
|
||||
- if commit.status
|
||||
= render_commit_status(commit, cssclass: 'btn btn-transparent')
|
||||
= clipboard_button_with_class({ clipboard_text: commit.id }, css_class: 'btn-transparent')
|
||||
= link_to commit.short_id, namespace_project_commit_path(project.namespace, project, commit), class: "commit-short-id btn btn-transparent"
|
||||
= link_to_browse_code(project, commit)
|
||||
.commit-actions.hidden-xs
|
||||
- if commit.status
|
||||
= render_commit_status(commit, cssclass: 'btn btn-transparent')
|
||||
= clipboard_button_with_class({ clipboard_text: commit.id }, css_class: 'btn-transparent')
|
||||
= link_to commit.short_id, namespace_project_commit_path(project.namespace, project, commit), class: "commit-short-id btn btn-transparent"
|
||||
= link_to_browse_code(project, commit)
|
||||
|
||||
- if commit.description?
|
||||
%pre.commit-row-description.js-toggle-content
|
||||
= preserve(markdown(escape_once(commit.description), pipeline: :single_line, author: commit.author))
|
||||
- if commit.description?
|
||||
%pre.commit-row-description.js-toggle-content
|
||||
= preserve(markdown(escape_once(commit.description), pipeline: :single_line, author: commit.author))
|
||||
|
||||
.commit-row-info
|
||||
= commit_author_link(commit, avatar: false, size: 24)
|
||||
authored
|
||||
#{time_ago_with_tooltip(commit.committed_date)}
|
||||
.commit-row-info
|
||||
= commit_author_link(commit, avatar: false, size: 24)
|
||||
authored
|
||||
#{time_ago_with_tooltip(commit.committed_date)}
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
.scrolling-tabs-container
|
||||
.nav-links.sub-nav.scrolling-tabs
|
||||
%ul{ class: (container_class) }
|
||||
.fade-left
|
||||
%li.fade-left
|
||||
= icon('arrow-left')
|
||||
= nav_link(controller: %w(tree blob blame edit_tree new_tree find_file)) do
|
||||
= link_to project_files_path(@project) do
|
||||
Files
|
||||
|
@ -25,4 +26,5 @@
|
|||
= nav_link(controller: [:tags, :releases]) do
|
||||
= link_to namespace_project_tags_path(@project.namespace, @project) do
|
||||
Tags
|
||||
.fade-right
|
||||
%li.fade-right
|
||||
= icon('arrow-right')
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
= render "projects/commits/head"
|
||||
|
||||
%div{ class: (container_class) }
|
||||
.row-content-block.second-block.content-component-block
|
||||
.sub-header-block
|
||||
Compare branches, tags or commit ranges.
|
||||
%br
|
||||
Fill input field with commit id like
|
||||
|
|
|
@ -1,24 +1,24 @@
|
|||
- @no_container = true
|
||||
- page_title "#{params[:from]}...#{params[:to]}"
|
||||
= render "projects/commits/head"
|
||||
|
||||
%div{ class: (container_class) }
|
||||
.sub-header-block.no-bottom-space
|
||||
= render "form"
|
||||
|
||||
.row-content-block
|
||||
= render "form"
|
||||
|
||||
- if @commits.present?
|
||||
.prepend-top-default
|
||||
- if @commits.present?
|
||||
= render "projects/commits/commit_list"
|
||||
= render "projects/diffs/diffs", diffs: @diffs, project: @project, diff_refs: @diff_refs
|
||||
- else
|
||||
.light-well.prepend-top-default
|
||||
.center
|
||||
%h4
|
||||
There isn't anything to compare.
|
||||
%p.slead
|
||||
- if params[:to] == params[:from]
|
||||
%span.label-branch #{params[:from]}
|
||||
and
|
||||
%span.label-branch #{params[:to]}
|
||||
are the same.
|
||||
- else
|
||||
You'll need to use different branch names to get a valid comparison.
|
||||
- else
|
||||
.light-well
|
||||
.center
|
||||
%h4
|
||||
There isn't anything to compare.
|
||||
%p.slead
|
||||
- if params[:to] == params[:from]
|
||||
%span.label-branch #{params[:from]}
|
||||
and
|
||||
%span.label-branch #{params[:to]}
|
||||
are the same.
|
||||
- else
|
||||
You'll need to use different branch names to get a valid comparison.
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue