Commit d5fac1ce authored by Kamil Trzcinski's avatar Kamil Trzcinski

Merge remote-tracking branch 'origin/master' into retry-spinach-tests

parents 2fd009d2 b4e84809
We’re closing our issue tracker on GitHub so we can focus on the GitLab.com project and respond to issues more quickly.
We encourage you to open an issue on the [GitLab.com issue tracker](https://gitlab.com/gitlab-org/gitlab-ce/issues). You can log into GitLab.com using your GitHub account.
Thank you for taking the time to contribute back to GitLab!
Please open a merge request [on GitLab.com](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests), we look forward to reviewing your contribution! You can log into GitLab.com using your GitHub account.
...@@ -61,6 +61,8 @@ update-knapsack: ...@@ -61,6 +61,8 @@ update-knapsack:
- scripts/merge-reports knapsack/rspec_report.json knapsack/rspec_node_*.json - scripts/merge-reports knapsack/rspec_report.json knapsack/rspec_node_*.json
- scripts/merge-reports knapsack/spinach_report.json knapsack/spinach_node_*.json - scripts/merge-reports knapsack/spinach_report.json knapsack/spinach_node_*.json
- rm -f knapsack/*_node_*.json - rm -f knapsack/*_node_*.json
only:
- master
# Execute all testing suites # Execute all testing suites
......
...@@ -13,7 +13,8 @@ AllCops: ...@@ -13,7 +13,8 @@ AllCops:
# Exclude some GitLab files # Exclude some GitLab files
Exclude: Exclude:
- 'vendor/**/*' - 'vendor/**/*'
- 'db/**/*' - 'db/*'
- 'db/fixtures/**/*'
- 'tmp/**/*' - 'tmp/**/*'
- 'bin/**/*' - 'bin/**/*'
- 'lib/backup/**/*' - 'lib/backup/**/*'
...@@ -1088,6 +1089,9 @@ Rails/TimeZone: ...@@ -1088,6 +1089,9 @@ Rails/TimeZone:
Rails/Validation: Rails/Validation:
Enabled: false Enabled: false
Rails/UniqBeforePluck:
Enabled: false
##################### RSpec ################################## ##################### RSpec ##################################
# Check that instances are not being stubbed globally. # Check that instances are not being stubbed globally.
......
...@@ -6,11 +6,14 @@ v 8.9.0 (unreleased) ...@@ -6,11 +6,14 @@ v 8.9.0 (unreleased)
- Fix endless redirections when accessing user OAuth applications when they are disabled - Fix endless redirections when accessing user OAuth applications when they are disabled
- Allow enabling wiki page events from Webhook management UI - Allow enabling wiki page events from Webhook management UI
- Bump rouge to 1.11.0 - Bump rouge to 1.11.0
- Fix issue with arrow keys not working in search autocomplete dropdown
- Make EmailsOnPushWorker use Sidekiq mailers queue - Make EmailsOnPushWorker use Sidekiq mailers queue
- Fix wiki page events' webhook to point to the wiki repository - Fix wiki page events' webhook to point to the wiki repository
- Fix issue todo not remove when leave project !4150 (Long Nguyen) - 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 - Bump recaptcha gem to 3.0.0 to remove deprecated stoken support
- Allow forking projects with restricted visibility level - Allow forking projects with restricted visibility level
- Added descriptions to notification settings dropdown
- Improve note validation to prevent errors when creating invalid note via API - Improve note validation to prevent errors when creating invalid note via API
- Reduce number of fog gem dependencies - Reduce number of fog gem dependencies
- Remove project notification settings associated with deleted projects - Remove project notification settings associated with deleted projects
...@@ -19,14 +22,18 @@ v 8.9.0 (unreleased) ...@@ -19,14 +22,18 @@ v 8.9.0 (unreleased)
- Fix groups API to list only user's accessible projects - Fix groups API to list only user's accessible projects
- Retry spinach tests in case of failure using rerun reporter - Retry spinach tests in case of failure using rerun reporter
- Redesign account and email confirmation emails - Redesign account and email confirmation emails
- `git clone https://host/namespace/project` now works, in addition to using the `.git` suffix
- Bump nokogiri to 1.6.8 - Bump nokogiri to 1.6.8
- Use gitlab-shell v3.0.0 - Use gitlab-shell v3.0.0
- Upgrade to jQuery 2
- Use Knapsack to evenly distribute tests across multiple nodes - Use Knapsack to evenly distribute tests across multiple nodes
- Add `sha` parameter to MR merge API, to ensure only reviewed changes are merged - 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 - Don't allow MRs to be merged when commits were added since the last review / page load
- Add DB index on users.state - Add DB index on users.state
- Add rake task 'gitlab:db:configure' for conditionally seeding or migrating the database - 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) - Changed the Slack build message to use the singular duration if necessary (Aran Koning)
- Links from a wiki page to other wiki pages should be rewritten as expected
- Add option to project to only allow merge requests to be merged if the build succeeds (Rui Santos)
- Fix issues filter when ordering by milestone - Fix issues filter when ordering by milestone
- Todos will display target state if issuable target is 'Closed' or 'Merged' - Todos will display target state if issuable target is 'Closed' or 'Merged'
- Fix bug when sorting issues by milestone due date and filtering by two or more labels - Fix bug when sorting issues by milestone due date and filtering by two or more labels
...@@ -37,31 +44,41 @@ v 8.9.0 (unreleased) ...@@ -37,31 +44,41 @@ v 8.9.0 (unreleased)
- Use downcased path to container repository as this is expected path by Docker - Use downcased path to container repository as this is expected path by Docker
- Projects pending deletion will render a 404 page - Projects pending deletion will render a 404 page
- Measure queue duration between gitlab-workhorse and Rails - Measure queue duration between gitlab-workhorse and Rails
- Make Omniauth providers specs to not modify global configuration
- Make authentication service for Container Registry to be compatible with < Docker 1.11 - Make authentication service for Container Registry to be compatible with < Docker 1.11
- Add Application Setting to configure Container Registry token expire delay (default 5min) - Add Application Setting to configure Container Registry token expire delay (default 5min)
- Cache assigned issue and merge request counts in sidebar nav - Cache assigned issue and merge request counts in sidebar nav
- Use Knapsack only in CI environment - Use Knapsack only in CI environment
- Cache project build count in sidebar nav - Cache project build count in sidebar nav
- Add milestone expire date to the right sidebar
- Fix markdown_spec to use before instead of before(:all) to properly cleanup database after testing - Fix markdown_spec to use before instead of before(:all) to properly cleanup database after testing
- Reduce number of queries needed to render issue labels in the sidebar - Reduce number of queries needed to render issue labels in the sidebar
- Improve error handling importing projects - Improve error handling importing projects
- Remove duplicated notification settings - Remove duplicated notification settings
- Put project Files and Commits tabs under Code tab - Put project Files and Commits tabs under Code tab
- Replace Colorize with Rainbow for coloring console output in Rake tasks. - Replace Colorize with Rainbow for coloring console output in Rake tasks.
- Add workhorse controller and API helpers
- An indicator is now displayed at the top of the comment field for confidential issues. - An indicator is now displayed at the top of the comment field for confidential issues.
- RepositoryCheck::SingleRepositoryWorker public and private methods are now instrumented - RepositoryCheck::SingleRepositoryWorker public and private methods are now instrumented
- Improve issuables APIs performance when accessing notes !4471
v 8.8.4 (unreleased) - External links now open in a new tab
- Markdown editor now correctly resets the input value on edit cancellation !4175
- Toggling a task list item in a issue/mr description does not creates a Todo for mentions
- Improved UX of date pickers on issue & milestone forms
- Cache on the database if a project has an active external issue tracker.
- Put project Labels and Milestones pages links under Issues and Merge Requests tabs as subnav
v 8.8.5 (unreleased)
- Ensure branch cleanup regardless of whether the GitHub import process succeeds - Ensure branch cleanup regardless of whether the GitHub import process succeeds
- Fix issue with arrow keys not working in search autocomplete dropdown
- Fix todos page throwing errors when you have a project pending deletion - Fix todos page throwing errors when you have a project pending deletion
- Reduce number of SQL queries when rendering user references - Reduce number of SQL queries when rendering user references
- Upgrade to jQuery 2
- Remove prev/next buttons on issues and merge requests
- Import GitHub repositories respecting the API rate limit - Import GitHub repositories respecting the API rate limit
- Fix importer for GitHub comments on diff - Fix importer for GitHub comments on diff
- Disable Webhooks before proceeding with the GitHub import - Disable Webhooks before proceeding with the GitHub import
- Added descriptions to notification settings dropdown - Fix incremental trace upload API when using multi-byte UTF-8 chars in trace
v 8.8.4
- Fix LDAP-based login for users with 2FA enabled. !4493
v 8.8.3 v 8.8.3
- Fix 404 page when viewing TODOs that contain milestones or labels in different projects. !4312 - Fix 404 page when viewing TODOs that contain milestones or labels in different projects. !4312
...@@ -173,6 +190,7 @@ v 8.8.0 ...@@ -173,6 +190,7 @@ v 8.8.0
- Fixed advice on invalid permissions on upload path !2948 (Ludovic Perrine) - Fixed advice on invalid permissions on upload path !2948 (Ludovic Perrine)
- Allows MR authors to have the source branch removed when merging the MR. !2801 (Jeroen Jacobs) - Allows MR authors to have the source branch removed when merging the MR. !2801 (Jeroen Jacobs)
- When creating a .gitignore file a dropdown with templates will be provided - 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.7 v 8.7.7
- Fix import by `Any Git URL` broken if the URL contains a space - Fix import by `Any Git URL` broken if the URL contains a space
......
class @Activities class @Activities
constructor: -> constructor: ->
Pager.init 20, true Pager.init 20, true, false, @updateTooltips
$(".event-filter-link").on "click", (event) => $(".event-filter-link").on "click", (event) =>
event.preventDefault() event.preventDefault()
@toggleFilter($(event.currentTarget)) @toggleFilter($(event.currentTarget))
@reloadActivities() @reloadActivities()
updateTooltips: ->
gl.utils.localTimeAgo($('.js-timeago', '#activity'))
reloadActivities: -> reloadActivities: ->
$(".content_list").html '' $(".content_list").html ''
Pager.init 20, true Pager.init 20, true
......
...@@ -35,7 +35,6 @@ ...@@ -35,7 +35,6 @@
#= require raphael #= require raphael
#= require g.raphael #= require g.raphael
#= require g.bar #= require g.bar
#= require Chart
#= require branch-graph #= require branch-graph
#= require ace/ace #= require ace/ace
#= require ace/ext-searchbox #= require ace/ext-searchbox
...@@ -226,6 +225,10 @@ $ -> ...@@ -226,6 +225,10 @@ $ ->
form = btn.closest("form") form = btn.closest("form")
new ConfirmDangerModal(form, text) new ConfirmDangerModal(form, text)
$(document).on 'click', 'button', ->
$(this).blur()
$('input[type="search"]').each -> $('input[type="search"]').each ->
$this = $(this) $this = $(this)
$this.attr 'value', $this.val() $this.attr 'value', $this.val()
...@@ -264,9 +267,10 @@ $ -> ...@@ -264,9 +267,10 @@ $ ->
$(document).trigger('breakpoint:change', [bootstrapBreakpoint]) $(document).trigger('breakpoint:change', [bootstrapBreakpoint])
$(window) $(window)
.off "resize" .off "resize.app"
.on "resize", (e) -> .on "resize.app", (e) ->
fitSidebarForSize() fitSidebarForSize()
gl.awardsHandler = new AwardsHandler()
checkInitialSidebarSize() checkInitialSidebarSize()
new Aside() new Aside()
...@@ -65,7 +65,7 @@ class @AwardsHandler ...@@ -65,7 +65,7 @@ class @AwardsHandler
$addBtn.removeClass 'is-loading' $addBtn.removeClass 'is-loading'
$menu = $('.emoji-menu') $menu = $('.emoji-menu')
@positionMenu($menu, $addBtn) @positionMenu($menu, $addBtn)
@renderFrequentlyUsedBlock() @renderFrequentlyUsedBlock() unless @frequentEmojiBlockRendered
setTimeout => setTimeout =>
$menu.addClass 'is-visible' $menu.addClass 'is-visible'
...@@ -100,7 +100,7 @@ class @AwardsHandler ...@@ -100,7 +100,7 @@ class @AwardsHandler
$menu.css(css) $menu.css(css)
addAward: (votesBlock, awardUrl, emoji, checkMutuality = yes, callback) -> addAward: (votesBlock, awardUrl, emoji, checkMutuality = true, callback) ->
emoji = @normilizeEmojiName emoji emoji = @normilizeEmojiName emoji
...@@ -111,7 +111,7 @@ class @AwardsHandler ...@@ -111,7 +111,7 @@ class @AwardsHandler
$('.emoji-menu').removeClass 'is-visible' $('.emoji-menu').removeClass 'is-visible'
addAwardToEmojiBar: (votesBlock, emoji, checkForMutuality = yes) -> addAwardToEmojiBar: (votesBlock, emoji, checkForMutuality = true) ->
@checkMutuality votesBlock, emoji if checkForMutuality @checkMutuality votesBlock, emoji if checkForMutuality
@addEmojiToFrequentlyUsedList emoji @addEmojiToFrequentlyUsedList emoji
...@@ -153,7 +153,7 @@ class @AwardsHandler ...@@ -153,7 +153,7 @@ class @AwardsHandler
if isAlreadyVoted if isAlreadyVoted
@showEmojiLoader $emojiButton @showEmojiLoader $emojiButton
@addAward votesBlock, awardUrl, mutualVote, no, -> @addAward votesBlock, awardUrl, mutualVote, false, ->
$emojiButton.removeClass 'is-loading' $emojiButton.removeClass 'is-loading'
...@@ -282,7 +282,7 @@ class @AwardsHandler ...@@ -282,7 +282,7 @@ class @AwardsHandler
@createEmojiMenu @getAwardMenuUrl(), => @createEmoji_ votesBlock, emoji @createEmojiMenu @getAwardMenuUrl(), => @createEmoji_ votesBlock, emoji
getAwardMenuUrl: -> return gl.awardMenuUrl getAwardMenuUrl: -> return gon.award_menu_url
resolveNameToCssClass: (emoji) -> resolveNameToCssClass: (emoji) ->
...@@ -336,13 +336,15 @@ class @AwardsHandler ...@@ -336,13 +336,15 @@ class @AwardsHandler
if $.cookie 'frequently_used_emojis' if $.cookie 'frequently_used_emojis'
frequentlyUsedEmojis = @getFrequentlyUsedEmojis() frequentlyUsedEmojis = @getFrequentlyUsedEmojis()
ul = $("<ul class='clearfix emoji-menu-list'>") ul = $("<ul class='clearfix emoji-menu-list frequent-emojis'>")
for emoji in frequentlyUsedEmojis for emoji in frequentlyUsedEmojis
$(".emoji-menu-content [data-emoji='#{emoji}']").closest('li').clone().appendTo(ul) $(".emoji-menu-content [data-emoji='#{emoji}']").closest('li').clone().appendTo(ul)
$('input.emoji-search').after(ul).after($('<h5>').text('Frequently used')) $('input.emoji-search').after(ul).after($('<h5>').text('Frequently used'))
@frequentEmojiBlockRendered = true
setupSearch: -> setupSearch: ->
...@@ -365,4 +367,4 @@ class @AwardsHandler ...@@ -365,4 +367,4 @@ class @AwardsHandler
searchEmojis: (term) -> searchEmojis: (term) ->
$(".emoji-menu-content [data-emoji*='#{term}']").closest('li').clone() $(".emoji-menu-list:not(.frequent-emojis) [data-emoji*='#{term}']").closest('li').clone()
class CiBuild class @CiBuild
@interval: null @interval: null
@state: null @state: null
constructor: (build_url, build_status, build_state) -> constructor: (@build_url, @build_status, @state) ->
clearInterval(CiBuild.interval) clearInterval(CiBuild.interval)
@state = build_state # Init breakpoint checker
@bp = Breakpoints.get()
@hideSidebar()
$('.js-build-sidebar').niceScroll()
$(document)
.off 'click', '.js-sidebar-build-toggle'
.on 'click', '.js-sidebar-build-toggle', @toggleSidebar
@initScrollButtonAffix() $(window)
.off 'resize.build'
.on 'resize.build', @hideSidebar
if build_status == "running" || build_status == "pending" if $('#build-trace').length
@getInitialBuildTrace()
@initScrollButtonAffix()
if @build_status is "running" or @build_status is "pending"
# #
# Bind autoscroll button to follow build output # Bind autoscroll button to follow build output
# #
$("#autoscroll-button").bind "click", -> $('#autoscroll-button').on 'click', ->
state = $(this).data("state") state = $(this).data("state")
if "enabled" is state if "enabled" is state
$(this).data "state", "disabled" $(this).data "state", "disabled"
...@@ -27,26 +39,37 @@ class CiBuild ...@@ -27,26 +39,37 @@ class CiBuild
# Only valid for runnig build when output changes during time # Only valid for runnig build when output changes during time
# #
CiBuild.interval = setInterval => CiBuild.interval = setInterval =>
if window.location.href.split("#").first() is build_url if window.location.href.split("#").first() is @build_url
last_state = @state @getBuildTrace()
$.ajax
url: build_url + "/trace.json?state=" + encodeURIComponent(@state)
dataType: "json"
success: (log) =>
return unless last_state is @state
if log.state and log.status is "running"
@state = log.state
if log.append
$('.fa-refresh').before log.html
else
$('#build-trace code').html log.html
$('#build-trace code').append '<i class="fa fa-refresh fa-spin"/>'
@checkAutoscroll()
else if log.status isnt build_status
Turbolinks.visit build_url
, 4000 , 4000
getInitialBuildTrace: ->
$.ajax
url: @build_url
dataType: 'json'
success: (build_data) ->
$('.js-build-output').html build_data.trace_html
if build_data.status is 'success' or build_data.status is 'failed'
$('.js-build-refresh').remove()
getBuildTrace: ->
$.ajax
url: "#{@build_url}/trace.json?state=#{encodeURIComponent(@state)}"
dataType: "json"
success: (log) =>
if log.state
@state = log.state
if log.status is "running"
if log.append
$('.js-build-output').append log.html
else
$('.js-build-output').html log.html
@checkAutoscroll()
else if log.status isnt @build_status
Turbolinks.visit @build_url
checkAutoscroll: -> checkAutoscroll: ->
$("html,body").scrollTop $("#build-trace").height() if "enabled" is $("#autoscroll-button").data("state") $("html,body").scrollTop $("#build-trace").height() if "enabled" is $("#autoscroll-button").data("state")
...@@ -61,4 +84,22 @@ class CiBuild ...@@ -61,4 +84,22 @@ class CiBuild
$body.outerHeight() - ($buildTrace.outerHeight() + $buildTrace.offset().top) $body.outerHeight() - ($buildTrace.outerHeight() + $buildTrace.offset().top)
) )
@CiBuild = CiBuild shouldHideSidebar: ->
bootstrapBreakpoint = @bp.getBreakpointSize()
bootstrapBreakpoint is 'xs' or bootstrapBreakpoint is 'sm'
toggleSidebar: =>
if @shouldHideSidebar()
$('.js-build-sidebar')
.toggleClass 'right-sidebar-expanded right-sidebar-collapsed'
hideSidebar: =>
if @shouldHideSidebar()
$('.js-build-sidebar')
.removeClass 'right-sidebar-expanded'
.addClass 'right-sidebar-collapsed'
else
$('.js-build-sidebar')
.removeClass 'right-sidebar-collapsed'
.addClass 'right-sidebar-expanded'
...@@ -23,7 +23,6 @@ class Dispatcher ...@@ -23,7 +23,6 @@ class Dispatcher
new Issue() new Issue()
shortcut_handler = new ShortcutsIssuable() shortcut_handler = new ShortcutsIssuable()
new ZenMode() new ZenMode()
gl.awardsHandler = new AwardsHandler()
when 'projects:milestones:show', 'groups:milestones:show', 'dashboard:milestones:show' when 'projects:milestones:show', 'groups:milestones:show', 'dashboard:milestones:show'
new Milestone() new Milestone()
when 'dashboard:todos:index' when 'dashboard:todos:index'
...@@ -54,7 +53,6 @@ class Dispatcher ...@@ -54,7 +53,6 @@ class Dispatcher
new Diff() new Diff()
shortcut_handler = new ShortcutsIssuable(true) shortcut_handler = new ShortcutsIssuable(true)
new ZenMode() new ZenMode()
gl.awardsHandler = new AwardsHandler()
when "projects:merge_requests:diffs" when "projects:merge_requests:diffs"
new Diff() new Diff()
new ZenMode() new ZenMode()
......
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
window.GitLab ?= {} window.GitLab ?= {}
GitLab.GfmAutoComplete = GitLab.GfmAutoComplete =
dataLoading: false dataLoading: false
dataLoaded: false
dataSource: '' dataSource: ''
...@@ -22,6 +23,24 @@ GitLab.GfmAutoComplete = ...@@ -22,6 +23,24 @@ GitLab.GfmAutoComplete =
Milestones: Milestones:
template: '<li>${title}</li>' template: '<li>${title}</li>'
Loading:
template: '<li><i class="fa fa-refresh fa-spin"></i> Loading...</li>'
DefaultOptions:
sorter: (query, items, searchKey) ->
return items if items[0].name? and items[0].name is 'loading'
$.fn.atwho.default.callbacks.sorter(query, items, searchKey)
filter: (query, data, searchKey) ->
return data if data[0] is 'loading'
$.fn.atwho.default.callbacks.filter(query, data, searchKey)
beforeInsert: (value) ->
if not GitLab.GfmAutoComplete.dataLoaded
@at
else
value
# Add GFM auto-completion to all input fields, that accept GFM input. # Add GFM auto-completion to all input fields, that accept GFM input.
setup: (wrap) -> setup: (wrap) ->
@input = $('.js-gfm-input') @input = $('.js-gfm-input')
...@@ -53,18 +72,37 @@ GitLab.GfmAutoComplete = ...@@ -53,18 +72,37 @@ GitLab.GfmAutoComplete =
# Emoji # Emoji
@input.atwho @input.atwho
at: ':' at: ':'
displayTpl: @Emoji.template displayTpl: (value) =>
if value.path?
@Emoji.template
else
@Loading.template
insertTpl: ':${name}:' insertTpl: ':${name}:'
data: ['loading']
callbacks:
sorter: @DefaultOptions.sorter
filter: @DefaultOptions.filter
beforeInsert: @DefaultOptions.beforeInsert
# Team Members # Team Members
@input.atwho @input.atwho
at: '@' at: '@'
displayTpl: @Members.template displayTpl: (value) =>
if value.username?
@Members.template
else
@Loading.template
insertTpl: '${atwho-at}${username}' insertTpl: '${atwho-at}${username}'
searchKey: 'search' searchKey: 'search'
data: ['loading']
callbacks: callbacks:
sorter: @DefaultOptions.sorter
filter: @DefaultOptions.filter
beforeInsert: @DefaultOptions.beforeInsert
beforeSave: (members) -> beforeSave: (members) ->
$.map members, (m) -> $.map members, (m) ->
return m if not m.username?
title = m.name title = m.name
title += " (#{m.count})" if m.count title += " (#{m.count})" if m.count
...@@ -76,11 +114,21 @@ GitLab.GfmAutoComplete = ...@@ -76,11 +114,21 @@ GitLab.GfmAutoComplete =
at: '#' at: '#'
alias: 'issues' alias: 'issues'
searchKey: 'search' searchKey: 'search'
displayTpl: @Issues.template displayTpl: (value) =>
if value.title?
@Issues.template
else
@Loading.template
data: ['loading']
insertTpl: '${atwho-at}${id}' insertTpl: '${atwho-at}${id}'
callbacks: callbacks:
sorter: @DefaultOptions.sorter
filter: @DefaultOptions.filter
beforeInsert: @DefaultOptions.beforeInsert
beforeSave: (issues) -> beforeSave: (issues) ->
$.map issues, (i) -> $.map issues, (i) ->
return i if not i.title?
id: i.iid id: i.iid
title: sanitize(i.title) title: sanitize(i.title)
search: "#{i.iid} #{i.title}" search: "#{i.iid} #{i.title}"
...@@ -89,11 +137,18 @@ GitLab.GfmAutoComplete = ...@@ -89,11 +137,18 @@ GitLab.GfmAutoComplete =
at: '%' at: '%'
alias: 'milestones' alias: 'milestones'
searchKey: 'search' searchKey: 'search'
displayTpl: @Milestones.template displayTpl: (value) =>
if value.title?
@Milestones.template
else
@Loading.template
insertTpl: '${atwho-at}"${title}"' insertTpl: '${atwho-at}"${title}"'
data: ['loading']
callbacks: callbacks:
beforeSave: (milestones) -> beforeSave: (milestones) ->
$.map milestones, (m) -> $.map milestones, (m) ->
return m if not m.title?
id: m.iid id: m.iid
title: sanitize(m.title) title: sanitize(m.title)
search: "#{m.title}" search: "#{m.title}"
...@@ -102,11 +157,21 @@ GitLab.GfmAutoComplete = ...@@ -102,11 +157,21 @@ GitLab.GfmAutoComplete =
at: '!' at: '!'
alias: 'mergerequests' alias: 'mergerequests'
searchKey: 'search' searchKey: 'search'
displayTpl: @Issues.template displayTpl: (value) =>
if value.title?
@Issues.template
else
@Loading.template
data: ['loading']
insertTpl: '${atwho-at}${id}' insertTpl: '${atwho-at}${id}'
callbacks: callbacks:
sorter: @DefaultOptions.sorter
filter: @DefaultOptions.filter
beforeInsert: @DefaultOptions.beforeInsert
beforeSave: (merges) -> beforeSave: (merges) ->
$.map merges, (m) -> $.map merges, (m) ->
return m if not m.title?
id: m.iid id: m.iid
title: sanitize(m.title) title: sanitize(m.title)
search: "#{m.iid} #{m.title}" search: "#{m.iid} #{m.title}"
...@@ -118,6 +183,8 @@ GitLab.GfmAutoComplete = ...@@ -118,6 +183,8 @@ GitLab.GfmAutoComplete =
$.getJSON(dataSource) $.getJSON(dataSource)
loadData: (data) -> loadData: (data) ->
@dataLoaded = true
# load members # load members
@input.atwho 'load', '@', data.members @input.atwho 'load', '@', data.members
# load issues # load issues
...@@ -128,3 +195,7 @@ GitLab.GfmAutoComplete = ...@@ -128,3 +195,7 @@ GitLab.GfmAutoComplete =
@input.atwho 'load', 'mergerequests', data.mergerequests @input.atwho 'load', 'mergerequests', data.mergerequests
# load emojis # load emojis
@input.atwho 'load', ':', data.emojis @input.atwho 'load', ':', data.emojis
# This trigger at.js again
# otherwise we would be stuck with loading until the user types
$(':focus').trigger('keyup')
...@@ -211,6 +211,7 @@ class GitLabDropdown ...@@ -211,6 +211,7 @@ class GitLabDropdown
@dropdown.on "shown.bs.dropdown", @opened @dropdown.on "shown.bs.dropdown", @opened
@dropdown.on "hidden.bs.dropdown", @hidden @dropdown.on "hidden.bs.dropdown", @hidden
$(@el).on "update.label", @updateLabel
@dropdown.on "click", ".dropdown-menu, .dropdown-menu-close", @shouldPropagate @dropdown.on "click", ".dropdown-menu, .dropdown-menu-close", @shouldPropagate
@dropdown.on 'keyup', (e) => @dropdown.on 'keyup', (e) =>
if e.which is 27 # Escape key if e.which is 27 # Escape key
...@@ -453,7 +454,7 @@ class GitLabDropdown ...@@ -453,7 +454,7 @@ class GitLabDropdown
# Toggle the dropdown label # Toggle the dropdown label
if @options.toggleLabel if @options.toggleLabel
$(@el).find(".dropdown-toggle-text").text @options.toggleLabel @updateLabel()
else else
selectedObject selectedObject
else if el.hasClass(INDETERMINATE_CLASS) else if el.hasClass(INDETERMINATE_CLASS)
...@@ -480,7 +481,7 @@ class GitLabDropdown ...@@ -480,7 +481,7 @@ class GitLabDropdown
# Toggle the dropdown label # Toggle the dropdown label
if @options.toggleLabel if @options.toggleLabel
$(@el).find(".dropdown-toggle-text").text @options.toggleLabel(selectedObject, el) @updateLabel(selectedObject, el)
if value? if value?
if !field.length and fieldName if !field.length and fieldName
@addInput(fieldName, value) @addInput(fieldName, value)
...@@ -579,6 +580,9 @@ class GitLabDropdown ...@@ -579,6 +580,9 @@ class GitLabDropdown
# Scroll the dropdown content up # Scroll the dropdown content up
$dropdownContent.scrollTop(listItemTop - dropdownContentTop) $dropdownContent.scrollTop(listItemTop - dropdownContentTop)
updateLabel: (selected = null, el = null) =>
$(@el).find(".dropdown-toggle-text").text @options.toggleLabel(selected, el)
$.fn.glDropdown = (opts) -> $.fn.glDropdown = (opts) ->
return @.each -> return @.each ->
if (!$.data @, 'glDropdown') if (!$.data @, 'glDropdown')
......
...@@ -4,4 +4,5 @@ ...@@ -4,4 +4,5 @@
# It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the # It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
# the compiled file. # the compiled file.
# #
#= require Chart
#= require_tree . #= require_tree .
...@@ -6,12 +6,18 @@ issuable_created = false ...@@ -6,12 +6,18 @@ issuable_created = false
Issuable.initTemplates() Issuable.initTemplates()
Issuable.initSearch() Issuable.initSearch()
Issuable.initChecks() Issuable.initChecks()
Issuable.initLabelFilterRemove()
initTemplates: -> initTemplates: ->
Issuable.labelRow = _.template( Issuable.labelRow = _.template(
'<% _.each(labels, function(label){ %> '<% _.each(labels, function(label){ %>
<span class="label-row"> <span class="label-row btn-group" role="group" aria-label="<%= _.escape(label.title) %>" style="color: <%= label.text_color %>;">
<a href="#"><span class="label color-label has-tooltip" style="background-color: <%= label.color %>; color: <%= label.text_color %>" title="<%= _.escape(label.description) %>" data-container="body"><%= _.escape(label.title) %></span></a> <a href="#" class="btn btn-transparent has-tooltip" style="background-color: <%= label.color %>;" title="<%= _.escape(label.description) %>" data-container="body">
<%= _.escape(label.title) %>
</a>
<button type="button" class="btn btn-transparent label-remove js-label-filter-remove" style="background-color: <%= label.color %>;" data-label="<%= _.escape(label.title) %>">
<i class="fa fa-times"></i>
</button>
</span> </span>
<% }); %>' <% }); %>'
) )
...@@ -35,6 +41,21 @@ issuable_created = false ...@@ -35,6 +41,21 @@ issuable_created = false
Issuable.filterResults $form Issuable.filterResults $form
, 500) , 500)
initLabelFilterRemove: ->
$(document)
.off 'click', '.js-label-filter-remove'
.on 'click', '.js-label-filter-remove', (e) ->
$button = $(@)
# Remove the label input box
$('input[name="label_name[]"]')
.filter -> @value is $button.data('label')
.remove()
# Submit the form to get new data
Issuable.filterResults $('.filter-form')
$('.js-label-select').trigger('update.label')
toggleLabelFilters: -> toggleLabelFilters: ->
$filteredLabels = $('.filtered-labels') $filteredLabels = $('.filtered-labels')
if $filteredLabels.find('.label-row').length > 0 if $filteredLabels.find('.label-row').length > 0
......
...@@ -95,8 +95,11 @@ class @LabelsSelect ...@@ -95,8 +95,11 @@ class @LabelsSelect
$newLabelCreateButton.enable() $newLabelCreateButton.enable()
if label.message? if label.message?
errors = _.map label.message, (value, key) ->
"#{key} #{value[0]}"
$newLabelError $newLabelError
.text label.message .html errors.join("<br/>")
.show() .show()
else else
$('.dropdown-menu-back', $dropdown.parent()).trigger 'click' $('.dropdown-menu-back', $dropdown.parent()).trigger 'click'
...@@ -254,7 +257,7 @@ class @LabelsSelect ...@@ -254,7 +257,7 @@ class @LabelsSelect
search: search:
fields: ['title'] fields: ['title']
selectable: true selectable: true
filterable: true
toggleLabel: (selected, el) -> toggleLabel: (selected, el) ->
selected_labels = $('.js-label-select').siblings('.dropdown-menu-labels').find('.is-active') selected_labels = $('.js-label-select').siblings('.dropdown-menu-labels').find('.is-active')
......
((w) ->
jQuery.timefor = (time, suffix, expiredLabel) ->
return '' unless time
suffix or= 'remaining'
expiredLabel or= 'Past due'
jQuery.timeago.settings.allowFuture = yes
{ suffixFromNow } = jQuery.timeago.settings.strings
jQuery.timeago.settings.strings.suffixFromNow = suffix
timefor = $.timeago time
if timefor.indexOf('ago') > -1
timefor = expiredLabel
jQuery.timeago.settings.strings.suffixFromNow = suffixFromNow
return timefor
) window
...@@ -12,6 +12,13 @@ ...@@ -12,6 +12,13 @@
$el.attr('title', gl.utils.formatDate($el.attr('datetime'))) $el.attr('title', gl.utils.formatDate($el.attr('datetime')))
) )
$timeagoEls.timeago() if setTimeago if setTimeago
$timeagoEls.timeago()
$timeagoEls.tooltip('destroy')
# Recreate with custom template
$timeagoEls.tooltip(
template: '<div class="tooltip local-timeago" role="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>'
)
) window ) window
...@@ -24,11 +24,21 @@ class @MilestoneSelect ...@@ -24,11 +24,21 @@ class @MilestoneSelect
if issueUpdateURL if issueUpdateURL
milestoneLinkTemplate = _.template( milestoneLinkTemplate = _.template(
'<a href="/<%= namespace %>/<%= path %>/milestones/<%= iid %>"><%= _.escape(title) %></a>' '<a href="/<%= namespace %>/<%= path %>/milestones/<%= iid %>">
<span class="has-tooltip" data-container="body" title="<%= remaining %>">
<%= _.escape(title) %>
</span>
</a>'
) )
milestoneLinkNoneTemplate = '<div class="light">None</div>' milestoneLinkNoneTemplate = '<div class="light">None</div>'
collapsedSidebarLabelTemplate = _.template(
'<span class="has-tooltip" data-container="body" title="<%= remaining %>" data-placement="left">
<%= _.escape(title) %>
</span>'
)
$dropdown.glDropdown( $dropdown.glDropdown(
data: (term, callback) -> data: (term, callback) ->
$.ajax( $.ajax(
...@@ -122,8 +132,9 @@ class @MilestoneSelect ...@@ -122,8 +132,9 @@ class @MilestoneSelect
if data.milestone? if data.milestone?
data.milestone.namespace = _this.currentProject.namespace data.milestone.namespace = _this.currentProject.namespace
data.milestone.path = _this.currentProject.path data.milestone.path = _this.currentProject.path
data.milestone.remaining = $.timefor data.milestone.due_date
$value.html(milestoneLinkTemplate(data.milestone)) $value.html(milestoneLinkTemplate(data.milestone))
$sidebarCollapsedValue.find('span').text(data.milestone.title) $sidebarCollapsedValue.find('span').html(collapsedSidebarLabelTemplate(data.milestone))
else else
$value.html(milestoneLinkNoneTemplate) $value.html(milestoneLinkNoneTemplate)
$sidebarCollapsedValue.find('span').text('No') $sidebarCollapsedValue.find('span').text('No')
......
...@@ -354,8 +354,7 @@ class @Notes ...@@ -354,8 +354,7 @@ class @Notes
Called in response to clicking the edit note link Called in response to clicking the edit note link
Replaces the note text with the note edit form Replaces the note text with the note edit form
Adds a hidden div with the original content of the note to fill the edit note form with Adds a data attribute to the form with the original content of the note for cancellations
if the user cancels
### ###
showEditForm: (e, scrollTo, myLastNote) -> showEditForm: (e, scrollTo, myLastNote) ->
e.preventDefault() e.preventDefault()
...@@ -371,6 +370,8 @@ class @Notes ...@@ -371,6 +370,8 @@ class @Notes
done = ($noteText) -> done = ($noteText) ->
# Neat little trick to put the cursor at the end # Neat little trick to put the cursor at the end
noteTextVal = $noteText.val() noteTextVal = $noteText.val()
# Store the original note text in a data attribute to retrieve if a user cancels edit.
form.find('form.edit-note').data 'original-note', noteTextVal
$noteText.val('').val(noteTextVal); $noteText.val('').val(noteTextVal);
new GLForm form new GLForm form
...@@ -393,14 +394,16 @@ class @Notes ...@@ -393,14 +394,16 @@ class @Notes
### ###
Called in response to clicking the edit note link Called in response to clicking the edit note link
Hides edit form Hides edit form and restores the original note text to the editor textarea.
### ###
cancelEdit: (e) -> cancelEdit: (e) ->
e.preventDefault() e.preventDefault()
note = $(this).closest(".note") note = $(this).closest(".note")
form = note.find(".current-note-edit-form")
note.removeClass "is-editting" note.removeClass "is-editting"
note.find(".current-note-edit-form") form.removeClass("current-note-edit-form")
.removeClass("current-note-edit-form") # Replace markdown textarea text with original note text.
form.find(".js-note-text").val(form.find('form.edit-note').data('original-note'))
### ###
Called in response to deleting a note of any kind. Called in response to deleting a note of any kind.
......
@Pager = @Pager =
init: (@limit = 0, preload, @disable = false) -> init: (@limit = 0, preload, @disable = false, @callback = $.noop) ->
@loading = $('.loading').first() @loading = $('.loading').first()
if preload if preload
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
@loading.hide() @loading.hide()
success: (data) -> success: (data) ->
Pager.append(data.count, data.html) Pager.append(data.count, data.html)
Pager.callback()
dataType: "json" dataType: "json"
append: (count, html) -> append: (count, html) ->
......
...@@ -7,12 +7,17 @@ class @ProjectNew ...@@ -7,12 +7,17 @@ class @ProjectNew
@toggleSettingsOnclick() @toggleSettingsOnclick()
toggleSettings: -> toggleSettings: =>
checked = $("#project_builds_enabled").prop("checked") @_showOrHide('#project_builds_enabled', '.builds-feature')
if checked @_showOrHide('#project_merge_requests_enabled', '.merge-requests-feature')
$('.builds-feature').show()
else
$('.builds-feature').hide()
toggleSettingsOnclick: -> toggleSettingsOnclick: ->
$("#project_builds_enabled").on 'click', @toggleSettings $('#project_builds_enabled, #project_merge_requests_enabled').on 'click', @toggleSettings
_showOrHide: (checkElement, container) ->
$container = $(container)
if $(checkElement).prop('checked')
$container.show()
else
$container.hide()
...@@ -19,3 +19,8 @@ class @Subscription ...@@ -19,3 +19,8 @@ class @Subscription
action = if status == 'subscribed' then 'Unsubscribe' else 'Subscribe' action = if status == 'subscribed' then 'Unsubscribe' else 'Subscribe'
btn.find('span').text(action) btn.find('span').text(action)
@subscription_status.find('>div').toggleClass('hidden') @subscription_status.find('>div').toggleClass('hidden')
if btn.attr('data-original-title')
btn.tooltip('hide')
.attr('data-original-title', action)
.tooltip('fixTitle')
...@@ -122,6 +122,9 @@ class @UserTabs ...@@ -122,6 +122,9 @@ class @UserTabs
@parentEl.find(tabSelector).html(data.html) @parentEl.find(tabSelector).html(data.html)
@loaded[action] = true @loaded[action] = true
# Fix tooltips
gl.utils.localTimeAgo($('.js-timeago', tabSelector))
loadActivities: (source) -> loadActivities: (source) ->
return if @loaded['activity'] is true return if @loaded['activity'] is true
......
...@@ -95,7 +95,7 @@ class @UsersSelect ...@@ -95,7 +95,7 @@ class @UsersSelect
data: (term, callback) => data: (term, callback) =>
isAuthorFilter = $('.js-author-search') isAuthorFilter = $('.js-author-search')
@users term, term is '' and isAuthorFilter, (users) => @users term, (users) =>
if term.length is 0 if term.length is 0
showDivider = 0 showDivider = 0
...@@ -221,7 +221,7 @@ class @UsersSelect ...@@ -221,7 +221,7 @@ class @UsersSelect
multiple: $(select).hasClass('multiselect') multiple: $(select).hasClass('multiselect')
minimumInputLength: 0 minimumInputLength: 0
query: (query) => query: (query) =>
@users query.term, @projectId?, (users) => @users query.term, (users) =>
data = { results: users } data = { results: users }
if query.term.length == 0 if query.term.length == 0
...@@ -304,7 +304,7 @@ class @UsersSelect ...@@ -304,7 +304,7 @@ class @UsersSelect
# Return users list. Filtered by query # Return users list. Filtered by query
# Only active users retrieved # Only active users retrieved
users: (query, fromProject, callback) => users: (query, callback) =>
url = @buildUrl(@usersPath) url = @buildUrl(@usersPath)
$.ajax( $.ajax(
...@@ -313,7 +313,7 @@ class @UsersSelect ...@@ -313,7 +313,7 @@ class @UsersSelect
search: query search: query
per_page: 20 per_page: 20
active: true active: true
project_id: @projectId if fromProject project_id: @projectId
group_id: @groupId group_id: @groupId
current_user: @showCurrentUser current_user: @showCurrentUser
author_id: @authorId author_id: @authorId
......
...@@ -79,6 +79,23 @@ ...@@ -79,6 +79,23 @@
@include btn-color($white-light, $border-color, $white-normal, $border-white-normal, $white-dark, $border-white-dark, $btn-white-active); @include btn-color($white-light, $border-color, $white-normal, $border-white-normal, $white-dark, $border-white-dark, $btn-white-active);
} }
@mixin btn-with-margin {
margin-left: $btn-side-margin;
float: left;
&.inline {
float: none;
}
&.btn-sm {
margin-left: $btn-sm-side-margin;
}
&.btn-xs {
margin-left: $btn-xs-side-margin;
}
}
.btn { .btn {
@include btn-default; @include btn-default;
@include btn-white; @include btn-white;
...@@ -142,15 +159,9 @@ ...@@ -142,15 +159,9 @@
} }
&.btn-grouped { &.btn-grouped {
margin-right: 7px; @include btn-with-margin;
float: left;
&:last-child {
margin-right: 0;
}
&.btn-xs {
margin-right: 3px;
}
} }
&.disabled { &.disabled {
pointer-events: auto !important; pointer-events: auto !important;
} }
...@@ -192,11 +203,7 @@ ...@@ -192,11 +203,7 @@
.btn-group { .btn-group {
&.btn-grouped { &.btn-grouped {
margin-right: 7px; @include btn-with-margin;
float: left;
&:last-child {
margin-right: 0;
}
} }
} }
......
...@@ -124,6 +124,7 @@ ...@@ -124,6 +124,7 @@
position: relative; position: relative;
padding: 5px 10px; padding: 5px 10px;
color: $dropdown-link-color; color: $dropdown-link-color;
line-height: initial;
text-overflow: ellipsis; text-overflow: ellipsis;
border-radius: 2px; border-radius: 2px;
white-space: nowrap; white-space: nowrap;
......
...@@ -76,6 +76,7 @@ label { ...@@ -76,6 +76,7 @@ label {
.form-control { .form-control {
@include box-shadow(none); @include box-shadow(none);
border-radius: 3px; border-radius: 3px;
padding: $gl-vert-padding $gl-input-padding;
} }
.select-wrapper { .select-wrapper {
......
...@@ -22,17 +22,17 @@ ...@@ -22,17 +22,17 @@
&:hover { &:hover {
background-color: $color-dark; background-color: $color-dark;
a { a {
color: #fff; color: $white-light;
h3 { h3 {
color: #fff; color: $white-light;
} }
} }
} }
} }
.collapse-nav a { .collapse-nav a {
color: #fff; color: $white-light;
background: $color; background: $color;
} }
...@@ -45,7 +45,7 @@ ...@@ -45,7 +45,7 @@
&:hover { &:hover {
background-color: $color-dark; background-color: $color-dark;
color: #fff; color: $white-light;
text-decoration: none; text-decoration: none;
} }
} }
...@@ -63,10 +63,20 @@ ...@@ -63,10 +63,20 @@
color: $color-light; color: $color-light;
} }
path,
polygon {
fill: $color-light;
}
.count { .count {
color: $color-light; color: $color-light;
background: $color-dark; background: $color-dark;
} }
svg {
position: relative;
top: 3px;
}
} }
&.separate-item { &.separate-item {
...@@ -74,7 +84,7 @@ ...@@ -74,7 +84,7 @@
} }
&.active a { &.active a {
color: #fff; color: $white-light;
background: $color-dark; background: $color-dark;
&.no-highlight { &.no-highlight {
...@@ -82,7 +92,12 @@ ...@@ -82,7 +92,12 @@
} }
i { i {
color: #fff color: $white-light
}
path,
polygon {
fill: $white-light;
} }
} }
} }
......
...@@ -185,7 +185,7 @@ header { ...@@ -185,7 +185,7 @@ header {
margin-left: 0; margin-left: 0;
.header-content { .header-content {
padding-left: $sidebar_width; margin-left: $sidebar_width;
transition-duration: .3s; transition-duration: .3s;
} }
} }
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
font-family: $regular_font; font-family: $regular_font;
font-size: $font-size-base; font-size: $font-size-base;
&.ui-datepicker,
&.ui-datepicker-inline { &.ui-datepicker-inline {
border: 1px solid #ddd; border: 1px solid #ddd;
padding: 10px; padding: 10px;
...@@ -10,6 +11,25 @@ ...@@ -10,6 +11,25 @@
.ui-datepicker-header { .ui-datepicker-header {
background: #fff; background: #fff;
border-color: #ddd; border-color: #ddd;
.ui-datepicker-prev,
.ui-datepicker-next {
top: 4px;
}
.ui-datepicker-prev {
left: 2px;
}
.ui-datepicker-next {
right: 2px;
}
.ui-state-hover {
background: transparent;
border: 0;
cursor: pointer;
}
} }
.ui-datepicker-calendar td a { .ui-datepicker-calendar td a {
...@@ -36,21 +56,18 @@ ...@@ -36,21 +56,18 @@
} }
.ui-state-highlight { .ui-state-highlight {
border: 1px solid #eee; border: 0;
background: #eee; background: transparent;
} }
.ui-state-active { .ui-datepicker-calendar {
border: 1px solid $gl-primary; .ui-state-active,
background: $gl-primary; .ui-state-hover,
color: #fff; .ui-state-focus {
} border: 1px solid $gl-primary;
background: $gl-primary;
.ui-state-hover, color: #fff;
.ui-state-focus { }
border: 1px solid $row-hover;
background: $row-hover;
color: #333;
} }
} }
......
...@@ -137,8 +137,16 @@ ul.content-list { ...@@ -137,8 +137,16 @@ ul.content-list {
padding-top: 1px; padding-top: 1px;
float: right; float: right;
.btn { > .btn,
padding: 10px 14px; > .btn-group {
margin-right: $gl-padding-top;
display: inline-block;
margin-top: 4px;
margin-bottom: 4px;
&:last-child {
margin-right: 0;
}
} }
} }
......
...@@ -171,6 +171,7 @@ ...@@ -171,6 +171,7 @@
> form { > form {
display: inline-block; display: inline-block;
margin-top: -1px; margin-top: -1px;
margin-bottom: 12px;
} }
.icon-label { .icon-label {
...@@ -207,7 +208,7 @@ ...@@ -207,7 +208,7 @@
@media (max-width: $screen-xs-max) { @media (max-width: $screen-xs-max) {
padding-bottom: 0; padding-bottom: 0;
width: 100%;
.btn, form, .dropdown, .dropdown-menu-toggle, .form-control { .btn, form, .dropdown, .dropdown-menu-toggle, .form-control {
margin: 0 0 10px; margin: 0 0 10px;
display: block; display: block;
...@@ -238,16 +239,6 @@ ...@@ -238,16 +239,6 @@
margin: 0; margin: 0;
} }
} }
/* Small devices (tablets, 768px and lower) */
@media (max-width: $screen-sm-max) {
width: 100%;
text-align: left;
input {
width: 300px;
}
}
} }
} }
...@@ -304,6 +295,19 @@ ...@@ -304,6 +295,19 @@
border-bottom: none; border-bottom: none;
height: 51px; height: 51px;
svg {
position: relative;
top: 2px;
margin-right: 2px;
height: 15px;
width: auto;
path,
polygon {
fill: $layout-link-gray;
}
}
.fade-right { .fade-right {
@include fade(left, rgba(250, 250, 250, 0.4), $background-color); @include fade(left, rgba(250, 250, 250, 0.4), $background-color);
right: 0; right: 0;
...@@ -325,9 +329,17 @@ ...@@ -325,9 +329,17 @@
} }
&.active { &.active {
a, i { a, i {
color: $black; color: $black;
} }
svg {
path,
polygon {
fill: $black;
}
}
} }
.badge { .badge {
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
background: #fff; background: #fff;
border-color: $input-border; border-color: $input-border;
height: 35px; height: 35px;
padding: $gl-vert-padding $gl-btn-padding; padding: $gl-vert-padding $gl-input-padding;
font-size: $gl-font-size; font-size: $gl-font-size;
line-height: 1.42857143; line-height: 1.42857143;
border-radius: $border-radius-base; border-radius: $border-radius-base;
......
...@@ -38,6 +38,11 @@ ...@@ -38,6 +38,11 @@
.header-logo { .header-logo {
height: $header-height; height: $header-height;
padding: 8px 26px; padding: 8px 26px;
width: $sidebar_width;
position: fixed;
z-index: 999;
overflow: hidden;
transition-duration: .3s;
&:hover { &:hover {
background-color: #eee; background-color: #eee;
...@@ -73,7 +78,8 @@ ...@@ -73,7 +78,8 @@
.nav-sidebar { .nav-sidebar {
margin: 22px 0; margin-top: 22 + $header-height;
margin-bottom: 116px;
transition-duration: .3s; transition-duration: .3s;
list-style: none; list-style: none;
overflow: hidden; overflow: hidden;
...@@ -109,8 +115,7 @@ ...@@ -109,8 +115,7 @@
} }
i { i {
width: 16px; font-size: 16px;
color: $gray-light;
} }
.nav-link-text { .nav-link-text {
...@@ -167,6 +172,7 @@ ...@@ -167,6 +172,7 @@
.header-logo { .header-logo {
width: 0; width: 0;
padding: 8px 0;
a { a {
padding-left: ($sidebar_collapsed_width - 36) / 2; padding-left: ($sidebar_collapsed_width - 36) / 2;
...@@ -267,7 +273,9 @@ ...@@ -267,7 +273,9 @@
padding-right: 0; padding-right: 0;
@media (min-width: $screen-sm-min) and (max-width: $screen-sm-max) { @media (min-width: $screen-sm-min) and (max-width: $screen-sm-max) {
padding-right: $sidebar_collapsed_width; &:not(.build-sidebar) {
padding-right: $sidebar_collapsed_width;
}
} }
@media (min-width: $screen-md-min) { @media (min-width: $screen-md-min) {
......
...@@ -192,3 +192,8 @@ ...@@ -192,3 +192,8 @@
.text-info:hover { .text-info:hover {
color: $brand-info; color: $brand-info;
} }
// Prevent datetimes on tooltips to break into two lines
.local-timeago {
white-space: nowrap;
}
...@@ -57,6 +57,7 @@ $code_line_height: 1.5; ...@@ -57,6 +57,7 @@ $code_line_height: 1.5;
*/ */
$gl-padding: 16px; $gl-padding: 16px;
$gl-btn-padding: 10px; $gl-btn-padding: 10px;
$gl-input-padding: 10px;
$gl-vert-padding: 6px; $gl-vert-padding: 6px;
$gl-padding-top: 10px; $gl-padding-top: 10px;
...@@ -79,6 +80,9 @@ $provider-btn-not-active-color: #4688f1; ...@@ -79,6 +80,9 @@ $provider-btn-not-active-color: #4688f1;
$link-underline-blue: #4a8bee; $link-underline-blue: #4a8bee;
$layout-link-gray: #7e7c7c; $layout-link-gray: #7e7c7c;
$todo-alert-blue: #428bca; $todo-alert-blue: #428bca;
$btn-side-margin: 10px;
$btn-sm-side-margin: 7px;
$btn-xs-side-margin: 5px;
/* /*
* Color schema * Color schema
...@@ -121,7 +125,7 @@ $border-white-normal: #d6dae2; ...@@ -121,7 +125,7 @@ $border-white-normal: #d6dae2;
$border-white-dark: #c6cacf; $border-white-dark: #c6cacf;
$border-gray-light: #dcdcdc; $border-gray-light: #dcdcdc;
$border-gray-normal: rgba(0, 0, 0, 0.10); $border-gray-normal: #d7d7d7;
$border-gray-dark: #c6cacf; $border-gray-dark: #c6cacf;
$border-green-light: #2faa60; $border-green-light: #2faa60;
...@@ -256,3 +260,6 @@ $calendar-header-color: #b8b8b8; ...@@ -256,3 +260,6 @@ $calendar-header-color: #b8b8b8;
$calendar-hover-bg: #ecf3fe; $calendar-hover-bg: #ecf3fe;
$calendar-border-color: rgba(#000, .1); $calendar-border-color: rgba(#000, .1);
$calendar-unselectable-bg: #faf9f9; $calendar-unselectable-bg: #faf9f9;
$ci-output-bg: #1d1f21;
$ci-text-color: #c5c8c6;
...@@ -101,13 +101,21 @@ ...@@ -101,13 +101,21 @@
line-height: 20px; line-height: 20px;
outline: 0; outline: 0;
&:hover,
&.active, &.active,
&:active { &:active {
background-color: $white-dark; background-color: $row-hover;
border-color: $row-hover-border;
box-shadow: none; box-shadow: none;
outline: 0; outline: 0;
} }
&.btn {
&:focus {
outline: 0;
}
}
&.is-loading { &.is-loading {
.award-control-icon-normal, .award-control-icon-normal,
.emoji-icon { .emoji-icon {
......
...@@ -53,37 +53,92 @@ ...@@ -53,37 +53,92 @@
left: 70px; left: 70px;
} }
} }
}
.build-widget { .build-header {
padding: 10px; position: relative;
background: $background-color; padding-right: 40px;
margin-bottom: 20px;
border-radius: 4px;
.title { @media (min-width: $screen-sm-min) {
margin-top: 0; padding-right: 0;
color: #666;
line-height: 1.5;
}
.attr-name {
color: #777;
}
} }
.alert-disabled { a {
background: $background-color; color: $gl-gray;
a { &:hover {
color: #3084bb !important; color: $gl-link-color;
text-decoration: none;
} }
} }
code {
color: $code-color;
}
.avatar {
float: none;
margin-right: 2px;
margin-left: 2px;
}
} }
table.builds { table.builds {
.build-link { .build-link {
a { a {
color: $gl-dark-link-color; color: $gl-dark-link-color;
} }
} }
} }
.build-trace {
background: $ci-output-bg;
color: $ci-text-color;
white-space: pre;
overflow-x: auto;
font-size: 12px;
.fa-refresh {
font-size: 24px;
}
.bash {
display: block;
}
}
.right-sidebar.build-sidebar {
padding-top: $gl-padding;
padding-bottom: $gl-padding;
&.right-sidebar-collapsed {
display: none;
}
.block {
width: 100%;
}
.build-sidebar-header {
padding-top: 0;
.gutter-toggle {
margin-top: 0;
}
}
}
.build-detail-row {
margin-bottom: 5px;
}
.build-light-text {
color: $gl-placeholder-color;
}
.build-gutter-toggle {
position: absolute;
top: 50%;
right: 0;
margin-top: -17px;
}
...@@ -2,13 +2,21 @@ ...@@ -2,13 +2,21 @@
margin-bottom: 20px; margin-bottom: 20px;
border-bottom: 1px solid #eee; border-bottom: 1px solid #eee;
> h1 { > h1, h2, h3, h4, h5, h6 {
font-weight: 400; font-weight: 400;
} }
.lead { .lead {
margin-bottom: 20px; margin-bottom: 20px;
} }
ul, ol {
padding-left: 0;
}
li {
list-style-type: none;
}
} }
.confirmation-content { .confirmation-content {
......
...@@ -29,7 +29,7 @@ ...@@ -29,7 +29,7 @@
} }
} }
.issuable-sidebar { .right-sidebar {
a { a {
color: inherit; color: inherit;
} }
...@@ -74,6 +74,10 @@ ...@@ -74,6 +74,10 @@
} }
} }
.block-first {
padding-top: 0;
}
.title { .title {
color: $gl-text-color; color: $gl-text-color;
margin-bottom: 10px; margin-bottom: 10px;
......
...@@ -50,11 +50,26 @@ ...@@ -50,11 +50,26 @@
.label-row { .label-row {
.label-name { .label-name {
display: inline-block; display: block;
width: 170px; margin-bottom: 10px;
@media (max-width: $screen-xs-min) { @media (min-width: $screen-sm-min) {
display: block; display: inline-block;
width: 200px;
margin-bottom: 0;
}
}
.label-description {
display: block;
margin-bottom: 10px;
@media (min-width: $screen-sm-min) {
display: inline-block;
width: 40%;
margin-left: 10px;
margin-bottom: 0;
vertical-align: middle;
} }
} }
...@@ -68,10 +83,6 @@ ...@@ -68,10 +83,6 @@
padding: 3px 4px; padding: 3px 4px;
} }
.label-subscription {
display: inline-block;
}
.dropdown-labels-error { .dropdown-labels-error {
padding: 5px 10px; padding: 5px 10px;
margin-bottom: 10px; margin-bottom: 10px;
...@@ -79,62 +90,27 @@ ...@@ -79,62 +90,27 @@
color: $white-light; color: $white-light;
} }
@mixin labels-mobile {
@media (max-width: $screen-xs-min) {
display: block;
width: 100%;
margin-left: 0;
padding: 10px 0;
}
}
.manage-labels-list { .manage-labels-list {
.btn-action {
color: $gl-dark-link-color;
.prepend-left-10, .prepend-description-left { .fa {
display: inline-block; font-size: 18px;
width: 40%; vertical-align: middle;
vertical-align: middle;
@include labels-mobile;
}
.prepend-description-left {
width: 57%;
@include labels-mobile;
}
.pull-info-right {
float: right;
@media (max-width: $screen-xs-min) {
float: none;
} }
.action-buttons { &:hover {
border-color: transparent; color: $gl-link-color;
padding: 6px;
color: $gl-text-color;
&.label-subscribe-button { &.remove-row {
padding-left: 0; color: $gl-danger;
} }
} }
}
i { .dropdown {
color: $gl-text-color; @media (min-width: $screen-sm-min) {
} float: right;
.append-right-20 {
a {
color: $gl-text-color;
}
@media (max-width: $screen-xs-min) {
display: block;
margin-bottom: 10px;
}
} }
} }
} }
...@@ -169,3 +145,40 @@ ...@@ -169,3 +145,40 @@
} }
} }
} }
.filtered-labels {
.label-row {
&:not(:last-child) {
margin-right: 5px;
}
}
.label-remove {
border-left: 1px solid rgba(0, 0, 0, .1);
z-index: 3;
}
.btn {
color: inherit;
}
}
.label-options-toggle {
width: 100%;
}
.label-subscribe-button {
.label-subscribe-button-loading {
display: none;
}
&.disabled {
.label-subscribe-button-icon {
display: none;
}
.label-subscribe-button-loading {
display: block;
}
}
}
...@@ -108,6 +108,11 @@ ...@@ -108,6 +108,11 @@
font-size: 17px; font-size: 17px;
margin: 5px 0; margin: 5px 0;
color: $gl-gray-dark; color: $gl-gray-dark;
&.has-conflicts .fa-exclamation-triangle {
color: $gl-warning;
}
} }
p:last-child { p:last-child {
......
...@@ -129,17 +129,8 @@ ...@@ -129,17 +129,8 @@
display: none; display: none;
font-size: 15px; font-size: 15px;
.form-actions { .md-area {
padding-left: 20px; background-color: #fff;
.btn-save {
float: left;
}
.note-form-option {
float: left;
padding: 2px 0 0 25px;
}
} }
} }
......
...@@ -11,18 +11,15 @@ ...@@ -11,18 +11,15 @@
$magenta: #cd00cd; $magenta: #cd00cd;
$cyan: #00cdcd; $cyan: #00cdcd;
$white: #e5e5e5; $white: #e5e5e5;
$l-black: #7f7f7f; $l-black: #373b41;
$l-red: #f00; $l-red: #c66;
$l-green: #0f0; $l-green: #b5bd68;
$l-yellow: #ff0; $l-yellow: #f0c674;
$l-blue: #5c5cff; $l-blue: #81a2be;
$l-magenta: #f0f; $l-magenta: #b294bb;
$l-cyan: #0ff; $l-cyan: #8abeb7;
$l-white: #fff; $l-white: $ci-text-color;
.term-bold {
font-weight: bold;
}
.term-italic { .term-italic {
font-style: italic; font-style: italic;
} }
......
...@@ -74,6 +74,7 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController ...@@ -74,6 +74,7 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController
:two_factor_grace_period, :two_factor_grace_period,
:gravatar_enabled, :gravatar_enabled,
:sign_in_text, :sign_in_text,
:after_sign_up_text,
:help_page_text, :help_page_text,
:home_page_url, :home_page_url,
:after_sign_out_path, :after_sign_out_path,
......
...@@ -6,6 +6,7 @@ class ApplicationController < ActionController::Base ...@@ -6,6 +6,7 @@ class ApplicationController < ActionController::Base
include Gitlab::GonHelper include Gitlab::GonHelper
include GitlabRoutingHelper include GitlabRoutingHelper
include PageLayoutHelper include PageLayoutHelper
include WorkhorseHelper
before_action :authenticate_user_from_token! before_action :authenticate_user_from_token!
before_action :authenticate_user! before_action :authenticate_user!
......
...@@ -10,10 +10,7 @@ class Projects::AvatarsController < Projects::ApplicationController ...@@ -10,10 +10,7 @@ class Projects::AvatarsController < Projects::ApplicationController
return if cached_blob? return if cached_blob?
headers.store(*Gitlab::Workhorse.send_git_blob(@repository, @blob)) send_git_blob @repository, @blob
headers['Content-Disposition'] = 'inline'
headers['Content-Type'] = safe_content_type(@blob)
head :ok # 'render nothing: true' messes up the Content-Type
else else
render_404 render_404
end end
......
...@@ -41,7 +41,7 @@ class Projects::BuildsController < Projects::ApplicationController ...@@ -41,7 +41,7 @@ class Projects::BuildsController < Projects::ApplicationController
def trace def trace
respond_to do |format| respond_to do |format|
format.json do format.json do
render json: @build.trace_with_state(params[:state]).merge!(id: @build.id, status: @build.status) render json: @build.trace_with_state(params[:state].presence).merge!(id: @build.id, status: @build.status)
end end
end end
end end
......
class Projects::GitHttpController < Projects::ApplicationController class Projects::GitHttpController < Projects::ApplicationController
attr_reader :user attr_reader :user
# Git clients will not know what authenticity token to send along
skip_before_action :verify_authenticity_token
skip_before_action :repository skip_before_action :repository
before_action :authenticate_user before_action :authenticate_user
before_action :ensure_project_found! before_action :ensure_project_found!
......
...@@ -61,12 +61,9 @@ class Projects::MergeRequestsController < Projects::ApplicationController ...@@ -61,12 +61,9 @@ class Projects::MergeRequestsController < Projects::ApplicationController
format.json { render json: @merge_request } format.json { render json: @merge_request }
format.patch { render text: @merge_request.to_patch } format.patch { render text: @merge_request.to_patch }
format.diff do format.diff do
headers.store(*Gitlab::Workhorse.send_git_diff(@project.repository, return render_404 unless @merge_request.diff_refs
@merge_request.diff_base_commit.id,
@merge_request.last_commit.id))
headers['Content-Disposition'] = 'inline'
head :ok send_git_diff @project.repository, @merge_request.diff_refs
end end
end end
end end
......
...@@ -18,10 +18,7 @@ class Projects::RawController < Projects::ApplicationController ...@@ -18,10 +18,7 @@ class Projects::RawController < Projects::ApplicationController
if @blob.lfs_pointer? if @blob.lfs_pointer?
send_lfs_object send_lfs_object
else else
headers.store(*Gitlab::Workhorse.send_git_blob(@repository, @blob)) send_git_blob @repository, @blob
headers['Content-Disposition'] = 'inline'
headers['Content-Type'] = safe_content_type(@blob)
head :ok # 'render nothing: true' messes up the Content-Type
end end
else else
render_404 render_404
......
...@@ -11,8 +11,7 @@ class Projects::RepositoriesController < Projects::ApplicationController ...@@ -11,8 +11,7 @@ class Projects::RepositoriesController < Projects::ApplicationController
end end
def archive def archive
headers.store(*Gitlab::Workhorse.send_git_archive(@project, params[:ref], params[:format])) send_git_archive @repository, ref: params[:ref], format: params[:format]
head :ok
rescue => ex rescue => ex
logger.error("#{self.class.name}: #{ex}") logger.error("#{self.class.name}: #{ex}")
return git_not_found! return git_not_found!
......
...@@ -95,7 +95,7 @@ class Projects::WikisController < Projects::ApplicationController ...@@ -95,7 +95,7 @@ class Projects::WikisController < Projects::ApplicationController
ext.analyze(text, author: current_user) ext.analyze(text, author: current_user)
render json: { render json: {
body: view_context.markdown(text, pipeline: :wiki, project_wiki: @project_wiki), body: view_context.markdown(text, pipeline: :wiki, project_wiki: @project_wiki, page_slug: params[:id]),
references: { references: {
users: ext.users.map(&:username) users: ext.users.map(&:username)
} }
......
...@@ -234,7 +234,7 @@ class ProjectsController < Projects::ApplicationController ...@@ -234,7 +234,7 @@ class ProjectsController < Projects::ApplicationController
:issues_tracker_id, :default_branch, :issues_tracker_id, :default_branch,
:wiki_enabled, :visibility_level, :import_url, :last_activity_at, :namespace_id, :avatar, :wiki_enabled, :visibility_level, :import_url, :last_activity_at, :namespace_id, :avatar,
:builds_enabled, :build_allow_git_fetch, :build_timeout_in_minutes, :build_coverage_regex, :builds_enabled, :build_allow_git_fetch, :build_timeout_in_minutes, :build_coverage_regex,
:public_builds, :public_builds, :only_allow_merge_if_build_succeeds
) )
end end
......
...@@ -14,6 +14,7 @@ class SessionsController < Devise::SessionsController ...@@ -14,6 +14,7 @@ class SessionsController < Devise::SessionsController
before_action :load_recaptcha before_action :load_recaptcha
def new def new
set_minimum_password_length
if Gitlab.config.ldap.enabled if Gitlab.config.ldap.enabled
@ldap_servers = Gitlab::LDAP::Config.servers @ldap_servers = Gitlab::LDAP::Config.servers
else else
......
...@@ -30,4 +30,8 @@ module AppearancesHelper ...@@ -30,4 +30,8 @@ module AppearancesHelper
render 'shared/logo.svg' render 'shared/logo.svg'
end end
end end
def navbar_icon(icon_name)
render "shared/icons/#{icon_name}.svg"
end
end end
...@@ -15,6 +15,10 @@ module ApplicationSettingsHelper ...@@ -15,6 +15,10 @@ module ApplicationSettingsHelper
current_application_settings.sign_in_text current_application_settings.sign_in_text
end end
def after_sign_up_text
current_application_settings.after_sign_up_text
end
def shared_runners_text def shared_runners_text
current_application_settings.shared_runners_text current_application_settings.shared_runners_text
end end
......
...@@ -67,9 +67,9 @@ module DropdownsHelper ...@@ -67,9 +67,9 @@ module DropdownsHelper
end end
end end
def dropdown_filter(placeholder) def dropdown_filter(placeholder, search_id: nil)
content_tag :div, class: "dropdown-input" do content_tag :div, class: "dropdown-input" do
filter_output = search_field_tag nil, nil, class: "dropdown-input-field", placeholder: placeholder filter_output = search_field_tag search_id, nil, class: "dropdown-input-field", placeholder: placeholder
filter_output << icon('search', class: "dropdown-input-search") filter_output << icon('search', class: "dropdown-input-search")
filter_output << icon('times', class: "dropdown-input-clear js-dropdown-input-clear", role: "button") filter_output << icon('times', class: "dropdown-input-clear js-dropdown-input-clear", role: "button")
......
...@@ -108,7 +108,7 @@ module GitlabMarkdownHelper ...@@ -108,7 +108,7 @@ module GitlabMarkdownHelper
def render_wiki_content(wiki_page) def render_wiki_content(wiki_page)
case wiki_page.format case wiki_page.format
when :markdown when :markdown
markdown(wiki_page.content, pipeline: :wiki, project_wiki: @project_wiki) markdown(wiki_page.content, pipeline: :wiki, project_wiki: @project_wiki, page_slug: wiki_page.slug)
when :asciidoc when :asciidoc
asciidoc(wiki_page.content) asciidoc(wiki_page.content)
else else
......
...@@ -32,7 +32,7 @@ module LabelsHelper ...@@ -32,7 +32,7 @@ module LabelsHelper
# link_to_label(label) { "My Custom Label Text" } # link_to_label(label) { "My Custom Label Text" }
# #
# Returns a String # Returns a String
def link_to_label(label, project: nil, type: :issue, tooltip: true, &block) def link_to_label(label, project: nil, type: :issue, tooltip: true, css_class: nil, &block)
project ||= @project || label.project project ||= @project || label.project
link = send("namespace_project_#{type.to_s.pluralize}_path", link = send("namespace_project_#{type.to_s.pluralize}_path",
project.namespace, project.namespace,
...@@ -40,9 +40,9 @@ module LabelsHelper ...@@ -40,9 +40,9 @@ module LabelsHelper
label_name: [label.name]) label_name: [label.name])
if block_given? if block_given?
link_to link, &block link_to link, class: css_class, &block
else else
link_to render_colored_label(label, tooltip: tooltip), link link_to render_colored_label(label, tooltip: tooltip), link, class: css_class
end end
end end
......
...@@ -56,7 +56,7 @@ module MilestonesHelper ...@@ -56,7 +56,7 @@ module MilestonesHelper
def milestone_remaining_days(milestone) def milestone_remaining_days(milestone)
if milestone.expired? if milestone.expired?
content_tag(:strong, 'expired') content_tag(:strong, 'Past due')
elsif milestone.due_date elsif milestone.due_date
days = milestone.remaining_days days = milestone.remaining_days
content = content_tag(:strong, days) content = content_tag(:strong, days)
......
...@@ -30,6 +30,8 @@ module NavHelper ...@@ -30,6 +30,8 @@ module NavHelper
else else
"page-gutter right-sidebar-expanded" "page-gutter right-sidebar-expanded"
end end
elsif current_path?('builds#show')
"page-gutter build-sidebar right-sidebar-expanded"
end end
end end
......
# Helpers to send Git blobs, diffs or archives through Workhorse.
# Workhorse will also serve files when using `send_file`.
module WorkhorseHelper
# Send a Git blob through Workhorse
def send_git_blob(repository, blob)
headers.store(*Gitlab::Workhorse.send_git_blob(repository, blob))
headers['Content-Disposition'] = 'inline'
headers['Content-Type'] = safe_content_type(blob)
head :ok # 'render nothing: true' messes up the Content-Type
end
# Send a Git diff through Workhorse
def send_git_diff(repository, diff_refs)
headers.store(*Gitlab::Workhorse.send_git_diff(repository, diff_refs))
headers['Content-Disposition'] = 'inline'
head :ok
end
# Archive a Git repository and send it through Workhorse
def send_git_archive(repository, ref:, format:)
headers.store(*Gitlab::Workhorse.send_git_archive(repository, ref: ref, format: format))
head :ok
end
end
...@@ -113,7 +113,10 @@ class ApplicationSetting < ActiveRecord::Base ...@@ -113,7 +113,10 @@ class ApplicationSetting < ActiveRecord::Base
signup_enabled: Settings.gitlab['signup_enabled'], signup_enabled: Settings.gitlab['signup_enabled'],
signin_enabled: Settings.gitlab['signin_enabled'], signin_enabled: Settings.gitlab['signin_enabled'],
gravatar_enabled: Settings.gravatar['enabled'], gravatar_enabled: Settings.gravatar['enabled'],
sign_in_text: Settings.extra['sign_in_text'], sign_in_text: nil,
after_sign_up_text: nil,
help_page_text: nil,
shared_runners_text: nil,
restricted_visibility_levels: Settings.gitlab['restricted_visibility_levels'], restricted_visibility_levels: Settings.gitlab['restricted_visibility_levels'],
max_attachment_size: Settings.gitlab['max_attachment_size'], max_attachment_size: Settings.gitlab['max_attachment_size'],
session_expire_delay: Settings.gitlab['session_expire_delay'], session_expire_delay: Settings.gitlab['session_expire_delay'],
......
...@@ -194,7 +194,7 @@ module Ci ...@@ -194,7 +194,7 @@ module Ci
def trace_length def trace_length
if raw_trace if raw_trace
raw_trace.length raw_trace.bytesize
else else
0 0
end end
...@@ -216,7 +216,7 @@ module Ci ...@@ -216,7 +216,7 @@ module Ci
recreate_trace_dir recreate_trace_dir
File.truncate(path_to_trace, offset) if File.exist?(path_to_trace) File.truncate(path_to_trace, offset) if File.exist?(path_to_trace)
File.open(path_to_trace, 'a') do |f| File.open(path_to_trace, 'ab') do |f|
f.write(trace_part) f.write(trace_part)
end end
end end
......
...@@ -3,7 +3,7 @@ module Ci ...@@ -3,7 +3,7 @@ module Ci
extend Ci::Model extend Ci::Model
belongs_to :trigger, class_name: 'Ci::Trigger' belongs_to :trigger, class_name: 'Ci::Trigger'
belongs_to :commit, class_name: 'Ci::Pipeline', foreign_key: :commit_id belongs_to :pipeline, class_name: 'Ci::Pipeline', foreign_key: :commit_id
has_many :builds, class_name: 'Ci::Build' has_many :builds, class_name: 'Ci::Build'
serialize :variables serialize :variables
......
...@@ -198,7 +198,7 @@ class Commit ...@@ -198,7 +198,7 @@ class Commit
end end
def notes_with_associations def notes_with_associations
notes.includes(:author, :project) notes.includes(:author)
end end
def method_missing(m, *args, &block) def method_missing(m, *args, &block)
......
...@@ -17,7 +17,12 @@ module Issuable ...@@ -17,7 +17,12 @@ module Issuable
belongs_to :assignee, class_name: "User" belongs_to :assignee, class_name: "User"
belongs_to :updated_by, class_name: "User" belongs_to :updated_by, class_name: "User"
belongs_to :milestone belongs_to :milestone
has_many :notes, as: :noteable, dependent: :destroy has_many :notes, as: :noteable, dependent: :destroy do
def authors_loaded?
# We check first if we're loaded to not load unnecesarily.
loaded? && to_a.all? { |note| note.association(:author).loaded? }
end
end
has_many :label_links, as: :target, dependent: :destroy has_many :label_links, as: :target, dependent: :destroy
has_many :labels, through: :label_links has_many :labels, through: :label_links
has_many :todos, as: :target, dependent: :destroy has_many :todos, as: :target, dependent: :destroy
...@@ -44,6 +49,7 @@ module Issuable ...@@ -44,6 +49,7 @@ module Issuable
scope :without_label, -> { joins("LEFT OUTER JOIN label_links ON label_links.target_type = '#{name}' AND label_links.target_id = #{table_name}.id").where(label_links: { id: nil }) } scope :without_label, -> { joins("LEFT OUTER JOIN label_links ON label_links.target_type = '#{name}' AND label_links.target_id = #{table_name}.id").where(label_links: { id: nil }) }
scope :join_project, -> { joins(:project) } scope :join_project, -> { joins(:project) }
scope :inc_notes_with_associations, -> { includes(notes: :author) }
scope :references_project, -> { references(:project) } scope :references_project, -> { references(:project) }
scope :non_archived, -> { join_project.where(projects: { archived: false }) } scope :non_archived, -> { join_project.where(projects: { archived: false }) }
...@@ -179,7 +185,13 @@ module Issuable ...@@ -179,7 +185,13 @@ module Issuable
end end
def user_notes_count def user_notes_count
notes.user.count if notes.loaded?
# Use the in-memory association to select and count to avoid hitting the db
notes.to_a.count { |note| !note.system? }
else
# do the count query
notes.user.count
end
end end
def subscribed_without_subscriptions?(user) def subscribed_without_subscriptions?(user)
...@@ -239,7 +251,13 @@ module Issuable ...@@ -239,7 +251,13 @@ module Issuable
end end
def notes_with_associations def notes_with_associations
notes.includes(:author, :project) # If A has_many Bs, and B has_many Cs, and you do
# `A.includes(b: :c).each { |a| a.b.includes(:c) }`, sadly ActiveRecord
# will do the inclusion again. So, we check if all notes in the relation
# already have their authors loaded (possibly because the scope
# `inc_notes_with_associations` was used) and skip the inclusion if that's
# the case.
notes.authors_loaded? ? notes : notes.includes(:author)
end end
def updated_tasks def updated_tasks
......
...@@ -260,19 +260,20 @@ class MergeRequest < ActiveRecord::Base ...@@ -260,19 +260,20 @@ class MergeRequest < ActiveRecord::Base
end end
def mergeable? def mergeable?
return false unless open? && !work_in_progress? && !broken? return false unless mergeable_state?
check_if_can_be_merged check_if_can_be_merged
can_be_merged? can_be_merged?
end end
def gitlab_merge_status def mergeable_state?
if work_in_progress? return false unless open?
"work_in_progress" return false if work_in_progress?
else return false if broken?
merge_status_name return false unless mergeable_ci_state?
end
true
end end
def can_cancel_merge_when_build_succeeds?(current_user) def can_cancel_merge_when_build_succeeds?(current_user)
...@@ -481,6 +482,12 @@ class MergeRequest < ActiveRecord::Base ...@@ -481,6 +482,12 @@ class MergeRequest < ActiveRecord::Base
::Gitlab::GitAccess.new(user, project).can_push_to_branch?(target_branch) ::Gitlab::GitAccess.new(user, project).can_push_to_branch?(target_branch)
end end
def mergeable_ci_state?
return true unless project.only_allow_merge_if_build_succeeds?
!pipeline || pipeline.success?
end
def state_human_name def state_human_name
if merged? if merged?
"Merged" "Merged"
......
...@@ -253,20 +253,69 @@ class Project < ActiveRecord::Base ...@@ -253,20 +253,69 @@ class Project < ActiveRecord::Base
non_archived.where(table[:name].matches(pattern)) non_archived.where(table[:name].matches(pattern))
end end
def find_with_namespace(id) # Finds a single project for the given path.
namespace_path, project_path = id.split('/', 2) #
# path - The full project path (including namespace path).
#
# Returns a Project, or nil if no project could be found.
def find_with_namespace(path)
where_paths_in([path]).reorder(nil).take
end
return nil if !namespace_path || !project_path # Builds a relation to find multiple projects by their full paths.
#
# Each path must be in the following format:
#
# namespace_path/project_path
#
# For example:
#
# gitlab-org/gitlab-ce
#
# Usage:
#
# Project.where_paths_in(%w{gitlab-org/gitlab-ce gitlab-org/gitlab-ee})
#
# This would return the projects with the full paths matching the values
# given.
#
# paths - An Array of full paths (namespace path + project path) for which
# to find the projects.
#
# Returns an ActiveRecord::Relation.
def where_paths_in(paths)
wheres = []
cast_lower = Gitlab::Database.postgresql?
paths.each do |path|
namespace_path, project_path = path.split('/', 2)
next unless namespace_path && project_path
namespace_path = connection.quote(namespace_path)
project_path = connection.quote(project_path)
where = "(namespaces.path = #{namespace_path}
AND projects.path = #{project_path})"
if cast_lower
where = "(
#{where}
OR (
LOWER(namespaces.path) = LOWER(#{namespace_path})
AND LOWER(projects.path) = LOWER(#{project_path})
)
)"
end
# Use of unscoped ensures we're not secretly adding any ORDER BYs, which wheres << where
# have a negative impact on performance (and aren't needed for this end
# query).
projects = unscoped.
joins(:namespace).
iwhere('namespaces.path' => namespace_path)
projects.find_by('projects.path' => project_path) || if wheres.empty?
projects.iwhere('projects.path' => project_path).take none
else
joins(:namespace).where(wheres.join(' OR '))
end
end end
def visibility_levels def visibility_levels
...@@ -523,9 +572,21 @@ class Project < ActiveRecord::Base ...@@ -523,9 +572,21 @@ class Project < ActiveRecord::Base
end end
def external_issue_tracker def external_issue_tracker
return @external_issue_tracker if defined?(@external_issue_tracker) if has_external_issue_tracker.nil? # To populate existing projects
@external_issue_tracker ||= cache_has_external_issue_tracker
services.issue_trackers.active.without_defaults.first end
if has_external_issue_tracker?
return @external_issue_tracker if defined?(@external_issue_tracker)
@external_issue_tracker = services.external_issue_trackers.first
else
nil
end
end
def cache_has_external_issue_tracker
update_column(:has_external_issue_tracker, services.external_issue_trackers.any?)
end end
def can_have_issues_tracker_id? def can_have_issues_tracker_id?
......
...@@ -16,6 +16,7 @@ class Service < ActiveRecord::Base ...@@ -16,6 +16,7 @@ class Service < ActiveRecord::Base
after_initialize :initialize_properties after_initialize :initialize_properties
after_commit :reset_updated_properties after_commit :reset_updated_properties
after_commit :cache_project_has_external_issue_tracker
belongs_to :project belongs_to :project
has_one :service_hook has_one :service_hook
...@@ -34,6 +35,7 @@ class Service < ActiveRecord::Base ...@@ -34,6 +35,7 @@ class Service < ActiveRecord::Base
scope :note_hooks, -> { where(note_events: true, active: true) } scope :note_hooks, -> { where(note_events: true, active: true) }
scope :build_hooks, -> { where(build_events: true, active: true) } scope :build_hooks, -> { where(build_events: true, active: true) }
scope :wiki_page_hooks, -> { where(wiki_page_events: true, active: true) } scope :wiki_page_hooks, -> { where(wiki_page_events: true, active: true) }
scope :external_issue_trackers, -> { issue_trackers.active.without_defaults }
default_value_for :category, 'common' default_value_for :category, 'common'
...@@ -192,4 +194,12 @@ class Service < ActiveRecord::Base ...@@ -192,4 +194,12 @@ class Service < ActiveRecord::Base
service.project_id = project_id service.project_id = project_id
service if service.save service if service.save
end end
private
def cache_project_has_external_issue_tracker
if project && !project.destroyed?
project.cache_has_external_issue_tracker
end
end
end end
...@@ -102,7 +102,7 @@ class Snippet < ActiveRecord::Base ...@@ -102,7 +102,7 @@ class Snippet < ActiveRecord::Base
end end
def notes_with_associations def notes_with_associations
notes.includes(:author, :project) notes.includes(:author)
end end
class << self class << self
......
...@@ -11,7 +11,7 @@ module Ci ...@@ -11,7 +11,7 @@ module Ci
trigger_request = trigger.trigger_requests.create!( trigger_request = trigger.trigger_requests.create!(
variables: variables, variables: variables,
commit: pipeline, pipeline: pipeline,
) )
if pipeline.create_builds(nil, trigger_request) if pipeline.create_builds(nil, trigger_request)
......
...@@ -20,7 +20,7 @@ class TodoService ...@@ -20,7 +20,7 @@ class TodoService
# * mark all pending todos related to the issue for the current user as done # * mark all pending todos related to the issue for the current user as done
# #
def update_issue(issue, current_user) def update_issue(issue, current_user)
create_mention_todos(issue.project, issue, current_user) update_issuable(issue, current_user)
end end
# When close an issue we should: # When close an issue we should:
...@@ -53,7 +53,7 @@ class TodoService ...@@ -53,7 +53,7 @@ class TodoService
# * create a todo for each mentioned user on merge request # * create a todo for each mentioned user on merge request
# #
def update_merge_request(merge_request, current_user) def update_merge_request(merge_request, current_user)
create_mention_todos(merge_request.project, merge_request, current_user) update_issuable(merge_request, current_user)
end end
# When close a merge request we should: # When close a merge request we should:
...@@ -153,6 +153,13 @@ class TodoService ...@@ -153,6 +153,13 @@ class TodoService
create_mention_todos(issuable.project, issuable, author) create_mention_todos(issuable.project, issuable, author)
end end
def update_issuable(issuable, author)
# Skip toggling a task list item in a description
return if issuable.tasks? && issuable.updated_tasks.any?
create_mention_todos(issuable.project, issuable, author)
end
def handle_note(note, author) def handle_note(note, author)
# Skip system notes, and notes on project snippet # Skip system notes, and notes on project snippet
return if note.system? || note.for_snippet? return if note.system? || note.for_snippet?
......
...@@ -154,6 +154,11 @@ ...@@ -154,6 +154,11 @@
.col-sm-10 .col-sm-10
= f.text_area :sign_in_text, class: 'form-control', rows: 4 = f.text_area :sign_in_text, class: 'form-control', rows: 4
.help-block Markdown enabled .help-block Markdown enabled
.form-group
= f.label :after_sign_up_text, class: 'control-label col-sm-2'
.col-sm-10
= f.text_area :after_sign_up_text, class: 'form-control', rows: 4
.help-block Markdown enabled
.form-group .form-group
= f.label :help_page_text, class: 'control-label col-sm-2' = f.label :help_page_text, class: 'control-label col-sm-2'
.col-sm-10 .col-sm-10
......
...@@ -7,9 +7,6 @@ ...@@ -7,9 +7,6 @@
= awards.count = awards.count
- if current_user - if current_user
:javascript
gl.awardMenuUrl = "#{emojis_path}"
.award-menu-holder.js-award-holder .award-menu-holder.js-award-holder
%button.btn.award-control.js-add-award{ type: "button" } %button.btn.award-control.js-add-award{ type: "button" }
= icon('smile-o', class: "award-control-icon award-control-icon-normal") = icon('smile-o', class: "award-control-icon award-control-icon-normal")
......
...@@ -9,5 +9,4 @@ ...@@ -9,5 +9,4 @@
- if current_user.can_create_group? - if current_user.can_create_group?
.nav-controls .nav-controls
= link_to new_group_path, class: "btn btn-new" do = link_to new_group_path, class: "btn btn-new" do
= icon('plus')
New Group New Group
...@@ -18,5 +18,4 @@ ...@@ -18,5 +18,4 @@
= render 'shared/projects/dropdown' = render 'shared/projects/dropdown'
- if current_user.can_create_project? - if current_user.can_create_project?
= link_to new_project_path, class: 'btn btn-new' do = link_to new_project_path, class: 'btn btn-new' do
= icon('plus')
New Project New Project
...@@ -3,6 +3,9 @@ ...@@ -3,6 +3,9 @@
Almost there... Almost there...
%p.lead %p.lead
Please check your email to confirm your account Please check your email to confirm your account
- if after_sign_up_text.present?
.well-confirmation.text-center
= markdown(after_sign_up_text)
%p.confirmation-content.text-center %p.confirmation-content.text-center
No confirmation email received? Please check your spam folder or No confirmation email received? Please check your spam folder or
.append-bottom-20.prepend-top-20.text-center .append-bottom-20.prepend-top-20.text-center
......
...@@ -6,7 +6,8 @@ ...@@ -6,7 +6,8 @@
- if @user.two_factor_otp_enabled? - if @user.two_factor_otp_enabled?
%h5 Authenticate via Two-Factor App %h5 Authenticate via Two-Factor App
= form_for(resource, as: resource_name, url: session_path(resource_name), method: :post) do |f| = form_for(resource, as: resource_name, url: session_path(resource_name), method: :post) do |f|
= f.hidden_field :remember_me, value: params[resource_name][:remember_me] - resource_params = params[resource_name].presence || params
= f.hidden_field :remember_me, value: resource_params.fetch(:remember_me, 0)
= f.text_field :otp_attempt, class: 'form-control', placeholder: 'Two-Factor Authentication code', required: true, autofocus: true, autocomplete: 'off' = f.text_field :otp_attempt, class: 'form-control', placeholder: 'Two-Factor Authentication code', required: true, autofocus: true, autocomplete: 'off'
%p.help-block.hint Enter the code from the two-factor app on your mobile device. If you've lost your device, you may enter one of your recovery codes. %p.help-block.hint Enter the code from the two-factor app on your mobile device. If you've lost your device, you may enter one of your recovery codes.
.prepend-top-20 .prepend-top-20
......
...@@ -16,7 +16,7 @@ ...@@ -16,7 +16,7 @@
%div %div
= f.email_field :email, class: "form-control middle", placeholder: "Email", required: true = f.email_field :email, class: "form-control middle", placeholder: "Email", required: true
.form-group.append-bottom-20#password-strength .form-group.append-bottom-20#password-strength
= f.password_field :password, class: "form-control bottom", placeholder: "Password", required: true = f.password_field :password, class: "form-control bottom", placeholder: "Password - minimum length #{@minimum_password_length} characters", required: true, pattern: ".{#{@minimum_password_length},}", title: "Minimum length is #{@minimum_password_length} characters"
%div %div
- if current_application_settings.recaptcha_enabled - if current_application_settings.recaptcha_enabled
= recaptcha_tags = recaptcha_tags
......
...@@ -34,9 +34,9 @@ ...@@ -34,9 +34,9 @@
%strong.member-access-level= member.human_access %strong.member-access-level= member.human_access
- if show_controls - if show_controls
- if can?(current_user, :update_group_member, member) - if can?(current_user, :update_group_member, member)
= button_tag class: "btn-xs btn js-toggle-button", = button_tag class: "btn-xs btn btn-grouped inline js-toggle-button",
title: 'Edit access level', type: 'button' do title: 'Edit access level', type: 'button' do
%i.fa.fa-pencil-square-o = icon('pencil')
- if can?(current_user, :destroy_group_member, member) - if can?(current_user, :destroy_group_member, member)
&nbsp; &nbsp;
...@@ -46,7 +46,7 @@ ...@@ -46,7 +46,7 @@
Leave Leave
- else - else
= link_to group_group_member_path(@group, member), data: { confirm: remove_user_from_group_message(@group, member) }, method: :delete, remote: true, class: "btn-xs btn btn-remove", title: 'Remove user from group' do = link_to group_group_member_path(@group, member), data: { confirm: remove_user_from_group_message(@group, member) }, method: :delete, remote: true, class: "btn-xs btn btn-remove", title: 'Remove user from group' do
%i.fa.fa-minus.fa-inverse = icon('trash')
.edit-member.hide.js-toggle-content .edit-member.hide.js-toggle-content
%br %br
......
...@@ -39,9 +39,8 @@ ...@@ -39,9 +39,8 @@
.col-md-6 .col-md-6
.form-group .form-group
= f.label :due_date, "Due Date", class: "control-label" = f.label :due_date, "Due Date", class: "control-label"
.col-sm-10= f.hidden_field :due_date
.col-sm-10 .col-sm-10
.datepicker = f.text_field :due_date, class: "datepicker form-control", placeholder: "Select due date"
.form-actions .form-actions
= f.submit 'Create Milestone', class: "btn-create btn" = f.submit 'Create Milestone', class: "btn-create btn"
......
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
= auto_discovery_link_tag(:atom, group_url(@group, format: :atom, private_token: current_user.private_token), title: "#{@group.name} activity") = auto_discovery_link_tag(:atom, group_url(@group, format: :atom, private_token: current_user.private_token), title: "#{@group.name} activity")
.cover-block.groups-cover-block .cover-block.groups-cover-block
.container-fluid.container-limited %div{ class: (container_class) }
= link_to group_icon(@group), target: '_blank' do = link_to group_icon(@group), target: '_blank' do
= image_tag group_icon(@group), class: "avatar group-avatar s70" = image_tag group_icon(@group), class: "avatar group-avatar s70"
.group-info .group-info
...@@ -35,7 +35,6 @@ ...@@ -35,7 +35,6 @@
= render 'shared/projects/dropdown' = render 'shared/projects/dropdown'
- if can? current_user, :create_projects, @group - if can? current_user, :create_projects, @group
= link_to new_project_path(namespace_id: @group.id), class: 'btn btn-new pull-right' do = link_to new_project_path(namespace_id: @group.id), class: 'btn btn-new pull-right' do
= icon('plus')
New Project New Project
.tab-content .tab-content
......
...@@ -2,106 +2,102 @@ ...@@ -2,106 +2,102 @@
= nav_link(controller: :dashboard, html_options: {class: 'home'}) do = nav_link(controller: :dashboard, html_options: {class: 'home'}) do
= link_to admin_root_path, title: 'Overview' do = link_to admin_root_path, title: 'Overview' do
= icon('dashboard fw') = icon('dashboard fw')
%span .nav-link-text
Overview Overview
= nav_link(controller: [:admin, :projects]) do = nav_link(controller: [:admin, :projects]) do
= link_to admin_namespaces_projects_path, title: 'Projects' do = link_to admin_namespaces_projects_path, title: 'Projects' do
= icon('cube fw') = icon('cube fw')
%span .nav-link-text
Projects Projects
= nav_link(controller: :users) do = nav_link(controller: :users) do
= link_to admin_users_path, title: 'Users' do = link_to admin_users_path, title: 'Users' do
= icon('user fw') = icon('user fw')
%span .nav-link-text
Users Users
= nav_link(controller: :groups) do = nav_link(controller: :groups) do
= link_to admin_groups_path, title: 'Groups' do = link_to admin_groups_path, title: 'Groups' do
= icon('group fw') = icon('group fw')
%span .nav-link-text
Groups Groups
= nav_link(controller: :deploy_keys) do = nav_link(controller: :deploy_keys) do
= link_to admin_deploy_keys_path, title: 'Deploy Keys' do = link_to admin_deploy_keys_path, title: 'Deploy Keys' do
= icon('key fw') = icon('key fw')
%span .nav-link-text
Deploy Keys Deploy Keys
= nav_link path: ['runners#index', 'runners#show'] do = nav_link path: ['runners#index', 'runners#show'] do
= link_to admin_runners_path, title: 'Runners' do = link_to admin_runners_path, title: 'Runners' do
= icon('cog fw') = icon('cog fw')
%span .nav-link-text
Runners Runners
%span.count= number_with_delimiter(Ci::Runner.count(:all))
= nav_link path: 'builds#index' do = nav_link path: 'builds#index' do
= link_to admin_builds_path, title: 'Builds' do = link_to admin_builds_path, title: 'Builds' do
= icon('link fw') = icon('link fw')
%span .nav-link-text
Builds Builds
%span.count= number_with_delimiter(Ci::Build.count(:all))
= nav_link(controller: :logs) do = nav_link(controller: :logs) do
= link_to admin_logs_path, title: 'Logs' do = link_to admin_logs_path, title: 'Logs' do
= icon('file-text fw') = icon('file-text fw')
%span .nav-link-text
Logs Logs
= nav_link(controller: :health_check) do = nav_link(controller: :health_check) do
= link_to admin_health_check_path, title: 'Health Check' do = link_to admin_health_check_path, title: 'Health Check' do
= icon('medkit fw') = icon('medkit fw')
%span .nav-link-text
Health Check Health Check
= nav_link(controller: :broadcast_messages) do = nav_link(controller: :broadcast_messages) do
= link_to admin_broadcast_messages_path, title: 'Messages' do = link_to admin_broadcast_messages_path, title: 'Messages' do
= icon('bullhorn fw') = icon('bullhorn fw')
%span .nav-link-text
Messages Messages
= nav_link(controller: :hooks) do = nav_link(controller: :hooks) do
= link_to admin_hooks_path, title: 'Hooks' do = link_to admin_hooks_path, title: 'Hooks' do
= icon('external-link fw') = icon('external-link fw')
%span .nav-link-text
Hooks Hooks
= nav_link(controller: :background_jobs) do = nav_link(controller: :background_jobs) do
= link_to admin_background_jobs_path, title: 'Background Jobs' do = link_to admin_background_jobs_path, title: 'Background Jobs' do
= icon('cog fw') = icon('cog fw')
%span .nav-link-text
Background Jobs Background Jobs
= nav_link(controller: :appearances) do = nav_link(controller: :appearances) do
= link_to admin_appearances_path, title: 'Appearances' do = link_to admin_appearances_path, title: 'Appearances' do
= icon('image') = icon('image')
%span .nav-link-text
Appearance Appearance
= nav_link(controller: :applications) do = nav_link(controller: :applications) do
= link_to admin_applications_path, title: 'Applications' do = link_to admin_applications_path, title: 'Applications' do
= icon('cloud fw') = icon('cloud fw')
%span .nav-link-text
Applications Applications
= nav_link(controller: :services) do = nav_link(controller: :services) do
= link_to admin_application_settings_services_path, title: 'Service Templates' do = link_to admin_application_settings_services_path, title: 'Service Templates' do
= icon('copy fw') = icon('copy fw')
%span .nav-link-text
Service Templates Service Templates
= nav_link(controller: :labels) do = nav_link(controller: :labels) do
= link_to admin_labels_path, title: 'Labels' do = link_to admin_labels_path, title: 'Labels' do
= icon('tags fw') = icon('tags fw')
%span .nav-link-text
Labels Labels
= nav_link(controller: :abuse_reports) do = nav_link(controller: :abuse_reports) do
= link_to admin_abuse_reports_path, title: "Abuse Reports" do = link_to admin_abuse_reports_path, title: "Abuse Reports" do
= icon('exclamation-circle fw') = icon('exclamation-circle fw')
%span .nav-link-text
Abuse Reports Abuse Reports
%span.count= number_with_delimiter(AbuseReport.count(:all))
- if askimet_enabled? - if askimet_enabled?
= nav_link(controller: :spam_logs) do = nav_link(controller: :spam_logs) do
= link_to admin_spam_logs_path, title: "Spam Logs" do = link_to admin_spam_logs_path, title: "Spam Logs" do
= icon('exclamation-triangle fw') = icon('exclamation-triangle fw')
%span .nav-link-text
Spam Logs Spam Logs
%span.count= number_with_delimiter(SpamLog.count(:all))
= nav_link(controller: :application_settings, html_options: { class: 'separate-item'}) do = nav_link(controller: :application_settings, html_options: { class: 'separate-item'}) do
= link_to admin_application_settings_path, title: 'Settings' do = link_to admin_application_settings_path, title: 'Settings' do
= icon('cogs fw') = icon('cogs fw')
%span .nav-link-text
Settings Settings
%ul.nav.nav-sidebar %ul.nav.nav-sidebar
= nav_link(path: ['root#index', 'projects#trending', 'projects#starred', 'dashboard/projects#index'], html_options: {class: "#{project_tab_class} home"}) do = nav_link(path: ['root#index', 'projects#trending', 'projects#starred', 'dashboard/projects#index'], html_options: {class: "#{project_tab_class} home"}) do
= link_to dashboard_projects_path, title: 'Projects', class: 'dashboard-shortcuts-projects' do = link_to dashboard_projects_path, title: 'Projects', class: 'dashboard-shortcuts-projects' do
= icon('bookmark fw') = navbar_icon('project')
.nav-link-text .nav-link-text
Projects Projects
= nav_link(controller: :todos) do = nav_link(controller: :todos) do
...@@ -11,27 +11,27 @@ ...@@ -11,27 +11,27 @@
Todos Todos
= nav_link(path: 'dashboard#activity') do = nav_link(path: 'dashboard#activity') do
= link_to activity_dashboard_path, class: 'dashboard-shortcuts-activity', title: 'Activity' do = link_to activity_dashboard_path, class: 'dashboard-shortcuts-activity', title: 'Activity' do
= icon('dashboard fw') = navbar_icon('activity')
.nav-link-text .nav-link-text
Activity Activity
= nav_link(controller: [:groups, 'groups/milestones', 'groups/group_members']) do = nav_link(controller: [:groups, 'groups/milestones', 'groups/group_members']) do
= link_to dashboard_groups_path, title: 'Groups' do = link_to dashboard_groups_path, title: 'Groups' do
= icon('group fw') = navbar_icon('group')
.nav-link-text .nav-link-text
Groups Groups
= nav_link(controller: 'dashboard/milestones') do = nav_link(controller: 'dashboard/milestones') do
= link_to dashboard_milestones_path, title: 'Milestones' do = link_to dashboard_milestones_path, title: 'Milestones' do
= icon('clock-o fw') = navbar_icon('milestones')
.nav-link-text .nav-link-text
Milestones Milestones
= nav_link(path: 'dashboard#issues') do = nav_link(path: 'dashboard#issues') do
= link_to assigned_issues_dashboard_path, title: 'Issues', class: 'dashboard-shortcuts-issues' do = link_to assigned_issues_dashboard_path, title: 'Issues', class: 'dashboard-shortcuts-issues' do
= icon('exclamation-circle fw') = navbar_icon('issues')
.nav-link-text .nav-link-text
Issues Issues
= nav_link(path: 'dashboard#merge_requests') do = nav_link(path: 'dashboard#merge_requests') do
= link_to assigned_mrs_dashboard_path, title: 'Merge Requests', class: 'dashboard-shortcuts-merge_requests' do = link_to assigned_mrs_dashboard_path, title: 'Merge Requests', class: 'dashboard-shortcuts-merge_requests' do
= icon('tasks fw') = navbar_icon('mr')
.nav-link-text .nav-link-text
Merge Requests Merge Requests
= nav_link(controller: :snippets) do = nav_link(controller: :snippets) do
......
...@@ -2,20 +2,20 @@ ...@@ -2,20 +2,20 @@
= nav_link(path: ['dashboard#show', 'root#show', 'projects#trending', 'projects#starred', 'projects#index'], html_options: {class: 'home'}) do = nav_link(path: ['dashboard#show', 'root#show', 'projects#trending', 'projects#starred', 'projects#index'], html_options: {class: 'home'}) do
= link_to explore_root_path, title: 'Projects' do = link_to explore_root_path, title: 'Projects' do
= icon('bookmark fw') = icon('bookmark fw')
%span .nav-link-text
Projects Projects
= nav_link(controller: [:groups, 'groups/milestones', 'groups/group_members']) do = nav_link(controller: [:groups, 'groups/milestones', 'groups/group_members']) do
= link_to explore_groups_path, title: 'Groups' do = link_to explore_groups_path, title: 'Groups' do
= icon('group fw') = icon('group fw')
%span .nav-link-text
Groups Groups
= nav_link(controller: :snippets) do = nav_link(controller: :snippets) do
= link_to explore_snippets_path, title: 'Snippets' do = link_to explore_snippets_path, title: 'Snippets' do
= icon('clipboard fw') = icon('clipboard fw')
%span .nav-link-text
Snippets Snippets
= nav_link(controller: :help) do = nav_link(controller: :help) do
= link_to help_path, title: 'Help' do = link_to help_path, title: 'Help' do
= icon('question-circle fw') = icon('question-circle fw')
%span .nav-link-text
Help Help
...@@ -5,36 +5,36 @@ ...@@ -5,36 +5,36 @@
.fade-left .fade-left
= nav_link(path: 'groups#show', html_options: {class: 'home'}) do = nav_link(path: 'groups#show', html_options: {class: 'home'}) do
= link_to group_path(@group), title: 'Home' do = link_to group_path(@group), title: 'Home' do
= icon('group fw') = navbar_icon('group')
%span %span
Group Group
= nav_link(path: 'groups#activity') do = nav_link(path: 'groups#activity') do
= link_to activity_group_path(@group), title: 'Activity' do = link_to activity_group_path(@group), title: 'Activity' do
= icon('dashboard fw') = navbar_icon('activity')
%span %span
Activity Activity
= nav_link(controller: [:group, :milestones]) do = nav_link(controller: [:group, :milestones]) do
= link_to group_milestones_path(@group), title: 'Milestones' do = link_to group_milestones_path(@group), title: 'Milestones' do
= icon('clock-o fw') = navbar_icon('milestones')
%span %span
Milestones Milestones
= nav_link(path: 'groups#issues') do = nav_link(path: 'groups#issues') do
= link_to issues_group_path(@group), title: 'Issues' do = link_to issues_group_path(@group), title: 'Issues' do
= icon('exclamation-circle fw') = navbar_icon('issues')
%span %span
Issues Issues
- issues = IssuesFinder.new(current_user, group_id: @group.id, state: 'opened').execute - issues = IssuesFinder.new(current_user, group_id: @group.id, state: 'opened').execute
%span.badge.count= number_with_delimiter(issues.count) %span.badge.count= number_with_delimiter(issues.count)
= nav_link(path: 'groups#merge_requests') do = nav_link(path: 'groups#merge_requests') do
= link_to merge_requests_group_path(@group), title: 'Merge Requests' do = link_to merge_requests_group_path(@group), title: 'Merge Requests' do
= icon('tasks fw') = navbar_icon('mr')
%span %span
Merge Requests Merge Requests
- merge_requests = MergeRequestsFinder.new(current_user, group_id: @group.id, state: 'opened').execute - merge_requests = MergeRequestsFinder.new(current_user, group_id: @group.id, state: 'opened').execute
%span.badge.count= number_with_delimiter(merge_requests.count) %span.badge.count= number_with_delimiter(merge_requests.count)
= nav_link(controller: [:group_members]) do = nav_link(controller: [:group_members]) do
= link_to group_group_members_path(@group), title: 'Members' do = link_to group_group_members_path(@group), title: 'Members' do
= icon('users fw') = navbar_icon('members')
%span %span
Members Members
.fade-right .fade-right
...@@ -24,17 +24,19 @@ ...@@ -24,17 +24,19 @@
.fade-left .fade-left
= nav_link(path: 'projects#show', html_options: {class: 'home'}) do = nav_link(path: 'projects#show', html_options: {class: 'home'}) do
= link_to project_path(@project), title: 'Project', class: 'shortcuts-project' do = link_to project_path(@project), title: 'Project', class: 'shortcuts-project' do
= icon('bookmark fw') = navbar_icon('project')
%span %span
Project Project
= nav_link(path: 'projects#activity') do = nav_link(path: 'projects#activity') do
= link_to activity_project_path(@project), title: 'Activity', class: 'shortcuts-project-activity' do = link_to activity_project_path(@project), title: 'Activity', class: 'shortcuts-project-activity' do
= icon('dashboard fw') = navbar_icon('activity')
%span %span
Activity Activity
- if project_nav_tab? :files - 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 = 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: 'Files', class: 'shortcuts-tree' do = link_to project_files_path(@project), title: 'Code', class: 'shortcuts-tree' do
= icon('code fw') = icon('code fw')
%span %span
Code Code
...@@ -42,7 +44,7 @@ ...@@ -42,7 +44,7 @@
- if project_nav_tab? :pipelines - if project_nav_tab? :pipelines
= nav_link(controller: :pipelines) do = nav_link(controller: :pipelines) do
= link_to project_pipelines_path(@project), title: 'Pipelines', class: 'shortcuts-pipelines' do = link_to project_pipelines_path(@project), title: 'Pipelines', class: 'shortcuts-pipelines' do
= icon('ship fw') = navbar_icon('pipelines')
%span %span
Pipelines Pipelines
...@@ -60,17 +62,10 @@ ...@@ -60,17 +62,10 @@
%span %span
Graphs Graphs
- if project_nav_tab? :milestones
= nav_link(controller: :milestones) do
= link_to namespace_project_milestones_path(@project.namespace, @project), title: 'Milestones' do
= icon('clock-o fw')
%span
Milestones
- if project_nav_tab? :issues - if project_nav_tab? :issues
= nav_link(controller: :issues) do = nav_link(controller: [:issues, :labels, :milestones]) do
= link_to url_for_project_issues(@project, only_path: true), title: 'Issues', class: 'shortcuts-issues' do = link_to url_for_project_issues(@project, only_path: true), title: 'Issues', class: 'shortcuts-issues' do
= icon('exclamation-circle fw') = navbar_icon('issues')
%span %span
Issues Issues
- if @project.default_issues_tracker? - if @project.default_issues_tracker?
...@@ -79,22 +74,15 @@ ...@@ -79,22 +74,15 @@
- if project_nav_tab? :merge_requests - if project_nav_tab? :merge_requests
= nav_link(controller: :merge_requests) do = nav_link(controller: :merge_requests) do
= link_to namespace_project_merge_requests_path(@project.namespace, @project), title: 'Merge Requests', class: 'shortcuts-merge_requests' do = link_to namespace_project_merge_requests_path(@project.namespace, @project), title: 'Merge Requests', class: 'shortcuts-merge_requests' do
= icon('tasks fw') = navbar_icon('mr')
%span %span
Merge Requests Merge Requests
%span.badge.count.merge_counter= number_with_delimiter(@project.merge_requests.opened.count) %span.badge.count.merge_counter= number_with_delimiter(@project.merge_requests.opened.count)
- if project_nav_tab? :labels
= nav_link(controller: :labels) do
= link_to namespace_project_labels_path(@project.namespace, @project), title: 'Labels' do
= icon('tags fw')
%span
Labels
- if project_nav_tab? :wiki - if project_nav_tab? :wiki
= nav_link(controller: :wikis) do = nav_link(controller: :wikis) do
= link_to get_project_wiki_path(@project), title: 'Wiki', class: 'shortcuts-wiki' do = link_to get_project_wiki_path(@project), title: 'Wiki', class: 'shortcuts-wiki' do
= icon('book fw') = navbar_icon('wiki')
%span %span
Wiki Wiki
...@@ -127,5 +115,4 @@ ...@@ -127,5 +115,4 @@
%li.hidden %li.hidden
= link_to project_commits_path(@project), title: 'Commits', class: 'shortcuts-commits' do = link_to project_commits_path(@project), title: 'Commits', class: 'shortcuts-commits' do
Commits Commits
.fade-right .fade-right
...@@ -5,8 +5,8 @@ ...@@ -5,8 +5,8 @@
- content_for :scripts_body_top do - content_for :scripts_body_top do
- project = @target_project || @project - project = @target_project || @project
- if @project_wiki - if @project_wiki && @page
- markdown_preview_path = namespace_project_wikis_markdown_preview_path(project.namespace, project) - markdown_preview_path = namespace_project_wiki_markdown_preview_path(project.namespace, project, params[:id])
- else - else
- markdown_preview_path = markdown_preview_namespace_project_path(project.namespace, project) - markdown_preview_path = markdown_preview_namespace_project_path(project.namespace, project)
- if current_user - if current_user
......
- empty_repo = @project.empty_repo? - empty_repo = @project.empty_repo?
.project-home-panel.cover-block.clearfix{:class => ("empty-project" if empty_repo)} .project-home-panel.cover-block.clearfix{:class => ("empty-project" if empty_repo)}
.container-fluid.container-limited %div{ class: (container_class) }
.row .row
.project-image-container .project-image-container
= project_icon(@project, alt: '', class: 'project-avatar avatar s70') = project_icon(@project, alt: '', class: 'project-avatar avatar s70')
......
%fieldset.builds-feature
%h5.prepend-top-0
Merge Requests
.form-group
.checkbox
= f.label :only_allow_merge_if_build_succeeds do
= f.check_box :only_allow_merge_if_build_succeeds
%strong Only allow merge requests to be merged if the build succeeds
.help-block
Builds need to be configured to enable this feature.
= link_to icon('question-circle'), help_page_path('workflow', 'merge_requests#only-allow-merge-requests-to-be-merged-if-the-build-succeeds')
- page_title 'Artifacts', "#{@build.name} (##{@build.id})", 'Builds' - page_title 'Artifacts', "#{@build.name} (##{@build.id})", 'Builds'
- header_title project_title(@project, "Builds", project_builds_path(@project))
.top-block.row-content-block.clearfix .top-block.row-content-block.clearfix
.pull-right .pull-right
......
...@@ -21,12 +21,10 @@ ...@@ -21,12 +21,10 @@
.controls.hidden-xs .controls.hidden-xs
- if create_mr_button?(@repository.root_ref, branch.name) - if create_mr_button?(@repository.root_ref, branch.name)
= link_to create_mr_path(@repository.root_ref, branch.name), class: 'btn btn-grouped btn-xs' do = link_to create_mr_path(@repository.root_ref, branch.name), class: 'btn btn-grouped btn-xs' do
= icon('plus')
Merge Request Merge Request
- if branch.name != @repository.root_ref - if branch.name != @repository.root_ref
= link_to namespace_project_compare_index_path(@project.namespace, @project, from: @repository.root_ref, to: branch.name), class: 'btn btn-grouped btn-xs', method: :post, title: "Compare" do = link_to namespace_project_compare_index_path(@project.namespace, @project, from: @repository.root_ref, to: branch.name), class: 'btn btn-grouped btn-xs', method: :post, title: "Compare" do
= icon("exchange")
Compare Compare
- if can_remove_branch?(@project, branch.name) - if can_remove_branch?(@project, branch.name)
......
...@@ -3,31 +3,30 @@ ...@@ -3,31 +3,30 @@
= render "projects/commits/head" = render "projects/commits/head"
%div{ class: (container_class) } %div{ class: (container_class) }
.row-content-block.second-block.content-component-block .top-area
.pull-right .nav-text
- if can? current_user, :push_code, @project Protected branches can be managed in project settings
- if can? current_user, :push_code, @project
.nav-controls
= link_to new_namespace_project_branch_path(@project.namespace, @project), class: 'btn btn-create' do = link_to new_namespace_project_branch_path(@project.namespace, @project), class: 'btn btn-create' do
= icon('plus')
New branch New branch
&nbsp; .dropdown.inline
.dropdown.inline %button.dropdown-toggle.btn{type: 'button', 'data-toggle' => 'dropdown'}
%button.dropdown-toggle.btn{type: 'button', 'data-toggle' => 'dropdown'} %span.light
%span.light - if @sort.present?
- if @sort.present? = @sort.humanize
= @sort.humanize - else
- else
Name
%b.caret
%ul.dropdown-menu.dropdown-menu-align-right
%li
= link_to namespace_project_branches_path(sort: nil) do
Name Name
= link_to namespace_project_branches_path(sort: 'recently_updated') do %b.caret
= sort_title_recently_updated %ul.dropdown-menu.dropdown-menu-align-right
= link_to namespace_project_branches_path(sort: 'last_updated') do %li
= sort_title_oldest_updated = link_to namespace_project_branches_path(sort: nil) do
.oneline Name
Protected branches can be managed in project settings = link_to namespace_project_branches_path(sort: 'recently_updated') do
= sort_title_recently_updated
= link_to namespace_project_branches_path(sort: 'last_updated') do
= sort_title_oldest_updated
- unless @branches.empty? - unless @branches.empty?
%ul.content-list.all-branches %ul.content-list.all-branches
- @branches.each do |branch| - @branches.each do |branch|
......
.content-block.build-header
= ci_status_with_icon(@build.status)
Build
%strong ##{@build.id}
for commit
= link_to ci_status_path(@build.pipeline) do
%strong= @build.pipeline.short_sha
from
= link_to namespace_project_commits_path(@project.namespace, @project, @build.ref) do
%code
= @build.ref
- if @build.user
= render "user"
= time_ago_with_tooltip(@build.created_at)
%button.btn.btn-default.pull-right.visible-xs-block.visible-sm-block.build-gutter-toggle.js-sidebar-build-toggle{ role: "button", type: "button" }
= icon('angle-double-left')
%aside.right-sidebar.right-sidebar-expanded.build-sidebar.js-build-sidebar
.block.build-sidebar-header.visible-xs-block.visible-sm-block.append-bottom-default
Build
%strong ##{@build.id}
%a.gutter-toggle.pull-right.js-sidebar-build-toggle{ href: "#" }
= icon('angle-double-right')
- if @build.coverage
.block.block-first
.title
Test coverage
%p.build-detail-row
#{@build.coverage}%
- if can?(current_user, :read_build, @project) && @build.artifacts?
.block{ class: ("block-first" if !@build.coverage) }
.title
Build artifacts
.btn-group.btn-group-justified{ role: :group }
= link_to download_namespace_project_build_artifacts_path(@project.namespace, @project, @build), class: 'btn btn-sm btn-default' do
Download
- if @build.artifacts_metadata?
= link_to browse_namespace_project_build_artifacts_path(@project.namespace, @project, @build), class: 'btn btn-sm btn-default' do
Browse
.block{ class: ("block-first" if !@build.coverage && !(can?(current_user, :read_build, @project) && @build.artifacts?)) }
.title
Build details
- if @build.retryable?
= link_to "Retry", retry_namespace_project_build_path(@project.namespace, @project, @build), class: 'pull-right', method: :post
- if @build.merge_request
%p.build-detail-row
%span.build-light-text Merge Request:
= link_to "#{@build.merge_request.to_reference}", merge_request_path(@build.merge_request)
- if @build.duration
%p.build-detail-row
%span.build-light-text Duration:
#{duration_in_words(@build.finished_at, @build.started_at)}
- if @build.finished_at
%p.build-detail-row
%span.build-light-text Finished:
#{time_ago_with_tooltip(@build.finished_at)}
- if @build.erased_at
%p.build-detail-row
%span.build-light-text Erased:
#{time_ago_with_tooltip(@build.erased_at)}
%p.build-detail-row
%span.build-light-text Runner:
- if @build.runner && current_user && current_user.admin
= link_to "##{@build.runner.id}", admin_runner_path(@build.runner.id)
- elsif @build.runner
\##{@build.runner.id}
.btn-group.btn-group-justified{ role: :group }
- if @build.has_trace?
= link_to 'Raw', raw_namespace_project_build_path(@project.namespace, @project, @build), class: 'btn btn-sm btn-default'
- if @build.active?
= link_to "Cancel", cancel_namespace_project_build_path(@project.namespace, @project, @build), class: 'btn btn-sm btn-default', method: :post
- if can?(current_user, :update_build, @project) && @build.erasable?
= link_to erase_namespace_project_build_path(@project.namespace, @project, @build),
class: "btn btn-sm btn-default", method: :post,
data: { confirm: "Are you sure you want to erase this build?" } do
Erase
- if @build.trigger_request
.build-widget
%h4.title
Trigger
%p
%span.build-light-text Token:
#{@build.trigger_request.trigger.short_token}
- if @build.trigger_request.variables
%p
%span.build-light-text Variables:
%code
- @build.trigger_request.variables.each do |key, value|
#{key}=#{value}
.block
.title
Commit message
%p.build-light-text.append-bottom-0
#{@build.pipeline.git_commit_message}
- if @build.tags.any?
.block
.title
Tags
- @build.tag_list.each do |tag|
%span.label.label-primary
= tag
by
%a{ href: user_path(@build.user) }
= image_tag avatar_icon(@build.user, 24), class: "avatar s24"
%strong= @build.user.to_reference
...@@ -34,7 +34,6 @@ ...@@ -34,7 +34,6 @@
= link_to 'Get started with Builds', help_page_path('ci/quick_start', 'README'), class: 'btn btn-info' = link_to 'Get started with Builds', help_page_path('ci/quick_start', 'README'), class: 'btn btn-info'
= link_to ci_lint_path, class: 'btn btn-default' do = link_to ci_lint_path, class: 'btn btn-default' do
= icon('wrench')
%span CI Lint %span CI Lint
%ul.content-list %ul.content-list
......
- page_title "#{@build.name} (##{@build.id})", "Builds" - page_title "#{@build.name} (##{@build.id})", "Builds"
- trace_with_state = @build.trace_with_state - trace_with_state = @build.trace_with_state
- header_title project_title(@project, "Builds", project_builds_path(@project))
.build-page .build-page
.row-content-block.top-block = render "header"
Build ##{@build.id} for commit
%strong.monospace= link_to @build.pipeline.short_sha, ci_status_path(@build.pipeline)
from
= link_to @build.ref, namespace_project_commits_path(@project.namespace, @project, @build.ref)
- merge_request = @build.merge_request
- if merge_request
via
= link_to "merge request #{merge_request.to_reference}", merge_request_path(merge_request)
#up-build-trace
- builds = @build.pipeline.builds.latest.to_a - builds = @build.pipeline.builds.latest.to_a
- if builds.size > 1 - if builds.size > 1
%ul.nav-links.no-top.no-bottom %ul.nav-links.no-top.no-bottom
...@@ -33,18 +25,6 @@ ...@@ -33,18 +25,6 @@
&middot; &middot;
%i.fa.fa-warning %i.fa.fa-warning
This build was retried. This build was retried.
.row-content-block.middle-block
.build-head
.clearfix
= ci_status_with_icon(@build.status)
- if @build.duration
%span
%i.fa.fa-time
#{duration_in_words(@build.finished_at, @build.started_at)}
.pull-right
#{time_ago_with_tooltip(@build.finished_at) if @build.finished_at}
- if @build.stuck? - if @build.stuck?
- unless @build.any_runners_online? - unless @build.any_runners_online?
.bs-callout.bs-callout-warning .bs-callout.bs-callout-warning
...@@ -64,158 +44,27 @@ ...@@ -64,158 +44,27 @@
= link_to namespace_project_runners_path(@build.project.namespace, @build.project) do = link_to namespace_project_runners_path(@build.project.namespace, @build.project) do
Runners page Runners page
.row.prepend-top-default .prepend-top-default
.col-md-9 - if @build.active?
.clearfix .autoscroll-container
- if @build.active? %button.btn.btn-success.btn-sm#autoscroll-button{:type => "button", :data => {:state => 'disabled'}} enable autoscroll
.autoscroll-container
%button.btn.btn-success.btn-sm#autoscroll-button{:type => "button", :data => {:state => 'disabled'}} enable autoscroll
.clearfix
#js-build-scroll.scroll-controls #js-build-scroll.scroll-controls
= link_to '#up-build-trace', class: 'btn' do = link_to '#build-trace', class: 'btn' do
%i.fa.fa-angle-up %i.fa.fa-angle-up
= link_to '#down-build-trace', class: 'btn' do = link_to '#down-build-trace', class: 'btn' do
%i.fa.fa-angle-down %i.fa.fa-angle-down
- if @build.erased?
.erased.alert.alert-warning
- erased_by = "by #{link_to @build.erased_by.name, user_path(@build.erased_by)}" if @build.erased_by
Build has been erased #{erased_by.html_safe} #{time_ago_with_tooltip(@build.erased_at)}
- else
%pre.build-trace#build-trace
%code.bash.js-build-output
= icon("refresh spin", class: "js-build-refresh")
- if @build.erased? #down-build-trace
.erased.alert.alert-warning
- erased_by = "by #{link_to @build.erased_by.name, user_path(@build.erased_by)}" if @build.erased_by
Build has been erased #{erased_by.html_safe} #{time_ago_with_tooltip(@build.erased_at)}
- else
%pre.trace#build-trace
%code.bash
= preserve do
= raw trace_with_state[:html]
- if @build.active?
%i{:class => "fa fa-refresh fa-spin"}
%div#down-build-trace
.col-md-3
- if @build.coverage
.build-widget
%h4.title
Test coverage
%h1 #{@build.coverage}%
- if can?(current_user, :read_build, @project) && @build.artifacts?
.build-widget.artifacts
%h4.title Build artifacts
.center
.btn-group{ role: :group }
= link_to download_namespace_project_build_artifacts_path(@project.namespace, @project, @build), class: 'btn btn-sm btn-primary' do
= icon('download')
Download
- if @build.artifacts_metadata?
= link_to browse_namespace_project_build_artifacts_path(@project.namespace, @project, @build), class: 'btn btn-sm btn-primary' do
= icon('folder-open')
Browse
.build-widget.build-controls
%h4.title
Build ##{@build.id}
- if can?(current_user, :update_build, @project)
.center
.btn-group{ role: :group }
- if @build.active?
= link_to "Cancel", cancel_namespace_project_build_path(@project.namespace, @project, @build), class: 'btn btn-sm btn-danger', method: :post
- elsif @build.retryable?
= link_to "Retry", retry_namespace_project_build_path(@project.namespace, @project, @build), class: 'btn btn-sm btn-primary', method: :post
- if @build.erasable?
= link_to erase_namespace_project_build_path(@project.namespace, @project, @build),
class: 'btn btn-sm btn-warning', method: :post,
data: { confirm: 'Are you sure you want to erase this build?' } do
= icon('eraser')
Erase
- if @build.has_trace?
= link_to 'Raw', raw_namespace_project_build_path(@project.namespace, @project, @build),
class: 'btn btn-sm btn-success', target: '_blank'
.clearfix
- if @build.duration
%p
%span.attr-name Duration:
#{duration_in_words(@build.finished_at, @build.started_at)}
%p
%span.attr-name Created:
#{time_ago_with_tooltip(@build.created_at)}
- if @build.finished_at
%p
%span.attr-name Finished:
#{time_ago_with_tooltip(@build.finished_at)}
- if @build.erased_at
%p
%span.attr-name Erased:
#{time_ago_with_tooltip(@build.erased_at)}
%p
%span.attr-name Runner:
- if @build.runner && current_user && current_user.admin
= link_to "##{@build.runner.id}", admin_runner_path(@build.runner.id)
- elsif @build.runner
\##{@build.runner.id}
- if @build.trigger_request
.build-widget
%h4.title
Trigger
%p
%span.attr-name Token:
#{@build.trigger_request.trigger.short_token}
- if @build.trigger_request.variables
%p
%span.attr-name Variables:
%code
- @build.trigger_request.variables.each do |key, value|
#{key}=#{value}
.build-widget
%h4.title
Commit
.pull-right
%small
= link_to @build.pipeline.short_sha, ci_status_path(@build.pipeline), class: "monospace"
%p
%span.attr-name Branch:
= link_to @build.ref, namespace_project_commits_path(@project.namespace, @project, @build.ref)
%p
%span.attr-name Author:
#{@build.pipeline.git_author_name}
%p
%span.attr-name Message:
#{@build.pipeline.git_commit_message}
- if @build.tags.any?
.build-widget
%h4.title
Tags
- @build.tag_list.each do |tag|
%span.label.label-primary
= tag
- if @builds.present?
.build-widget
%h4.title #{pluralize(@builds.count(:id), "other build")} for
= succeed ":" do
= link_to @build.pipeline.short_sha, ci_status_path(@build.pipeline), class: "monospace"
%table.table.builds
- @builds.each_with_index do |build, i|
%tr.build
%td
= ci_icon_for_status(build.status)
%td
= link_to namespace_project_build_path(@project.namespace, @project, build) do
- if build.name
= build.name
- else
%span ##{build.id}
%td.status= build.status
= render "sidebar"
:javascript :javascript
new CiBuild("#{namespace_project_build_url(@project.namespace, @project, @build)}", "#{@build.status}", "#{trace_with_state[:state]}") new CiBuild("#{namespace_project_build_url(@project.namespace, @project, @build)}", "#{@build.status}", "#{trace_with_state[:state]}")
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment