Commit 4934cabb authored by Robert Speicher's avatar Robert Speicher

Merge branch 'master' into 8-10-stable

parents 836bcabd 7cb51ba5
...@@ -5,7 +5,7 @@ v 8.10.0 (unreleased) ...@@ -5,7 +5,7 @@ v 8.10.0 (unreleased)
- Fix projects dropdown loading performance with a simplified api cal. !5113 (tiagonbotelho) - Fix projects dropdown loading performance with a simplified api cal. !5113 (tiagonbotelho)
- Fix commit builds API, return all builds for all pipelines for given commit. !4849 - Fix commit builds API, return all builds for all pipelines for given commit. !4849
- Replace Haml with Hamlit to make view rendering faster. !3666 - Replace Haml with Hamlit to make view rendering faster. !3666
- Expire the branch cache after `git gc` runs - Refresh the branch cache after `git gc` runs
- Refactor repository paths handling to allow multiple git mount points - Refactor repository paths handling to allow multiple git mount points
- Optimize system note visibility checking by memoizing the visible reference count !5070 - Optimize system note visibility checking by memoizing the visible reference count !5070
- Add Application Setting to configure default Repository Path for new projects - Add Application Setting to configure default Repository Path for new projects
...@@ -14,9 +14,11 @@ v 8.10.0 (unreleased) ...@@ -14,9 +14,11 @@ v 8.10.0 (unreleased)
- Wrap code blocks on Activies and Todos page. !4783 (winniehell) - Wrap code blocks on Activies and Todos page. !4783 (winniehell)
- Align flash messages with left side of page content !4959 (winniehell) - Align flash messages with left side of page content !4959 (winniehell)
- Display tooltip for "Copy to Clipboard" button !5164 (winniehell) - Display tooltip for "Copy to Clipboard" button !5164 (winniehell)
- Use default cursor for table header of project files !5165 (winniehell)
- Display last commit of deleted branch in push events !4699 (winniehell) - Display last commit of deleted branch in push events !4699 (winniehell)
- Escape file extension when parsing search results !5141 (winniehell) - Escape file extension when parsing search results !5141 (winniehell)
- Apply the trusted_proxies config to the rack request object for use with rack_attack - Apply the trusted_proxies config to the rack request object for use with rack_attack
- Upgrade to Rails 4.2.7. !5236
- Add Sidekiq queue duration to transaction metrics. - Add Sidekiq queue duration to transaction metrics.
- Add a new column `artifacts_size` to table `ci_builds` !4964 - Add a new column `artifacts_size` to table `ci_builds` !4964
- Let Workhorse serve format-patch diffs - Let Workhorse serve format-patch diffs
...@@ -28,9 +30,12 @@ v 8.10.0 (unreleased) ...@@ -28,9 +30,12 @@ v 8.10.0 (unreleased)
- Add Spring EmojiOne updates. - Add Spring EmojiOne updates.
- Add syntax for multiline blockquote using `>>>` fence !3954 - Add syntax for multiline blockquote using `>>>` fence !3954
- Fix viewing notification settings when a project is pending deletion - Fix viewing notification settings when a project is pending deletion
- Updated compare dropdown menus to use GL dropdown
- Eager load award emoji on notes
- Fix pagination when sorting by columns with lots of ties (like priority) - Fix pagination when sorting by columns with lots of ties (like priority)
- The Markdown reference parsers now re-use query results to prevent running the same queries multiple times !5020 - The Markdown reference parsers now re-use query results to prevent running the same queries multiple times !5020
- Updated project header design - Updated project header design
- Issuable collapsed assignee tooltip is now the users name
- Exclude email check from the standard health check - Exclude email check from the standard health check
- Updated layout for Projects, Groups, Users on Admin area !4424 - Updated layout for Projects, Groups, Users on Admin area !4424
- Fix changing issue state columns in milestone view - Fix changing issue state columns in milestone view
...@@ -78,7 +83,6 @@ v 8.10.0 (unreleased) ...@@ -78,7 +83,6 @@ v 8.10.0 (unreleased)
- Fix importer for GitHub Pull Requests when a branch was reused across Pull Requests - Fix importer for GitHub Pull Requests when a branch was reused across Pull Requests
- Add date when user joined the team on the member page - Add date when user joined the team on the member page
- Fix 404 redirect after validation fails importing a GitLab project - Fix 404 redirect after validation fails importing a GitLab project
- Fix 404 redirect after validation fails importing a GitLab project
- Added setting to set new users by default as external !4545 (Dravere) - Added setting to set new users by default as external !4545 (Dravere)
- Add min value for project limit field on user's form !3622 (jastkand) - Add min value for project limit field on user's form !3622 (jastkand)
- Reset project pushes_since_gc when we enqueue the git gc call - Reset project pushes_since_gc when we enqueue the git gc call
...@@ -89,6 +93,8 @@ v 8.10.0 (unreleased) ...@@ -89,6 +93,8 @@ v 8.10.0 (unreleased)
- Optimistic locking for Issues and Merge Requests (Title and description overriding prevention) - Optimistic locking for Issues and Merge Requests (Title and description overriding prevention)
- Redesign Builds and Pipelines pages - Redesign Builds and Pipelines pages
- Change status color and icon for running builds - Change status color and icon for running builds
- Fix markdown rendering for: consecutive labels references, label references that begin with a digit or contains `.`
- Fix last update timestamp on issues not preserved on gitlab.com and project imports
v 8.9.6 v 8.9.6
- Fix importing of events under notes for GitLab projects. !5154 - Fix importing of events under notes for GitLab projects. !5154
...@@ -98,7 +104,10 @@ v 8.9.6 ...@@ -98,7 +104,10 @@ v 8.9.6
- Overwrite Host and X-Forwarded-Host headers in NGINX !5213 - Overwrite Host and X-Forwarded-Host headers in NGINX !5213
- Keeps issue number when importing from Gitlab.com - Keeps issue number when importing from Gitlab.com
v 8.9.6 (unreleased) v 8.9.7 (unreleased)
- Fix import_data wrongly saved as a result of an invalid import_url
v 8.9.6
- Fix importing of events under notes for GitLab projects - Fix importing of events under notes for GitLab projects
v 8.9.5 v 8.9.5
...@@ -260,6 +269,7 @@ v 8.9.0 ...@@ -260,6 +269,7 @@ v 8.9.0
- 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)
- Fix race condition on merge when build succeeds - Fix race condition on merge when build succeeds
- Added shortcut to focus filter search fields and added documentation #18120
- Links from a wiki page to other wiki pages should be rewritten as expected - 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) - Add option to project to only allow merge requests to be merged if the build succeeds (Rui Santos)
- Added navigation shortcuts to the project pipelines, milestones, builds and forks page. !4393 - Added navigation shortcuts to the project pipelines, milestones, builds and forks page. !4393
...@@ -2221,8 +2231,6 @@ v 7.7.0 ...@@ -2221,8 +2231,6 @@ v 7.7.0
- Fixes for edit comments: drag-n-drop images, preview mode, selecting images, save & update - Fixes for edit comments: drag-n-drop images, preview mode, selecting images, save & update
- Remove password strength indicator - Remove password strength indicator
v 7.6.0 v 7.6.0
- Fork repository to groups - Fork repository to groups
- New rugged version - New rugged version
......
...@@ -145,7 +145,8 @@ might be edited to make them small and simple. ...@@ -145,7 +145,8 @@ might be edited to make them small and simple.
You are encouraged to use the template below for feature proposals. You are encouraged to use the template below for feature proposals.
``` ```
## Description including problem, use cases, benefits, and/or goals ## Description
Include problem, use cases, benefits, and/or goals
## Proposal ## Proposal
......
source 'https://rubygems.org' source 'https://rubygems.org'
gem 'rails', '4.2.6' gem 'rails', '4.2.7'
gem 'rails-deprecated_sanitizer', '~> 1.0.3' gem 'rails-deprecated_sanitizer', '~> 1.0.3'
# Responders respond_to and respond_with # Responders respond_to and respond_with
......
...@@ -3,34 +3,34 @@ GEM ...@@ -3,34 +3,34 @@ GEM
specs: specs:
RedCloth (4.3.2) RedCloth (4.3.2)
ace-rails-ap (4.0.2) ace-rails-ap (4.0.2)
actionmailer (4.2.6) actionmailer (4.2.7)
actionpack (= 4.2.6) actionpack (= 4.2.7)
actionview (= 4.2.6) actionview (= 4.2.7)
activejob (= 4.2.6) activejob (= 4.2.7)
mail (~> 2.5, >= 2.5.4) mail (~> 2.5, >= 2.5.4)
rails-dom-testing (~> 1.0, >= 1.0.5) rails-dom-testing (~> 1.0, >= 1.0.5)
actionpack (4.2.6) actionpack (4.2.7)
actionview (= 4.2.6) actionview (= 4.2.7)
activesupport (= 4.2.6) activesupport (= 4.2.7)
rack (~> 1.6) rack (~> 1.6)
rack-test (~> 0.6.2) rack-test (~> 0.6.2)
rails-dom-testing (~> 1.0, >= 1.0.5) rails-dom-testing (~> 1.0, >= 1.0.5)
rails-html-sanitizer (~> 1.0, >= 1.0.2) rails-html-sanitizer (~> 1.0, >= 1.0.2)
actionview (4.2.6) actionview (4.2.7)
activesupport (= 4.2.6) activesupport (= 4.2.7)
builder (~> 3.1) builder (~> 3.1)
erubis (~> 2.7.0) erubis (~> 2.7.0)
rails-dom-testing (~> 1.0, >= 1.0.5) rails-dom-testing (~> 1.0, >= 1.0.5)
rails-html-sanitizer (~> 1.0, >= 1.0.2) rails-html-sanitizer (~> 1.0, >= 1.0.2)
activejob (4.2.6) activejob (4.2.7)
activesupport (= 4.2.6) activesupport (= 4.2.7)
globalid (>= 0.3.0) globalid (>= 0.3.0)
activemodel (4.2.6) activemodel (4.2.7)
activesupport (= 4.2.6) activesupport (= 4.2.7)
builder (~> 3.1) builder (~> 3.1)
activerecord (4.2.6) activerecord (4.2.7)
activemodel (= 4.2.6) activemodel (= 4.2.7)
activesupport (= 4.2.6) activesupport (= 4.2.7)
arel (~> 6.0) arel (~> 6.0)
activerecord-session_store (1.0.0) activerecord-session_store (1.0.0)
actionpack (>= 4.0, < 5.1) actionpack (>= 4.0, < 5.1)
...@@ -38,7 +38,7 @@ GEM ...@@ -38,7 +38,7 @@ GEM
multi_json (~> 1.11, >= 1.11.2) multi_json (~> 1.11, >= 1.11.2)
rack (>= 1.5.2, < 3) rack (>= 1.5.2, < 3)
railties (>= 4.0, < 5.1) railties (>= 4.0, < 5.1)
activesupport (4.2.6) activesupport (4.2.7)
i18n (~> 0.7) i18n (~> 0.7)
json (~> 1.7, >= 1.7.7) json (~> 1.7, >= 1.7.7)
minitest (~> 5.1) minitest (~> 5.1)
...@@ -515,16 +515,16 @@ GEM ...@@ -515,16 +515,16 @@ GEM
rack rack
rack-test (0.6.3) rack-test (0.6.3)
rack (>= 1.0) rack (>= 1.0)
rails (4.2.6) rails (4.2.7)
actionmailer (= 4.2.6) actionmailer (= 4.2.7)
actionpack (= 4.2.6) actionpack (= 4.2.7)
actionview (= 4.2.6) actionview (= 4.2.7)
activejob (= 4.2.6) activejob (= 4.2.7)
activemodel (= 4.2.6) activemodel (= 4.2.7)
activerecord (= 4.2.6) activerecord (= 4.2.7)
activesupport (= 4.2.6) activesupport (= 4.2.7)
bundler (>= 1.3.0, < 2.0) bundler (>= 1.3.0, < 2.0)
railties (= 4.2.6) railties (= 4.2.7)
sprockets-rails sprockets-rails
rails-deprecated_sanitizer (1.0.3) rails-deprecated_sanitizer (1.0.3)
activesupport (>= 4.2.0.alpha) activesupport (>= 4.2.0.alpha)
...@@ -534,9 +534,9 @@ GEM ...@@ -534,9 +534,9 @@ GEM
rails-deprecated_sanitizer (>= 1.0.1) rails-deprecated_sanitizer (>= 1.0.1)
rails-html-sanitizer (1.0.3) rails-html-sanitizer (1.0.3)
loofah (~> 2.0) loofah (~> 2.0)
railties (4.2.6) railties (4.2.7)
actionpack (= 4.2.6) actionpack (= 4.2.7)
activesupport (= 4.2.6) activesupport (= 4.2.7)
rake (>= 0.8.7) rake (>= 0.8.7)
thor (>= 0.18.1, < 2.0) thor (>= 0.18.1, < 2.0)
rainbow (2.1.0) rainbow (2.1.0)
...@@ -697,7 +697,7 @@ GEM ...@@ -697,7 +697,7 @@ GEM
spring (>= 0.9.1) spring (>= 0.9.1)
spring-commands-teaspoon (0.0.2) spring-commands-teaspoon (0.0.2)
spring (>= 0.9.1) spring (>= 0.9.1)
sprockets (3.6.2) sprockets (3.6.3)
concurrent-ruby (~> 1.0) concurrent-ruby (~> 1.0)
rack (> 1, < 3) rack (> 1, < 3)
sprockets-rails (3.1.1) sprockets-rails (3.1.1)
...@@ -920,7 +920,7 @@ DEPENDENCIES ...@@ -920,7 +920,7 @@ DEPENDENCIES
rack-attack (~> 4.3.1) rack-attack (~> 4.3.1)
rack-cors (~> 0.4.0) rack-cors (~> 0.4.0)
rack-oauth2 (~> 1.2.1) rack-oauth2 (~> 1.2.1)
rails (= 4.2.6) rails (= 4.2.7)
rails-deprecated_sanitizer (~> 1.0.3) rails-deprecated_sanitizer (~> 1.0.3)
rainbow (~> 2.1.0) rainbow (~> 2.1.0)
rblineprof (~> 0.3.6) rblineprof (~> 0.3.6)
......
class @CompareAutocomplete
constructor: ->
@initDropdown()
initDropdown: ->
$('.js-compare-dropdown').each ->
$dropdown = $(@)
selected = $dropdown.data('selected')
$dropdown.glDropdown(
data: (term, callback) ->
$.ajax(
url: $dropdown.data('refs-url')
data:
ref: $dropdown.data('ref')
).done (refs) ->
callback(refs)
selectable: true
filterable: true
filterByText: true
fieldName: $dropdown.attr('name')
filterInput: 'input[type="text"]'
renderRow: (ref) ->
if ref.header?
$('<li />')
.addClass('dropdown-header')
.text(ref.header)
else
link = $('<a />')
.attr('href', '#')
.addClass(if ref is selected then 'is-active' else '')
.text(ref)
.attr('data-ref', escape(ref))
$('<li />')
.append(link)
id: (obj, $el) ->
$el.attr('data-ref')
toggleLabel: (obj, $el) ->
$el.text().trim()
)
...@@ -39,6 +39,8 @@ class Dispatcher ...@@ -39,6 +39,8 @@ class Dispatcher
shortcut_handler = new ShortcutsNavigation() shortcut_handler = new ShortcutsNavigation()
new GLForm($('.issue-form')) new GLForm($('.issue-form'))
new IssuableForm($('.issue-form')) new IssuableForm($('.issue-form'))
new LabelsSelect()
new MilestoneSelect()
when 'projects:merge_requests:new', 'projects:merge_requests:edit' when 'projects:merge_requests:new', 'projects:merge_requests:edit'
new Diff() new Diff()
shortcut_handler = new ShortcutsNavigation() shortcut_handler = new ShortcutsNavigation()
...@@ -137,6 +139,8 @@ class Dispatcher ...@@ -137,6 +139,8 @@ class Dispatcher
new Project() new Project()
new ProjectAvatar() new ProjectAvatar()
switch path[1] switch path[1]
when 'compare'
new CompareAutocomplete()
when 'edit' when 'edit'
shortcut_handler = new ShortcutsNavigation() shortcut_handler = new ShortcutsNavigation()
new ProjectNew() new ProjectNew()
......
...@@ -456,6 +456,8 @@ class GitLabDropdown ...@@ -456,6 +456,8 @@ class GitLabDropdown
rowClicked: (el) -> rowClicked: (el) ->
fieldName = @options.fieldName fieldName = @options.fieldName
isInput = $(@el).is('input')
if @renderedData if @renderedData
groupName = el.data('group') groupName = el.data('group')
if groupName if groupName
...@@ -466,10 +468,19 @@ class GitLabDropdown ...@@ -466,10 +468,19 @@ class GitLabDropdown
selectedObject = @renderedData[selectedIndex] selectedObject = @renderedData[selectedIndex]
value = if @options.id then @options.id(selectedObject, el) else selectedObject.id value = if @options.id then @options.id(selectedObject, el) else selectedObject.id
field = @dropdown.parent().find("input[name='#{fieldName}'][value='#{value}']")
if isInput
field = $(@el)
else
field = @dropdown.parent().find("input[name='#{fieldName}'][value='#{value}']")
if el.hasClass(ACTIVE_CLASS) if el.hasClass(ACTIVE_CLASS)
el.removeClass(ACTIVE_CLASS) el.removeClass(ACTIVE_CLASS)
field.remove()
if isInput
field.val('')
else
field.remove()
# Toggle the dropdown label # Toggle the dropdown label
if @options.toggleLabel if @options.toggleLabel
...@@ -490,7 +501,9 @@ class GitLabDropdown ...@@ -490,7 +501,9 @@ class GitLabDropdown
else else
if not @options.multiSelect or el.hasClass('dropdown-clear-active') if not @options.multiSelect or el.hasClass('dropdown-clear-active')
@dropdown.find(".#{ACTIVE_CLASS}").removeClass ACTIVE_CLASS @dropdown.find(".#{ACTIVE_CLASS}").removeClass ACTIVE_CLASS
@dropdown.parent().find("input[name='#{fieldName}']").remove()
unless isInput
@dropdown.parent().find("input[name='#{fieldName}']").remove()
if !value? if !value?
field.remove() field.remove()
...@@ -505,7 +518,9 @@ class GitLabDropdown ...@@ -505,7 +518,9 @@ class GitLabDropdown
if !field.length and fieldName if !field.length and fieldName
@addInput(fieldName, value) @addInput(fieldName, value)
else else
field.val value field
.val value
.trigger 'change'
return selectedObject return selectedObject
......
...@@ -32,13 +32,11 @@ issuable_created = false ...@@ -32,13 +32,11 @@ issuable_created = false
$search = $('#issue_search') $search = $('#issue_search')
$form = $('.js-filter-form') $form = $('.js-filter-form')
$input = $("input[name='#{$search.attr('name')}']", $form) $input = $("input[name='#{$search.attr('name')}']", $form)
if $input.length is 0 if $input.length is 0
$form.append "<input type='hidden' name='#{$search.attr('name')}' value='#{_.escape($search.val())}'/>" $form.append "<input type='hidden' name='#{$search.attr('name')}' value='#{_.escape($search.val())}'/>"
else else
$input.val $search.val() $input.val $search.val()
Issuable.filterResults $form if $search.val() isnt ''
Issuable.filterResults $form
, 500) , 500)
initLabelFilterRemove: -> initLabelFilterRemove: ->
......
...@@ -184,20 +184,22 @@ class @LabelsSelect ...@@ -184,20 +184,22 @@ class @LabelsSelect
.value() .value()
if $dropdown.hasClass 'js-extra-options' if $dropdown.hasClass 'js-extra-options'
if showNo extraData = []
data.unshift(
id: 0
title: 'No Label'
)
if showAny if showAny
data.unshift( extraData.push(
isAny: true isAny: true
title: 'Any Label' title: 'Any Label'
) )
if data.length > 2 if showNo
data.splice 2, 0, 'divider' extraData.push(
id: 0
title: 'No Label'
)
if extraData.length
extraData.push 'divider'
data = extraData.concat(data)
callback data callback data
...@@ -287,6 +289,12 @@ class @LabelsSelect ...@@ -287,6 +289,12 @@ class @LabelsSelect
defaultLabel defaultLabel
fieldName: $dropdown.data('field-name') fieldName: $dropdown.data('field-name')
id: (label) -> id: (label) ->
if $dropdown.hasClass('js-issuable-form-dropdown')
if label.id is 0
return
else
return label.id
if $dropdown.hasClass("js-filter-submit") and not label.isAny? if $dropdown.hasClass("js-filter-submit") and not label.isAny?
label.title label.title
else else
...@@ -300,6 +308,9 @@ class @LabelsSelect ...@@ -300,6 +308,9 @@ class @LabelsSelect
$selectbox.hide() $selectbox.hide()
# display:block overrides the hide-collapse rule # display:block overrides the hide-collapse rule
$value.removeAttr('style') $value.removeAttr('style')
return if $dropdown.hasClass('js-issuable-form-dropdown')
if $dropdown.hasClass 'js-multiselect' if $dropdown.hasClass 'js-multiselect'
if $dropdown.hasClass('js-filter-submit') and (isIssueIndex or isMRIndex) if $dropdown.hasClass('js-filter-submit') and (isIssueIndex or isMRIndex)
selectedLabels = $dropdown selectedLabels = $dropdown
...@@ -321,7 +332,7 @@ class @LabelsSelect ...@@ -321,7 +332,7 @@ class @LabelsSelect
clicked: (label) -> clicked: (label) ->
_this.enableBulkLabelDropdown() _this.enableBulkLabelDropdown()
if $dropdown.hasClass('js-filter-bulk-update') if $dropdown.hasClass('js-filter-bulk-update') or $dropdown.hasClass('js-issuable-form-dropdown')
return return
page = $('body').data 'page' page = $('body').data 'page'
......
...@@ -62,7 +62,7 @@ class @MilestoneSelect ...@@ -62,7 +62,7 @@ class @MilestoneSelect
title: 'Upcoming' title: 'Upcoming'
) )
if extraOptions.length > 2 if extraOptions.length > 0
extraOptions.push 'divider' extraOptions.push 'divider'
callback(extraOptions.concat(data)) callback(extraOptions.concat(data))
......
...@@ -5,13 +5,12 @@ ...@@ -5,13 +5,12 @@
this.initPagination() this.initPagination()
initSearch: -> initSearch: ->
@timer = null projectsListFilter = $('.projects-list-filter')
$(".projects-list-filter").on('keyup', -> debounceFilter = _.debounce ProjectsList.filterResults, 500
clearTimeout(@timer) projectsListFilter.on 'keyup', (e) ->
@timer = setTimeout(ProjectsList.filterResults, 500) debounceFilter() if projectsListFilter.val() isnt ''
)
filterResults: => filterResults: ->
$('.projects-list-holder').fadeTo(250, 0.5) $('.projects-list-holder').fadeTo(250, 0.5)
form = null form = null
......
...@@ -2,9 +2,10 @@ class @Shortcuts ...@@ -2,9 +2,10 @@ class @Shortcuts
constructor: (skipResetBindings) -> constructor: (skipResetBindings) ->
@enabledHelp = [] @enabledHelp = []
Mousetrap.reset() if not skipResetBindings Mousetrap.reset() if not skipResetBindings
Mousetrap.bind('?', @onToggleHelp) Mousetrap.bind '?', @onToggleHelp
Mousetrap.bind('s', Shortcuts.focusSearch) Mousetrap.bind 's', Shortcuts.focusSearch
Mousetrap.bind(['ctrl+shift+p', 'command+shift+p'], @toggleMarkdownPreview) Mousetrap.bind 'f', (e) => @focusFilter e
Mousetrap.bind ['ctrl+shift+p', 'command+shift+p'], @toggleMarkdownPreview
Mousetrap.bind('t', -> Turbolinks.visit(findFileURL)) if findFileURL? Mousetrap.bind('t', -> Turbolinks.visit(findFileURL)) if findFileURL?
onToggleHelp: (e) => onToggleHelp: (e) =>
...@@ -32,10 +33,16 @@ class @Shortcuts ...@@ -32,10 +33,16 @@ class @Shortcuts
$('.js-more-help-button').remove() $('.js-more-help-button').remove()
) )
focusFilter: (e) ->
@filterInput ?= $('input[type=search]', '.nav-controls')
@filterInput.focus()
e.preventDefault()
@focusSearch: (e) -> @focusSearch: (e) ->
$('#search').focus() $('#search').focus()
e.preventDefault() e.preventDefault()
$(document).on 'click.more_help', '.js-more-help-button', (e) -> $(document).on 'click.more_help', '.js-more-help-button', (e) ->
$(@).remove() $(@).remove()
$('.hidden-shortcut').show() $('.hidden-shortcut').show()
......
...@@ -56,6 +56,11 @@ class @UsersSelect ...@@ -56,6 +56,11 @@ class @UsersSelect
username: '' username: ''
avatar: '' avatar: ''
$value.html(assigneeTemplate(user)) $value.html(assigneeTemplate(user))
$collapsedSidebar
.attr('title', user.name)
.tooltip('fixTitle')
$collapsedSidebar.html(collapsedAssigneeTemplate(user)) $collapsedSidebar.html(collapsedAssigneeTemplate(user))
...@@ -63,7 +68,6 @@ class @UsersSelect ...@@ -63,7 +68,6 @@ class @UsersSelect
'<% if( avatar ) { %> '<% if( avatar ) { %>
<a class="author_link" href="/u/<%- username %>"> <a class="author_link" href="/u/<%- username %>">
<img width="24" class="avatar avatar-inline s24" alt="" src="<%- avatar %>"> <img width="24" class="avatar avatar-inline s24" alt="" src="<%- avatar %>">
<span class="author">Toni Boehm</span>
</a> </a>
<% } else { %> <% } else { %>
<i class="fa fa-user"></i> <i class="fa fa-user"></i>
...@@ -151,11 +155,13 @@ class @UsersSelect ...@@ -151,11 +155,13 @@ class @UsersSelect
# display:block overrides the hide-collapse rule # display:block overrides the hide-collapse rule
$value.css('display', '') $value.css('display', '')
clicked: (user) -> clicked: (user, $el, e) ->
page = $('body').data 'page' page = $('body').data 'page'
isIssueIndex = page is 'projects:issues:index' isIssueIndex = page is 'projects:issues:index'
isMRIndex = page is page is 'projects:merge_requests:index' isMRIndex = page is page is 'projects:merge_requests:index'
if $dropdown.hasClass('js-filter-bulk-update') if $dropdown.hasClass('js-filter-bulk-update') or $dropdown.hasClass('js-issuable-form-dropdown')
e.preventDefault()
selectedId = user.id
return return
if $dropdown.hasClass('js-filter-submit') and (isIssueIndex or isMRIndex) if $dropdown.hasClass('js-filter-submit') and (isIssueIndex or isMRIndex)
...@@ -168,7 +174,8 @@ class @UsersSelect ...@@ -168,7 +174,8 @@ class @UsersSelect
.closest('.selectbox') .closest('.selectbox')
.find("input[name='#{$dropdown.data('field-name')}']").val() .find("input[name='#{$dropdown.data('field-name')}']").val()
assignTo(selected) assignTo(selected)
id: (user) ->
user.id
renderRow: (user) -> renderRow: (user) ->
username = if user.username then "@#{user.username}" else "" username = if user.username then "@#{user.username}" else ""
avatar = if user.avatar_url then user.avatar_url else false avatar = if user.avatar_url then user.avatar_url else false
......
...@@ -20,9 +20,12 @@ ...@@ -20,9 +20,12 @@
.blank-state-icon { .blank-state-icon {
padding-bottom: 20px; padding-bottom: 20px;
color: $gray-darkest;
font-size: 56px;
path { path,
fill: $gray-darkest; polygon {
fill: currentColor;
} }
} }
...@@ -37,6 +40,10 @@ ...@@ -37,6 +40,10 @@
margin-top: 0; margin-top: 0;
margin-bottom: $gl-padding; margin-bottom: $gl-padding;
font-size: 15px; font-size: 15px;
> strong {
font-weight: 600;
}
} }
.blank-state-welcome-title { .blank-state-welcome-title {
......
...@@ -16,8 +16,14 @@ ...@@ -16,8 +16,14 @@
font-weight: normal; font-weight: normal;
font-size: 16px; font-size: 16px;
line-height: 36px; line-height: 36px;
&.diff-collapsed { &.diff-collapsed {
padding: 5px;
cursor: pointer; cursor: pointer;
&:hover {
background-color: $row-hover;
}
} }
} }
......
...@@ -270,21 +270,6 @@ table { ...@@ -270,21 +270,6 @@ table {
} }
} }
.dashboard-intro-icon {
float: left;
text-align: center;
font-size: 32px;
color: #aaa;
width: 60px;
}
.dashboard-intro-text {
display: inline-block;
margin-left: -60px;
padding-left: 60px;
width: 100%;
}
.btn-sign-in { .btn-sign-in {
text-shadow: none; text-shadow: none;
......
...@@ -56,7 +56,7 @@ ...@@ -56,7 +56,7 @@
position: absolute; position: absolute;
top: 50%; top: 50%;
right: 6px; right: 6px;
margin-top: -4px; margin-top: -6px;
color: $dropdown-toggle-icon-color; color: $dropdown-toggle-icon-color;
font-size: 10px; font-size: 10px;
} }
......
...@@ -55,10 +55,6 @@ ...@@ -55,10 +55,6 @@
overflow-y: auto; overflow-y: auto;
overflow-x: hidden; overflow-x: hidden;
@media (min-width: $sidebar-breakpoint) {
bottom: 50px;
}
&.navbar-collapse { &.navbar-collapse {
padding: 0 !important; padding: 0 !important;
} }
...@@ -76,7 +72,7 @@ ...@@ -76,7 +72,7 @@
} }
a { a {
padding: 7px 16px; padding: 7px $gl-sidebar-padding;
font-size: $gl-font-size; font-size: $gl-font-size;
line-height: 24px; line-height: 24px;
display: block; display: block;
...@@ -143,7 +139,7 @@ ...@@ -143,7 +139,7 @@
} }
.nav-header-btn { .nav-header-btn {
padding: 10px 16px; padding: 10px $gl-sidebar-padding;
color: inherit; color: inherit;
transition-duration: .3s; transition-duration: .3s;
position: absolute; position: absolute;
......
...@@ -64,6 +64,7 @@ $gl-btn-padding: 10px; ...@@ -64,6 +64,7 @@ $gl-btn-padding: 10px;
$gl-input-padding: 10px; $gl-input-padding: 10px;
$gl-vert-padding: 6px; $gl-vert-padding: 6px;
$gl-padding-top: 10px; $gl-padding-top: 10px;
$gl-sidebar-padding: 22px;
/* /*
* Misc * Misc
......
...@@ -88,13 +88,7 @@ ...@@ -88,13 +88,7 @@
.user-name { .user-name {
display: inline-block; display: inline-block;
font-weight: bold; font-weight: 600;
}
.controls {
> .btn, > .dropdown {
margin-left: 5px;
}
} }
.dropdown { .dropdown {
......
...@@ -38,33 +38,6 @@ ...@@ -38,33 +38,6 @@
margin-right: 15px; margin-right: 15px;
} }
} }
&.group-admin {
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
.group-avatar, .group-details, .group-controls {
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
}
.group-details {
flex: 1 1 auto;
flex-direction: column;
min-width: 0;
}
.group-controls {
align-items: center;
a {
margin-left: 5px;
}
}
}
} }
.ldap-group-links { .ldap-group-links {
......
...@@ -324,6 +324,10 @@ ...@@ -324,6 +324,10 @@
.issuable-form-select-holder { .issuable-form-select-holder {
display: inline-block; display: inline-block;
width: 250px; width: 250px;
.dropdown-menu-toggle {
width: 100%;
}
} }
.table-holder { .table-holder {
......
...@@ -43,6 +43,13 @@ ...@@ -43,6 +43,13 @@
border-top-width: 1px; border-top-width: 1px;
} }
.commit-link {
a:hover {
text-decoration: none;
}
}
.branch-commit { .branch-commit {
.branch-name { .branch-name {
...@@ -80,7 +87,12 @@ ...@@ -80,7 +87,12 @@
margin-left: 0; margin-left: 0;
} }
.label {
margin-right: 4px;
}
.label-container { .label-container {
font-size: 0;
.label { .label {
margin-top: 5px; margin-top: 5px;
...@@ -116,6 +128,12 @@ ...@@ -116,6 +128,12 @@
color: $table-text-gray; color: $table-text-gray;
} }
.cancel-retry-btns {
.btn:not(:first-child) {
margin-left: 8px;
}
}
.dropdown-toggle, .dropdown-toggle,
.dropdown-menu { .dropdown-menu {
color: $table-text-gray; color: $table-text-gray;
......
...@@ -482,6 +482,10 @@ pre.light-well { ...@@ -482,6 +482,10 @@ pre.light-well {
a:hover { a:hover {
text-decoration: none; text-decoration: none;
} }
> span {
margin-left: 10px;
}
} }
} }
...@@ -639,3 +643,9 @@ pre.light-well { ...@@ -639,3 +643,9 @@ pre.light-well {
width: 300px; width: 300px;
} }
} }
.compare-form-group {
.dropdown-menu {
width: 300px;
}
}
...@@ -185,7 +185,7 @@ ...@@ -185,7 +185,7 @@
padding-right: $gl-padding + 15px; padding-right: $gl-padding + 15px;
} }
.btn-search { .btn-search, .btn-new {
width: 100%; width: 100%;
margin-top: 5px; margin-top: 5px;
......
...@@ -23,12 +23,11 @@ ...@@ -23,12 +23,11 @@
} }
&:hover { &:hover {
cursor: pointer;
td { td {
background-color: $row-hover; background-color: $row-hover;
border-top: 1px solid $row-hover-border; border-top: 1px solid $row-hover-border;
border-bottom: 1px solid $row-hover-border; border-bottom: 1px solid $row-hover-border;
cursor: pointer;
} }
} }
......
...@@ -9,7 +9,7 @@ module IssuablesHelper ...@@ -9,7 +9,7 @@ module IssuablesHelper
def multi_label_name(current_labels, default_label) def multi_label_name(current_labels, default_label)
# current_labels may be a string from before # current_labels may be a string from before
if current_labels.is_a?(Array) if current_labels.is_a?(Array) && current_labels.any?
if current_labels.count > 1 if current_labels.count > 1
"#{current_labels[0]} +#{current_labels.count - 1} more" "#{current_labels[0]} +#{current_labels.count - 1} more"
else else
...@@ -61,7 +61,7 @@ module IssuablesHelper ...@@ -61,7 +61,7 @@ module IssuablesHelper
output = content_tag :strong, "#{text} #{issuable.to_reference}", class: "identifier" output = content_tag :strong, "#{text} #{issuable.to_reference}", class: "identifier"
output << " opened #{time_ago_with_tooltip(issuable.created_at)} by ".html_safe output << " opened #{time_ago_with_tooltip(issuable.created_at)} by ".html_safe
output << content_tag(:strong) do output << content_tag(:strong) do
author_output = link_to_member(project, issuable.author, size: 24, mobile_classes: "hidden-xs") author_output = link_to_member(project, issuable.author, size: 24, mobile_classes: "hidden-xs", tooltip: true)
author_output << link_to_member(project, issuable.author, size: 24, by_username: true, avatar: false, mobile_classes: "hidden-sm hidden-md hidden-lg") author_output << link_to_member(project, issuable.author, size: 24, by_username: true, avatar: false, mobile_classes: "hidden-sm hidden-md hidden-lg")
end end
end end
......
...@@ -19,7 +19,7 @@ module ProjectsHelper ...@@ -19,7 +19,7 @@ module ProjectsHelper
end end
def link_to_member(project, author, opts = {}, &block) def link_to_member(project, author, opts = {}, &block)
default_opts = { avatar: true, name: true, size: 16, author_class: 'author', title: ":name" } default_opts = { avatar: true, name: true, size: 16, author_class: 'author', title: ":name", tooltip: false }
opts = default_opts.merge(opts) opts = default_opts.merge(opts)
return "(deleted)" unless author return "(deleted)" unless author
...@@ -33,7 +33,8 @@ module ProjectsHelper ...@@ -33,7 +33,8 @@ module ProjectsHelper
if opts[:by_username] if opts[:by_username]
author_html << content_tag(:span, sanitize("@#{author.username}"), class: opts[:author_class]) if opts[:name] author_html << content_tag(:span, sanitize("@#{author.username}"), class: opts[:author_class]) if opts[:name]
else else
author_html << content_tag(:span, sanitize(author.name), class: opts[:author_class]) if opts[:name] tooltip_data = { placement: 'top' }
author_html << content_tag(:span, sanitize(author.name), class: [opts[:author_class], ('has-tooltip' if opts[:tooltip])], title: (author.to_reference if opts[:tooltip]), data: (tooltip_data if opts[:tooltip])) if opts[:name]
end end
author_html << capture(&block) if block author_html << capture(&block) if block
...@@ -60,7 +61,7 @@ module ProjectsHelper ...@@ -60,7 +61,7 @@ module ProjectsHelper
project_link = link_to simple_sanitize(project.name), project_path(project), { class: "project-item-select-holder" } project_link = link_to simple_sanitize(project.name), project_path(project), { class: "project-item-select-holder" }
if current_user if current_user
project_link << icon("chevron-down", class: "dropdown-toggle-caret js-projects-dropdown-toggle", data: { target: ".js-dropdown-menu-projects", toggle: "dropdown" }) project_link << icon("chevron-down", class: "dropdown-toggle-caret js-projects-dropdown-toggle", aria: { label: "Toggle switch project dropdown" }, data: { target: ".js-dropdown-menu-projects", toggle: "dropdown" })
end end
full_title = "#{namespace_link} / #{project_link}".html_safe full_title = "#{namespace_link} / #{project_link}".html_safe
......
...@@ -45,7 +45,7 @@ module SearchHelper ...@@ -45,7 +45,7 @@ module SearchHelper
[ [
{ category: "Help", label: "API Help", url: help_page_path("api/README") }, { category: "Help", label: "API Help", url: help_page_path("api/README") },
{ category: "Help", label: "Markdown Help", url: help_page_path("markdown/markdown") }, { category: "Help", label: "Markdown Help", url: help_page_path("markdown/markdown") },
{ category: "Help", label: "Permissions Help", url: help_page_path("permissions/permissions") }, { category: "Help", label: "Permissions Help", url: help_page_path("user/permissions") },
{ category: "Help", label: "Public Access Help", url: help_page_path("public_access/public_access") }, { category: "Help", label: "Public Access Help", url: help_page_path("public_access/public_access") },
{ category: "Help", label: "Rake Tasks Help", url: help_page_path("raketasks/README") }, { category: "Help", label: "Rake Tasks Help", url: help_page_path("raketasks/README") },
{ category: "Help", label: "SSH Keys Help", url: help_page_path("ssh/README") }, { category: "Help", label: "SSH Keys Help", url: help_page_path("ssh/README") },
......
...@@ -52,14 +52,17 @@ class Label < ActiveRecord::Base ...@@ -52,14 +52,17 @@ class Label < ActiveRecord::Base
# This pattern supports cross-project references. # This pattern supports cross-project references.
# #
def self.reference_pattern def self.reference_pattern
# NOTE: The id pattern only matches when all characters on the expression
# are digits, so it will match ~2 but not ~2fa because that's probably a
# label name and we want it to be matched as such.
@reference_pattern ||= %r{ @reference_pattern ||= %r{
(#{Project.reference_pattern})? (#{Project.reference_pattern})?
#{Regexp.escape(reference_prefix)} #{Regexp.escape(reference_prefix)}
(?: (?:
(?<label_id>\d+) | # Integer-based label ID, or (?<label_id>\d+(?!\S\w)\b) | # Integer-based label ID, or
(?<label_name> (?<label_name>
[A-Za-z0-9_\-\?&]+ | # String-based single-word label title, or [A-Za-z0-9_\-\?\.&]+ | # String-based single-word label title, or
"[^,]+" # String-based multi-word label surrounded in quotes ".+?" # String-based multi-word label surrounded in quotes
) )
) )
}x }x
......
...@@ -162,7 +162,7 @@ class Project < ActiveRecord::Base ...@@ -162,7 +162,7 @@ class Project < ActiveRecord::Base
validates :namespace, presence: true validates :namespace, presence: true
validates_uniqueness_of :name, scope: :namespace_id validates_uniqueness_of :name, scope: :namespace_id
validates_uniqueness_of :path, scope: :namespace_id validates_uniqueness_of :path, scope: :namespace_id
validates :import_url, addressable_url: true, if: :external_import? validates :import_url, addressable_url: true, if: :import_url
validates :star_count, numericality: { greater_than_or_equal_to: 0 } validates :star_count, numericality: { greater_than_or_equal_to: 0 }
validate :check_limit, on: :create validate :check_limit, on: :create
validate :avatar_type, validate :avatar_type,
...@@ -464,8 +464,8 @@ class Project < ActiveRecord::Base ...@@ -464,8 +464,8 @@ class Project < ActiveRecord::Base
return super(value) unless Gitlab::UrlSanitizer.valid?(value) return super(value) unless Gitlab::UrlSanitizer.valid?(value)
import_url = Gitlab::UrlSanitizer.new(value) import_url = Gitlab::UrlSanitizer.new(value)
create_or_update_import_data(credentials: import_url.credentials)
super(import_url.sanitized_url) super(import_url.sanitized_url)
create_or_update_import_data(credentials: import_url.credentials)
end end
def import_url def import_url
...@@ -477,7 +477,13 @@ class Project < ActiveRecord::Base ...@@ -477,7 +477,13 @@ class Project < ActiveRecord::Base
end end
end end
def valid_import_url?
valid? || errors.messages[:import_url].nil?
end
def create_or_update_import_data(data: nil, credentials: nil) def create_or_update_import_data(data: nil, credentials: nil)
return unless valid_import_url?
project_import_data = import_data || build_import_data project_import_data = import_data || build_import_data
if data if data
project_import_data.data ||= {} project_import_data.data ||= {}
......
...@@ -43,7 +43,7 @@ module Projects ...@@ -43,7 +43,7 @@ module Projects
def import_repository def import_repository
begin begin
gitlab_shell.import_repository(project.repository_storage_path, project.path_with_namespace, project.import_url) gitlab_shell.import_repository(project.repository_storage_path, project.path_with_namespace, project.import_url)
rescue Gitlab::Shell::Error => e rescue => e
raise Error, "Error importing repository #{project.import_url} into #{project.path_with_namespace} - #{e.message}" raise Error, "Error importing repository #{project.import_url} into #{project.path_with_namespace} - #{e.message}"
end end
end end
......
- css_class = '' unless local_assigns[:css_class] - css_class = 'no-description' if group.description.blank?
%li.group-row.group-admin{ class: css_class } %li.group-row{ class: css_class }
.group-avatar .controls
= image_tag group_icon(group), class: 'avatar hidden-xs'
.group-details
.title
= link_to [:admin, group], class: 'group-name' do
= group.name
.group-stats
%span>= pluralize(number_with_delimiter(group.projects.count), 'project')
,
%span= pluralize(number_with_delimiter(group.users.count), 'member')
- if group.description.present?
.description
= markdown(group.description, pipeline: :description)
.group-controls.hidden-xs
= link_to 'Edit', edit_admin_group_path(group), id: "edit_#{dom_id(group)}", class: 'btn' = link_to 'Edit', edit_admin_group_path(group), id: "edit_#{dom_id(group)}", class: 'btn'
= link_to 'Delete', [:admin, group], data: { confirm: "Are you sure you want to remove #{group.name}?" }, method: :delete, class: 'btn btn-remove' = link_to 'Delete', [:admin, group], data: { confirm: "Are you sure you want to remove #{group.name}?" }, method: :delete, class: 'btn btn-remove'
.stats
%span
= icon('bookmark')
= number_with_delimiter(group.projects.count)
%span
= icon('users')
= number_with_delimiter(group.users.count)
%span.visibility-icon.has-tooltip{data: { container: 'body', placement: 'left' }, title: visibility_icon_description(group)}
= visibility_level_icon(group.visibility_level, fw: false)
= image_tag group_icon(group), class: "avatar s40 hidden-xs"
.title
= link_to [:admin, group], class: 'group-name' do
= group.name
- if group.description.present?
.description
= markdown(group.description, pipeline: :description)
...@@ -79,7 +79,7 @@ ...@@ -79,7 +79,7 @@
.panel-body.form-holder .panel-body.form-holder
%p.light %p.light
Read more about project permissions Read more about project permissions
%strong= link_to "here", help_page_path("permissions/permissions"), class: "vlink" %strong= link_to "here", help_page_path("user/permissions"), class: "vlink"
= form_tag members_update_admin_group_path(@group), id: "new_project_member", class: "bulk_import", method: :put do = form_tag members_update_admin_group_path(@group), id: "new_project_member", class: "bulk_import", method: :put do
%div %div
......
...@@ -66,7 +66,7 @@ ...@@ -66,7 +66,7 @@
%ul.projects-list.content-list %ul.projects-list.content-list
- @projects.each_with_index do |project| - @projects.each_with_index do |project|
%li.project-row %li.project-row
.controls.pull-right .controls
- if project.archived - if project.archived
%span.label.label-warning archived %span.label.label-warning archived
%span.label.label-gray %span.label.label-gray
......
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
%span It's you! %span It's you!
.user-email .user-email
= mail_to user.email, user.email = mail_to user.email, user.email
.controls.pull-right .controls
= link_to 'Edit', edit_admin_user_path(user), id: "edit_#{dom_id(user)}", class: 'btn' = link_to 'Edit', edit_admin_user_path(user), id: "edit_#{dom_id(user)}", class: 'btn'
- unless user == current_user - unless user == current_user
.dropdown.inline .dropdown.inline
......
- publicish_project_count = ProjectsFinder.new.execute(current_user).count - publicish_project_count = ProjectsFinder.new.execute(current_user).count
%h3.page-title Welcome to GitLab! .blank-state.blank-state-welcome
%p.light Self hosted Git management application. %h2.blank-state-welcome-title
%hr Welcome to GitLab
%div %p.blank-state-text
.dashboard-intro-icon Code, test, and deploy together
%i.fa.fa-bookmark-o .blank-state
.dashboard-intro-text .blank-state-icon
%p.slead = custom_icon("project", size: 50)
You don't have access to any projects right now. %h3.blank-state-title
%br You don't have access to any projects right now
- if current_user.can_create_project? %p.blank-state-text
You can create up to
%strong= pluralize(number_with_delimiter(current_user.projects_limit), "project") + "."
- else
If you are added to a project, it will be displayed here.
- if current_user.can_create_project? - if current_user.can_create_project?
.link_holder You can create up to
= link_to new_project_path, class: "btn btn-new" do %strong= number_with_delimiter(current_user.projects_limit)
= icon('plus') = succeed "." do
New Project = "project".pluralize(current_user.projects_limit)
- else
If you are added to a project, it will be displayed here.
- if current_user.can_create_project?
= link_to new_project_path, class: "btn btn-new" do
New project
- if current_user.can_create_group? - if current_user.can_create_group?
%hr .blank-state
%div .blank-state-icon
.dashboard-intro-icon = custom_icon("group", size: 50)
%i.fa.fa-users %h3.blank-state-title
.dashboard-intro-text You can create a group for several dependent projects.
%p.slead %p.blank-state-text
You can create a group for several dependent projects. Groups are the best way to manage projects and members.
%br = link_to new_group_path, class: "btn btn-new" do
Groups are the best way to manage projects and members. New group
.link_holder
= link_to new_group_path, class: "btn btn-new" do
%i.fa.fa-plus
New Group
-if publicish_project_count > 0 -if publicish_project_count > 0
%hr .blank-state
%div .blank-state-icon
.dashboard-intro-icon = icon("globe")
%i.fa.fa-globe %h3.blank-state-title
.dashboard-intro-text There are
%p.slead = number_with_delimiter(publicish_project_count)
There are public projects on this server.
%strong= number_with_delimiter(publicish_project_count) %p.blank-state-text
public projects on this server. Public projects are an easy way to allow everyone to have read-only access.
%br = link_to trending_explore_projects_path, class: "btn btn-new" do
Public projects are an easy way to allow everyone to have read-only access. Browse projects
.link_holder
= link_to trending_explore_projects_path, class: "btn btn-new" do
Browse public projects
...@@ -5,7 +5,8 @@ ...@@ -5,7 +5,8 @@
- page_title "Projects" - page_title "Projects"
- header_title "Projects", dashboard_projects_path - header_title "Projects", dashboard_projects_path
= render 'dashboard/projects_head' - if @projects.any? || params[:filter_projects]
= render 'dashboard/projects_head'
- if @last_push - if @last_push
= render "events/event_last_push", event: @last_push = render "events/event_last_push", event: @last_push
......
...@@ -3,4 +3,4 @@ ...@@ -3,4 +3,4 @@
%h3 Access Denied %h3 Access Denied
%hr %hr
%p You are not allowed to access this page. %p You are not allowed to access this page.
%p Read more about project permissions #{link_to "here", help_page_path("permissions/permissions"), class: "vlink"} %p Read more about project permissions #{link_to "here", help_page_path("user/permissions"), class: "vlink"}
...@@ -12,7 +12,7 @@ ...@@ -12,7 +12,7 @@
= select_tag :access_level, options_for_select(GroupMember.access_level_roles, @group_member.access_level), class: "project-access-select select2" = select_tag :access_level, options_for_select(GroupMember.access_level_roles, @group_member.access_level), class: "project-access-select select2"
.help-block .help-block
Read more about role permissions Read more about role permissions
%strong= link_to "here", help_page_path("permissions/permissions"), class: "vlink" %strong= link_to "here", help_page_path("user/permissions"), class: "vlink"
.form-actions .form-actions
= f.submit 'Add users to group', class: "btn btn-create" = f.submit 'Add users to group', class: "btn btn-create"
...@@ -18,6 +18,10 @@ ...@@ -18,6 +18,10 @@
%td.shortcut %td.shortcut
.key s .key s
%td Focus Search %td Focus Search
%tr
%td.shortcut
.key f
%td Focus Filter
%tr %tr
%td.shortcut %td.shortcut
.key ? .key ?
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
= icon('bars') = icon('bars')
= link_to '#', class: "nav-header-btn pin-nav-btn has-tooltip #{'is-active' if pinned_nav?} js-nav-pin", title: pinned_nav? ? "Unpin navigation" : "Pin Navigation", data: {placement: 'right', container: 'body'} do = link_to '#', class: "nav-header-btn pin-nav-btn has-tooltip #{'is-active' if pinned_nav?} js-nav-pin", title: pinned_nav? ? "Unpin navigation" : "Pin Navigation", data: {placement: 'right', container: 'body'} do
%span.sr-only Toggle navigation pinning %span.sr-only Toggle navigation pinning
= icon('thumb-tack') = icon('fw thumb-tack')
- if defined?(sidebar) && sidebar - if defined?(sidebar) && sidebar
= render "layouts/nav/#{sidebar}" = render "layouts/nav/#{sidebar}"
......
%header.navbar.navbar-fixed-top.navbar-gitlab{ class: nav_header_class } %header.navbar.navbar-fixed-top.navbar-gitlab{ class: nav_header_class }
%div{ class: fluid_layout ? "container-fluid" : "container-fluid" } %div{ class: fluid_layout ? "container-fluid" : "container-fluid" }
.header-content .header-content
%button.side-nav-toggle{type: 'button'} %button.side-nav-toggle{ type: 'button', "aria-label" => "Toggle global navigation" }
%span.sr-only Toggle navigation %span.sr-only Toggle navigation
= icon('bars') = icon('bars')
%button.navbar-toggle{type: 'button'} %button.navbar-toggle{type: 'button'}
...@@ -13,25 +13,25 @@ ...@@ -13,25 +13,25 @@
%li.hidden-sm.hidden-xs %li.hidden-sm.hidden-xs
= render 'layouts/search' unless current_controller?(:search) = render 'layouts/search' unless current_controller?(:search)
%li.visible-sm.visible-xs %li.visible-sm.visible-xs
= link_to search_path, title: 'Search', data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do = link_to search_path, title: 'Search', aria: { label: "Search" }, data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
= icon('search') = icon('search')
- if current_user - if current_user
- if session[:impersonator_id] - if session[:impersonator_id]
%li.impersonation %li.impersonation
= link_to admin_impersonation_path, method: :delete, title: 'Stop Impersonation', data: { toggle: 'tooltip', placement: 'bottom', container: 'body' } do = link_to admin_impersonation_path, method: :delete, title: "Stop Impersonation", aria: { label: 'Stop Impersonation' }, data: { toggle: 'tooltip', placement: 'bottom', container: 'body' } do
= icon('user-secret fw') = icon('user-secret fw')
- if current_user.is_admin? - if current_user.is_admin?
%li %li
= link_to admin_root_path, title: 'Admin Area', data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do = link_to admin_root_path, title: 'Admin Area', aria: { label: "Admin Area" }, data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
= icon('wrench fw') = icon('wrench fw')
%li %li
= link_to dashboard_todos_path, title: 'Todos', data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do = link_to dashboard_todos_path, title: 'Todos', aria: { label: "Todos" }, data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
= icon('bell fw') = icon('bell fw')
%span.badge.todos-pending-count{ class: ("hidden" if todos_pending_count == 0) } %span.badge.todos-pending-count{ class: ("hidden" if todos_pending_count == 0) }
= todos_pending_count = todos_pending_count
- if current_user.can_create_project? - if current_user.can_create_project?
%li %li
= link_to new_project_path, title: 'New project', data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do = link_to new_project_path, title: 'New project', aria: { label: "New project" }, data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
= icon('plus fw') = icon('plus fw')
- if Gitlab::Sherlock.enabled? - if Gitlab::Sherlock.enabled?
%li %li
...@@ -45,12 +45,12 @@ ...@@ -45,12 +45,12 @@
.dropdown-menu-nav.dropdown-menu-align-right .dropdown-menu-nav.dropdown-menu-align-right
%ul %ul
%li %li
= link_to "Profile", current_user, class: 'profile-link', data: { user: current_user.username } = link_to "Profile", current_user, class: 'profile-link', aria: { label: "Profile" }, data: { user: current_user.username }
%li %li
= link_to "Profile Settings", profile_path = link_to "Profile Settings", profile_path, aria: { label: "Profile Settings" }
%li.divider %li.divider
%li %li
= link_to "Sign out", destroy_user_session_path, method: :delete, class: "sign-out-link", title: 'Sign out' = link_to "Sign out", destroy_user_session_path, method: :delete, class: "sign-out-link", aria: { label: "Sign out" }
- else - else
%li %li
%div %div
......
...@@ -80,9 +80,10 @@ ...@@ -80,9 +80,10 @@
%span Download '#{build.name}' artifacts %span Download '#{build.name}' artifacts
- if can?(current_user, :update_pipeline, @project) - if can?(current_user, :update_pipeline, @project)
- if pipeline.retryable? .cancel-retry-btns
= link_to retry_namespace_project_pipeline_path(@project.namespace, @project, pipeline.id), class: 'btn has-tooltip', title: "Retry", method: :post do - if pipeline.retryable?
= icon("repeat") = link_to retry_namespace_project_pipeline_path(@project.namespace, @project, pipeline.id), class: 'btn has-tooltip', title: "Retry", method: :post do
- if pipeline.cancelable? = icon("repeat")
= link_to cancel_namespace_project_pipeline_path(@project.namespace, @project, pipeline.id), class: 'btn btn-remove has-tooltip', title: "Cancel", method: :post do - if pipeline.cancelable?
= icon("remove") = link_to cancel_namespace_project_pipeline_path(@project.namespace, @project, pipeline.id), class: 'btn btn-remove has-tooltip', title: "Cancel", method: :post do
= icon("remove")
...@@ -2,15 +2,17 @@ ...@@ -2,15 +2,17 @@
.clearfix .clearfix
- if params[:to] && params[:from] - if params[:to] && params[:from]
= link_to 'switch', {from: params[:to], to: params[:from]}, {class: 'commits-compare-switch has-tooltip', title: 'Switch base of comparison'} = link_to 'switch', {from: params[:to], to: params[:from]}, {class: 'commits-compare-switch has-tooltip', title: 'Switch base of comparison'}
.form-group .form-group.dropdown.compare-form-group.js-compare-from-dropdown
.input-group.inline-input-group .input-group.inline-input-group
%span.input-group-addon from %span.input-group-addon from
= text_field_tag :from, params[:from], class: "form-control", required: true = text_field_tag :from, params[:from], class: "form-control js-compare-dropdown", required: true, data: { refs_url: refs_namespace_project_path(@project.namespace, @project), toggle: "dropdown", target: ".js-compare-from-dropdown", selected: params[:from].presence }
= render "ref_dropdown"
= "..." = "..."
.form-group .form-group.dropdown.compare-form-group.js-compare-to-dropdown
.input-group.inline-input-group .input-group.inline-input-group
%span.input-group-addon to %span.input-group-addon to
= text_field_tag :to, params[:to], class: "form-control", required: true = text_field_tag :to, params[:to], class: "form-control js-compare-dropdown", required: true, data: { refs_url: refs_namespace_project_path(@project.namespace, @project), toggle: "dropdown", target: ".js-compare-to-dropdown", selected: params[:to].presence }
= render "ref_dropdown"
&nbsp; &nbsp;
= button_tag "Compare", class: "btn btn-create commits-compare-btn" = button_tag "Compare", class: "btn btn-create commits-compare-btn"
- if @merge_request.present? - if @merge_request.present?
...@@ -19,11 +21,3 @@ ...@@ -19,11 +21,3 @@
= link_to create_mr_path, class: 'prepend-left-10 btn' do = link_to create_mr_path, class: 'prepend-left-10 btn' do
= icon("plus") = icon("plus")
Create Merge Request Create Merge Request
:javascript
var availableTags = #{@project.repository.ref_names.to_json};
$("#from, #to").autocomplete({
source: availableTags,
minLength: 1
});
.dropdown-menu.dropdown-menu-selectable
= dropdown_title "Select branch/tag"
= dropdown_content
= dropdown_loading
...@@ -154,4 +154,9 @@ ...@@ -154,4 +154,9 @@
$('.import_gitlab_project').attr('disabled',true); $('.import_gitlab_project').attr('disabled',true);
$('.import_gitlab_project').attr('title', 'Project path required.'); $('.import_gitlab_project').attr('title', 'Project path required.');
} }
}) });
$('.import_git').click(function( event ) {
$projectImportUrl = $('#project_import_url')
$projectImportUrl.attr('disabled', !$projectImportUrl.attr('disabled'))
});
\ No newline at end of file
...@@ -12,7 +12,7 @@ ...@@ -12,7 +12,7 @@
= select_tag :access_level, options_for_select(ProjectMember.access_level_roles, @project_member.access_level), class: "project-access-select select2" = select_tag :access_level, options_for_select(ProjectMember.access_level_roles, @project_member.access_level), class: "project-access-select select2"
.help-block .help-block
Read more about role permissions Read more about role permissions
%strong= link_to "here", help_page_path("permissions/permissions"), class: "vlink" %strong= link_to "here", help_page_path("user/permissions"), class: "vlink"
.form-actions .form-actions
= f.submit 'Add users to project', class: "btn btn-create" = f.submit 'Add users to project', class: "btn btn-create"
...@@ -8,10 +8,10 @@ ...@@ -8,10 +8,10 @@
%p.prepend-top-20 %p.prepend-top-20
Protected branches are designed to: Protected branches are designed to:
%ul %ul
%li prevent pushes from everybody except #{link_to "masters", help_page_path("permissions/permissions"), class: "vlink"} %li prevent pushes from everybody except #{link_to "masters", help_page_path("user/permissions"), class: "vlink"}
%li prevent anyone from force pushing to the branch %li prevent anyone from force pushing to the branch
%li prevent anyone from deleting the branch %li prevent anyone from deleting the branch
%p.append-bottom-0 Read more about #{link_to "project permissions", help_page_path("permissions/permissions"), class: "underlined-link"} %p.append-bottom-0 Read more about #{link_to "project permissions", help_page_path("user/permissions"), class: "underlined-link"}
.col-lg-9 .col-lg-9
%h5.prepend-top-0 %h5.prepend-top-0
Protect a branch Protect a branch
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
= f.label :import_url, class: 'control-label' do = f.label :import_url, class: 'control-label' do
%span Git repository URL %span Git repository URL
.col-sm-10 .col-sm-10
= f.text_field :import_url, class: 'form-control', placeholder: 'https://username:password@gitlab.company.com/group/project.git' = f.text_field :import_url, class: 'form-control', placeholder: 'https://username:password@gitlab.company.com/group/project.git', disabled: true
.well.prepend-top-20 .well.prepend-top-20
%ul %ul
......
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <svg width="<%= size %>" height="<%= size %>" viewBox="0 0 16 16">
<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 3.7.2 (28276) - http://www.bohemiancoding.com/sketch -->
<title>Group</title>
<desc>Created with Sketch.</desc>
<defs></defs>
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd"> <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="Group" fill="#303030"> <g id="Group" fill="#303030">
<path d="M15.6667,10.0105 L10.3337,10.0105 C10.1497,10.0105 9.9997,10.1775 9.9997,10.3845 L9.9997,15.6145 C9.9997,15.8215 10.1497,15.9885 10.3337,15.9885 L15.6667,15.9885 C15.8507,15.9885 15.9997,15.8215 15.9997,15.6145 L15.9997,10.3845 C15.9997,10.1775 15.8507,10.0105 15.6667,10.0105 L15.6667,10.0105 L15.6667,10.0105 Z M11.9997,14.0105 L13.9997,14.0105 L13.9997,12.0105 L11.9997,12.0105 L11.9997,14.0105 L11.9997,14.0105 Z" id="Fill-11"></path> <path d="M15.6667,10.0105 L10.3337,10.0105 C10.1497,10.0105 9.9997,10.1775 9.9997,10.3845 L9.9997,15.6145 C9.9997,15.8215 10.1497,15.9885 10.3337,15.9885 L15.6667,15.9885 C15.8507,15.9885 15.9997,15.8215 15.9997,15.6145 L15.9997,10.3845 C15.9997,10.1775 15.8507,10.0105 15.6667,10.0105 L15.6667,10.0105 L15.6667,10.0105 Z M11.9997,14.0105 L13.9997,14.0105 L13.9997,12.0105 L11.9997,12.0105 L11.9997,14.0105 L11.9997,14.0105 Z" id="Fill-11"></path>
...@@ -15,4 +10,4 @@ ...@@ -15,4 +10,4 @@
<path d="M11.6667,6.21724894e-15 L4.3337,6.21724894e-15 C4.1497,6.21724894e-15 3.9997,0.167 3.9997,0.374 L3.9997,6.604 C3.9997,6.811 4.1497,6.978 4.3337,6.978 L11.6667,6.978 C11.8507,6.978 11.9997,6.811 11.9997,6.604 L11.9997,0.374 C11.9997,0.167 11.8507,6.21724894e-15 11.6667,6.21724894e-15 L11.6667,6.21724894e-15 L11.6667,6.21724894e-15 Z M5.9997,5 L9.9997,5 L9.9997,2 L5.9997,2 L5.9997,5 L5.9997,5 Z" id="Fill-14"></path> <path d="M11.6667,6.21724894e-15 L4.3337,6.21724894e-15 C4.1497,6.21724894e-15 3.9997,0.167 3.9997,0.374 L3.9997,6.604 C3.9997,6.811 4.1497,6.978 4.3337,6.978 L11.6667,6.978 C11.8507,6.978 11.9997,6.811 11.9997,6.604 L11.9997,0.374 C11.9997,0.167 11.8507,6.21724894e-15 11.6667,6.21724894e-15 L11.6667,6.21724894e-15 L11.6667,6.21724894e-15 Z M5.9997,5 L9.9997,5 L9.9997,2 L5.9997,2 L5.9997,5 L5.9997,5 Z" id="Fill-14"></path>
</g> </g>
</g> </g>
</svg> </svg>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 3.8.3 (29802) - http://www.bohemiancoding.com/sketch -->
<title>Page 1</title>
<desc>Created with Sketch.</desc>
<defs></defs>
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<path d="M6,6 L12,6 L12,5 L6,5 L6,6 Z M6,8 L12,8 L12,7 L6,7 L6,8 Z M6,10 L12,10 L12,9 L6,9 L6,10 Z M6,12 L12,12 L12,11 L6,11 L6,12 Z M4,6 L5,6 L5,5 L4,5 L4,6 Z M4,8 L5,8 L5,7 L4,7 L4,8 Z M4,10 L5,10 L5,9 L4,9 L4,10 Z M4,12 L5,12 L5,11 L4,11 L4,12 Z M13,3 L10,3 L10,4 L6,4 L6,3 L3,3 L3,13 L13,13 L13,3 Z M2,14 L14,14 L14,2 L2,2 L2,14 Z M1,0 C0.448,0 0,0.448 0,1 L0,15 C0,15.552 0.448,16 1,16 L15,16 C15.552,16 16,15.552 16,15 L16,1 C16,0.448 15.552,0 15,0 L1,0 Z" fill="#7F7E7E"></path>
</g>
</svg>
\ No newline at end of file
<svg width="<%= size %>" height="<%= size %>" viewBox="0 0 16 16">
<path d="M6,6 L12,6 L12,5 L6,5 L6,6 Z M6,8 L12,8 L12,7 L6,7 L6,8 Z M6,10 L12,10 L12,9 L6,9 L6,10 Z M6,12 L12,12 L12,11 L6,11 L6,12 Z M4,6 L5,6 L5,5 L4,5 L4,6 Z M4,8 L5,8 L5,7 L4,7 L4,8 Z M4,10 L5,10 L5,9 L4,9 L4,10 Z M4,12 L5,12 L5,11 L4,11 L4,12 Z M13,3 L10,3 L10,4 L6,4 L6,3 L3,3 L3,13 L13,13 L13,3 Z M2,14 L14,14 L14,2 L2,2 L2,14 Z M1,0 C0.448,0 0,0.448 0,1 L0,15 C0,15.552 0.448,16 1,16 L15,16 C15.552,16 16,15.552 16,15 L16,1 C16,0.448 15.552,0 15,0 L1,0 Z" fill="#7F7E7E" fill-rule="evenodd"></path>
</svg>
...@@ -21,10 +21,10 @@ ...@@ -21,10 +21,10 @@
placeholder: "Search assignee", data: { any_user: "Any Assignee", first_user: (current_user.username if current_user), null_user: true, current_user: true, project_id: (@project.id if @project), selected: params[:assignee_id], field_name: "assignee_id", default_label: "Assignee" } }) placeholder: "Search assignee", data: { any_user: "Any Assignee", first_user: (current_user.username if current_user), null_user: true, current_user: true, project_id: (@project.id if @project), selected: params[:assignee_id], field_name: "assignee_id", default_label: "Assignee" } })
.filter-item.inline.milestone-filter .filter-item.inline.milestone-filter
= render "shared/issuable/milestone_dropdown" = render "shared/issuable/milestone_dropdown", selected: params[:milestone_title], name: :milestone_title, show_any: true, show_upcoming: true
.filter-item.inline.labels-filter .filter-item.inline.labels-filter
= render "shared/issuable/label_dropdown" = render "shared/issuable/label_dropdown", selected: params[:label_name], data_options: { field_name: "label_name[]" }
.pull-right .pull-right
= render 'shared/sort_dropdown' = render 'shared/sort_dropdown'
......
...@@ -59,38 +59,24 @@ ...@@ -59,38 +59,24 @@
= f.label :assignee_id, "Assignee", class: "control-label #{"col-lg-4" if has_due_date}" = f.label :assignee_id, "Assignee", class: "control-label #{"col-lg-4" if has_due_date}"
.col-sm-10{ class: ("col-lg-8" if has_due_date) } .col-sm-10{ class: ("col-lg-8" if has_due_date) }
.issuable-form-select-holder .issuable-form-select-holder
= users_select_tag("#{issuable.class.model_name.param_key}[assignee_id]", - project = @target_project || @project
placeholder: 'Select assignee', class: 'custom-form-control', null_user: true, - if issuable.assignee_id
selected: issuable.assignee_id, project: @target_project || @project, = hidden_field_tag("#{issuable.class.model_name.param_key}[assignee_id]", issuable.assignee_id)
first_user: true, current_user: true, include_blank: true) = dropdown_tag(user_dropdown_label(issuable.assignee_id, "Assignee"), options: { toggle_class: "js-user-search js-issuable-form-dropdown js-assignee-search", title: "Filter by assignee", filter: true, dropdown_class: "dropdown-menu-user dropdown-menu-selectable dropdown-menu-assignee js-filter-submit",
%div placeholder: "Search assignee", data: { first_user: (current_user.username if current_user), null_user: true, current_user: true, project_id: (project.id if project), selected: issuable.assignee_id, field_name: "#{issuable.class.model_name.param_key}[assignee_id]", default_label: "Assignee" } })
= link_to 'Assign to me', '#', class: 'assign-to-me-link prepend-top-5 inline'
.form-group.issue-milestone .form-group.issue-milestone
= f.label :milestone_id, "Milestone", class: "control-label #{"col-lg-4" if has_due_date}" = f.label :milestone_id, "Milestone", class: "control-label #{"col-lg-4" if has_due_date}"
.col-sm-10{ class: ("col-lg-8" if has_due_date) } .col-sm-10{ class: ("col-lg-8" if has_due_date) }
- if milestone_options(issuable).present? .issuable-form-select-holder
.issuable-form-select-holder = render "shared/issuable/milestone_dropdown", selected: issuable.milestone_id, name: "#{issuable.class.model_name.param_key}[milestone_id]", show_any: false, show_upcoming: false
= f.select(:milestone_id, milestone_options(issuable),
{ include_blank: true }, { class: 'select2', data: { placeholder: 'Select milestone' } })
- else
.prepend-top-10
%span.light No open milestones available.
- if can? current_user, :admin_milestone, issuable.project
%div
= link_to 'Create new milestone', new_namespace_project_milestone_path(issuable.project.namespace, issuable.project), target: :blank, class: "prepend-top-5 inline"
.form-group .form-group
- has_labels = issuable.project.labels.any? - has_labels = issuable.project.labels.any?
- selected_labels = issuable.label_ids.any? ? issuable.label_ids : nil
- label_dropdown_toggle = issuable.labels.map { |label| label.title }
= f.label :label_ids, "Labels", class: "control-label #{"col-lg-4" if has_due_date}" = f.label :label_ids, "Labels", class: "control-label #{"col-lg-4" if has_due_date}"
.col-sm-10{ class: "#{"col-lg-8" if has_due_date} #{'issuable-form-padding-top' if !has_labels}" } .col-sm-10{ class: "#{"col-lg-8" if has_due_date} #{'issuable-form-padding-top' if !has_labels}" }
- if has_labels .issuable-form-select-holder
.issuable-form-select-holder = render "shared/issuable/label_dropdown", classes: ["js-issuable-form-dropdown"], selected: selected_labels, selected_toggle: label_dropdown_toggle, data_options: { field_name: "#{issuable.class.model_name.param_key}[label_ids][]", show_any: "false" }
= f.collection_select :label_ids, issuable.project.labels.all, :id, :name,
{ selected: issuable.label_ids }, multiple: true, class: 'select2', data: { placeholder: "Select labels" }
- else
%span.light No labels yet.
- if can? current_user, :admin_label, issuable.project
%div
= link_to 'Create new label', new_namespace_project_label_path(issuable.project.namespace, issuable.project), target: :blank, class: "prepend-top-5 inline"
- if has_due_date - if has_due_date
.col-lg-6 .col-lg-6
.form-group .form-group
......
...@@ -4,19 +4,21 @@ ...@@ -4,19 +4,21 @@
- show_footer = local_assigns.fetch(:show_footer, true) - show_footer = local_assigns.fetch(:show_footer, true)
- data_options = local_assigns.fetch(:data_options, {}) - data_options = local_assigns.fetch(:data_options, {})
- classes = local_assigns.fetch(:classes, []) - classes = local_assigns.fetch(:classes, [])
- dropdown_data = {toggle: 'dropdown', field_name: 'label_name[]', show_no: "true", show_any: "true", selected: params[:label_name], project_id: @project.try(:id), labels: labels_filter_path, default_label: "Label"} - selected = local_assigns.fetch(:selected, nil)
- selected_toggle = local_assigns.fetch(:selected_toggle, nil)
- dropdown_data = {toggle: 'dropdown', field_name: "label_name[]", show_no: "true", show_any: "true", selected: selected, project_id: @project.try(:id), labels: labels_filter_path, default_label: "Label"}
- dropdown_data.merge!(data_options) - dropdown_data.merge!(data_options)
- classes << 'js-extra-options' if extra_options - classes << 'js-extra-options' if extra_options
- classes << 'js-filter-submit' if filter_submit - classes << 'js-filter-submit' if filter_submit
- if params[:label_name].present? - if selected.present?
- if params[:label_name].respond_to?('any?') - if selected.respond_to?('any?')
- params[:label_name].each do |label| - selected.each do |label|
= hidden_field_tag "label_name[]", label, id: nil = hidden_field_tag data_options[:field_name], label, id: nil
.dropdown .dropdown
%button.dropdown-menu-toggle.js-label-select.js-multiselect{class: classes.join(' '), type: "button", data: dropdown_data} %button.dropdown-menu-toggle.js-label-select.js-multiselect{class: classes.join(' '), type: "button", data: dropdown_data}
%span.dropdown-toggle-text %span.dropdown-toggle-text
= h(multi_label_name(params[:label_name], "Label")) = h(multi_label_name(selected_toggle || selected, "Label"))
= icon('chevron-down') = icon('chevron-down')
.dropdown-menu.dropdown-select.dropdown-menu-paging.dropdown-menu-labels.dropdown-menu-selectable .dropdown-menu.dropdown-select.dropdown-menu-paging.dropdown-menu-labels.dropdown-menu-selectable
= render partial: "shared/issuable/label_page_default", locals: { title: "Filter by label", show_footer: show_footer, show_create: show_create } = render partial: "shared/issuable/label_page_default", locals: { title: "Filter by label", show_footer: show_footer, show_create: show_create }
......
- if params[:milestone_title].present? - if selected.present?
= hidden_field_tag(:milestone_title, params[:milestone_title]) = hidden_field_tag(name, selected)
= dropdown_tag(milestone_dropdown_label(params[:milestone_title]), options: { title: "Filter by milestone", toggle_class: 'js-milestone-select js-filter-submit', filter: true, dropdown_class: "dropdown-menu-selectable", = dropdown_tag(milestone_dropdown_label(selected), options: { title: "Filter by milestone", toggle_class: 'js-milestone-select js-filter-submit', filter: true, dropdown_class: "dropdown-menu-selectable",
placeholder: "Search milestones", footer_content: @project.present?, data: { show_no: true, show_any: true, show_upcoming: true, field_name: "milestone_title", selected: params[:milestone_title], project_id: @project.try(:id), milestones: milestones_filter_dropdown_path, default_label: "Milestone" } }) do placeholder: "Search milestones", footer_content: @project.present?, data: { show_no: true, show_any: show_any, show_upcoming: show_upcoming, field_name: name, selected: selected, project_id: @project.try(:id), milestones: milestones_filter_dropdown_path, default_label: "Milestone" } }) do
- if @project - if @project
%ul.dropdown-footer-list %ul.dropdown-footer-list
- if can? current_user, :admin_milestone, @project - if can? current_user, :admin_milestone, @project
......
...@@ -19,7 +19,7 @@ ...@@ -19,7 +19,7 @@
= form_for [@project.namespace.becomes(Namespace), @project, issuable], remote: true, format: :json, html: {class: 'issuable-context-form inline-update js-issuable-update'} do |f| = form_for [@project.namespace.becomes(Namespace), @project, issuable], remote: true, format: :json, html: {class: 'issuable-context-form inline-update js-issuable-update'} do |f|
.block.assignee .block.assignee
.sidebar-collapsed-icon.sidebar-collapsed-user{data: {toggle: "tooltip", placement: "left", container: "body"}, title: (issuable.assignee.to_reference if issuable.assignee)} .sidebar-collapsed-icon.sidebar-collapsed-user{data: {toggle: "tooltip", placement: "left", container: "body"}, title: (issuable.assignee.name if issuable.assignee)}
- if issuable.assignee - if issuable.assignee
= link_to_member(@project, issuable.assignee, size: 24) = link_to_member(@project, issuable.assignee, size: 24)
- else - else
......
...@@ -8,7 +8,9 @@ class GitGarbageCollectWorker ...@@ -8,7 +8,9 @@ class GitGarbageCollectWorker
project = Project.find(project_id) project = Project.find(project_id)
gitlab_shell.gc(project.repository_storage_path, project.path_with_namespace) gitlab_shell.gc(project.repository_storage_path, project.path_with_namespace)
# Expire the branch cache in case garbage collection caused a ref lookup to fail # Refresh the branch cache in case garbage collection caused a ref lookup to fail
project.repository.after_create_branch project.repository.after_create_branch
project.repository.branch_names
project.repository.has_visible_content?
end end
end end
# UI Guide for building GitLab # UI Guide for building GitLab
## GitLab UI development kit ## GitLab UI development kit
We created a page inside GitLab where you can check commonly used html and css elements. We created a page inside GitLab where you can check commonly used html and css elements.
When you run GitLab instance locally - just visit http://localhost:3000/help/ui page to see UI examples When you run GitLab instance locally - just visit http://localhost:3000/help/ui page to see UI examples
you can use during GitLab development. you can use during GitLab development.
## Design repository ## Design repository
All design files are stored in the [gitlab-design](https://gitlab.com/gitlab-org/gitlab-design) All design files are stored in the [gitlab-design](https://gitlab.com/gitlab-org/gitlab-design)
repository and maintained by GitLab UX designers. repository and maintained by GitLab UX designers.
## Navigation ## Navigation
GitLab's layout contains 2 sections: the left sidebar and the content. The left sidebar contains a static navigation menu. GitLab's layout contains 2 sections: the left sidebar and the content. The left sidebar contains a static navigation menu.
This menu will be visible regardless of what page you visit. The left sidebar also contains the GitLab logo This menu will be visible regardless of what page you visit. The left sidebar also contains the GitLab logo
and the current user's profile picture. The content section contains a header and the content itself. and the current user's profile picture. The content section contains a header and the content itself.
The header describes the current GitLab page and what navigation is The header describes the current GitLab page and what navigation is
available to user in this area. Depending on the area (project, group, profile setting) the header name and navigation may change. For example when user visits one of the available to user in this area. Depending on the area (project, group, profile setting) the header name and navigation may change. For example when user visits one of the
project pages the header will contain a project name and navigation for that project. When the user visits a group page it will contain a group name and navigation related to this group. project pages the header will contain a project name and navigation for that project. When the user visits a group page it will contain a group name and navigation related to this group.
### Adding new tab to header navigation ### Adding new tab to header navigation
We try to keep the amount of tabs in the header navigation between 5 and 10 so that it fits on a typical laptop screen. We also try not to confuse the user with too many options. Ideally each We try to keep the amount of tabs in the header navigation between 5 and 10 so that it fits on a typical laptop screen. We also try not to confuse the user with too many options. Ideally each
tab should represent separate functionality. Everything related to the issue tab should represent separate functionality. Everything related to the issue
tracker should be under the 'Issues' tab while everything related to the wiki should tracker should be under the 'Issues' tab while everything related to the wiki should
be under 'Wiki' tab and so on and so forth. be under 'Wiki' tab and so on and so forth.
When adding a new tab to the header don't use more than 2 words for text in the link. When adding a new tab to the header don't use more than 2 words for text in the link.
We want to keep links short and easy to remember and fit all of them in the small screen. We want to keep links short and easy to remember and fit all of them in the small screen.
## Mobile screen size ## Mobile screen size
We want GitLab to work well on small mobile screens as well. Size limitations make it is impossible to fit everything on a mobile screen. In this case it is OK to hide We want GitLab to work well on small mobile screens as well. Size limitations make it is impossible to fit everything on a mobile screen. In this case it is OK to hide
part of the UI for smaller resolutions in favor of a better user experience. part of the UI for smaller resolutions in favor of a better user experience.
However core functionality like browsing files, creating issues, writing comments, should However core functionality like browsing files, creating issues, writing comments, should
be available on all resolutions. be available on all resolutions.
## Icons ## Icons
* `trash` icon for button or link that does destructive action like removing * `trash` icon for button or link that does destructive action like removing
information from database or file system information from database or file system
* `x` icon for closing/hiding UI element. For example close modal window * `x` icon for closing/hiding UI element. For example close modal window
* `pencil` icon for edit button or link * `pencil` icon for edit button or link
...@@ -52,8 +52,14 @@ information from database or file system ...@@ -52,8 +52,14 @@ information from database or file system
* Button should contain icon or text. Exceptions should be approved by UX designer. * Button should contain icon or text. Exceptions should be approved by UX designer.
* Use red button for destructive actions (not revertable). For example removing issue. * Use red button for destructive actions (not revertable). For example removing issue.
* Use green or blue button for primary action. Primary button should be only one. * Use green or blue button for primary action. Primary button should be only one.
Do not use both green and blue button in one form. Do not use both green and blue button in one form.
* For all other cases use default white button. * For all other cases use default white button.
* Text button should have only first word capitalized. So should be "Create issue" instead of "Create Issue" * Text button should have only first word capitalized. So should be "Create issue" instead of "Create Issue"
## Counts
* Always use the [`number_with_delimiter`][number_with_delimiter] helper to
display counts in the UI.
[number_with_delimiter]: http://api.rubyonrails.org/classes/ActionView/Helpers/NumberHelper.html#method-i-number_with_delimiter
...@@ -28,7 +28,8 @@ GitLab supports two ways of adding a new OAuth2 application to an instance. You ...@@ -28,7 +28,8 @@ GitLab supports two ways of adding a new OAuth2 application to an instance. You
can either add an application as a regular user or add it in the admin area. can either add an application as a regular user or add it in the admin area.
What this means is that GitLab can actually have instance-wide and a user-wide What this means is that GitLab can actually have instance-wide and a user-wide
applications. There is no difference between them except for the different applications. There is no difference between them except for the different
permission levels they are set (user/admin). permission levels they are set (user/admin). The default callback URL is
`http://your-gitlab.example.com/users/auth/gitlab/callback`
## Adding an application through the profile ## Adding an application through the profile
......
...@@ -37,6 +37,7 @@ Feature: Project Issues ...@@ -37,6 +37,7 @@ Feature: Project Issues
And I submit new issue "500 error on profile" And I submit new issue "500 error on profile"
Then I should see issue "500 error on profile" Then I should see issue "500 error on profile"
@javascript
Scenario: I submit new unassigned issue with labels Scenario: I submit new unassigned issue with labels
Given project "Shop" has labels: "bug", "feature", "enhancement" Given project "Shop" has labels: "bug", "feature", "enhancement"
And I click link "New Issue" And I click link "New Issue"
......
...@@ -135,19 +135,17 @@ class Spinach::Features::ProjectForkedMergeRequests < Spinach::FeatureSteps ...@@ -135,19 +135,17 @@ class Spinach::Features::ProjectForkedMergeRequests < Spinach::FeatureSteps
end end
step 'I click "Assign to" dropdown"' do step 'I click "Assign to" dropdown"' do
first('.ajax-users-select').click click_button 'Assignee'
end end
step 'I should see the target project ID in the input selector' do step 'I should see the target project ID in the input selector' do
expect(page).to have_selector("input[data-project-id=\"#{@project.id}\"]") expect(find('.js-assignee-search')["data-project-id"]).to eq "#{@project.id}"
end end
step 'I should see the users from the target project ID' do step 'I should see the users from the target project ID' do
expect(page).to have_selector('.user-result', visible: true, count: 3) expect(page).to have_content 'Unassigned'
users = page.all('.user-name') expect(page).to have_content current_user.name
expect(users[0].text).to eq 'Unassigned' expect(page).to have_content @project.users.first.name
expect(users[1].text).to eq current_user.name
expect(users[2].text).to eq @project.users.first.name
end end
# Verify a link is generated against the correct project # Verify a link is generated against the correct project
......
...@@ -82,7 +82,8 @@ class Spinach::Features::ProjectIssues < Spinach::FeatureSteps ...@@ -82,7 +82,8 @@ class Spinach::Features::ProjectIssues < Spinach::FeatureSteps
step 'I submit new issue "500 error on profile" with label \'bug\'' do step 'I submit new issue "500 error on profile" with label \'bug\'' do
fill_in "issue_title", with: "500 error on profile" fill_in "issue_title", with: "500 error on profile"
select 'bug', from: "Labels" click_button "Label"
click_link "bug"
click_button "Submit issue" click_button "Submit issue"
end end
......
...@@ -15,32 +15,35 @@ module Gitlab ...@@ -15,32 +15,35 @@ module Gitlab
end end
def execute def execute
project_identifier = CGI.escape(project.import_source) ActiveRecord::Base.no_touching do
project_identifier = CGI.escape(project.import_source)
# Issues && Comments
issues = client.issues(project_identifier) # Issues && Comments
issues = client.issues(project_identifier)
issues.each do |issue|
body = @formatter.author_line(issue["author"]["name"]) issues.each do |issue|
body += issue["description"] body = @formatter.author_line(issue["author"]["name"])
body += issue["description"]
comments = client.issue_comments(project_identifier, issue["id"])
comments = client.issue_comments(project_identifier, issue["id"])
if comments.any?
body += @formatter.comments_header if comments.any?
body += @formatter.comments_header
end
comments.each do |comment|
body += @formatter.comment(comment["author"]["name"], comment["created_at"], comment["body"])
end
project.issues.create!(
iid: issue["iid"],
description: body,
title: issue["title"],
state: issue["state"],
updated_at: issue["updated_at"],
author_id: gl_user_id(project, issue["author"]["id"])
)
end end
comments.each do |comment|
body += @formatter.comment(comment["author"]["name"], comment["created_at"], comment["body"])
end
project.issues.create!(
iid: issue["iid"],
description: body,
title: issue["title"],
state: issue["state"],
author_id: gl_user_id(project, issue["author"]["id"])
)
end end
true true
......
...@@ -12,7 +12,10 @@ module Gitlab ...@@ -12,7 +12,10 @@ module Gitlab
json = IO.read(@path) json = IO.read(@path)
@tree_hash = ActiveSupport::JSON.decode(json) @tree_hash = ActiveSupport::JSON.decode(json)
@project_members = @tree_hash.delete('project_members') @project_members = @tree_hash.delete('project_members')
create_relations
ActiveRecord::Base.no_touching do
create_relations
end
rescue => e rescue => e
@shared.error(e) @shared.error(e)
false false
......
require "spec_helper"
describe "Compare", js: true do
let(:user) { create(:user) }
let(:project) { create(:project) }
before do
project.team << [user, :master]
login_as user
visit namespace_project_compare_index_path(project.namespace, project, from: "master", to: "master")
end
describe "branches" do
it "should pre-populate fields" do
expect(page.find_field("from").value).to eq("master")
end
it "should compare branches" do
fill_in "from", with: "fea"
find("#from").click
click_link "feature"
expect(page.find_field("from").value).to eq("feature")
click_button "Compare"
expect(page).to have_content "Commits"
end
end
describe "tags" do
it "should compare tags" do
fill_in "from", with: "v1.0"
find("#from").click
click_link "v1.0.0"
expect(page.find_field("from").value).to eq("v1.0.0")
click_button "Compare"
expect(page).to have_content "Commits"
end
end
end
...@@ -55,7 +55,7 @@ feature 'issue move to another project' do ...@@ -55,7 +55,7 @@ feature 'issue move to another project' do
first('.select2-choice').click first('.select2-choice').click
end end
fill_in('s2id_autogen2_search', with: new_project_search.name) fill_in('s2id_autogen1_search', with: new_project_search.name)
page.within '.select2-drop' do page.within '.select2-drop' do
expect(page).to have_content(new_project_search.name) expect(page).to have_content(new_project_search.name)
......
...@@ -50,9 +50,8 @@ describe 'Issues', feature: true do ...@@ -50,9 +50,8 @@ describe 'Issues', feature: true do
expect(page).to have_content "Assignee #{@user.name}" expect(page).to have_content "Assignee #{@user.name}"
first('#s2id_issue_assignee_id').click first('.js-user-search').click
sleep 2 # wait for ajax stuff to complete click_link 'Unassigned'
first('.user-result').click
click_button 'Save changes' click_button 'Save changes'
......
...@@ -93,8 +93,8 @@ describe Banzai::Filter::LabelReferenceFilter, lib: true do ...@@ -93,8 +93,8 @@ describe Banzai::Filter::LabelReferenceFilter, lib: true do
end end
it 'links with adjacent text' do it 'links with adjacent text' do
doc = reference_filter("Label (#{reference}.)") doc = reference_filter("Label (#{reference}).")
expect(doc.to_html).to match(%r(\(<a.+><span.+>#{label.name}</span></a>\.\))) expect(doc.to_html).to match(%r(\(<a.+><span.+>#{label.name}</span></a>\)\.))
end end
it 'ignores invalid label names' do it 'ignores invalid label names' do
...@@ -104,8 +104,32 @@ describe Banzai::Filter::LabelReferenceFilter, lib: true do ...@@ -104,8 +104,32 @@ describe Banzai::Filter::LabelReferenceFilter, lib: true do
end end
end end
context 'String-based single-word references that begin with a digit' do
let(:label) { create(:label, name: '2fa', project: project) }
let(:reference) { "#{Label.reference_prefix}#{label.name}" }
it 'links to a valid reference' do
doc = reference_filter("See #{reference}")
expect(doc.css('a').first.attr('href')).to eq urls.
namespace_project_issues_url(project.namespace, project, label_name: label.name)
expect(doc.text).to eq 'See 2fa'
end
it 'links with adjacent text' do
doc = reference_filter("Label (#{reference}).")
expect(doc.to_html).to match(%r(\(<a.+><span.+>#{label.name}</span></a>\)\.))
end
it 'ignores invalid label names' do
exp = act = "Label #{Label.reference_prefix}#{label.id}#{label.name.reverse}"
expect(reference_filter(act).to_html).to eq exp
end
end
context 'String-based single-word references with special characters' do context 'String-based single-word references with special characters' do
let(:label) { create(:label, name: '?gfm&', project: project) } let(:label) { create(:label, name: '?g.fm&', project: project) }
let(:reference) { "#{Label.reference_prefix}#{label.name}" } let(:reference) { "#{Label.reference_prefix}#{label.name}" }
it 'links to a valid reference' do it 'links to a valid reference' do
...@@ -113,17 +137,17 @@ describe Banzai::Filter::LabelReferenceFilter, lib: true do ...@@ -113,17 +137,17 @@ describe Banzai::Filter::LabelReferenceFilter, lib: true do
expect(doc.css('a').first.attr('href')).to eq urls. expect(doc.css('a').first.attr('href')).to eq urls.
namespace_project_issues_url(project.namespace, project, label_name: label.name) namespace_project_issues_url(project.namespace, project, label_name: label.name)
expect(doc.text).to eq 'See ?gfm&' expect(doc.text).to eq 'See ?g.fm&'
end end
it 'links with adjacent text' do it 'links with adjacent text' do
doc = reference_filter("Label (#{reference}.)") doc = reference_filter("Label (#{reference}).")
expect(doc.to_html).to match(%r(\(<a.+><span.+>\?gfm&amp;</span></a>\.\))) expect(doc.to_html).to match(%r(\(<a.+><span.+>\?g\.fm&amp;</span></a>\)\.))
end end
it 'ignores invalid label names' do it 'ignores invalid label names' do
act = "Label #{Label.reference_prefix}#{label.name.reverse}" act = "Label #{Label.reference_prefix}#{label.name.reverse}"
exp = "Label #{Label.reference_prefix}&amp;mfg?" exp = "Label #{Label.reference_prefix}&amp;mf.g?"
expect(reference_filter(act).to_html).to eq exp expect(reference_filter(act).to_html).to eq exp
end end
...@@ -153,8 +177,32 @@ describe Banzai::Filter::LabelReferenceFilter, lib: true do ...@@ -153,8 +177,32 @@ describe Banzai::Filter::LabelReferenceFilter, lib: true do
end end
end end
context 'String-based multi-word references that begin with a digit' do
let(:label) { create(:label, name: '2 factor authentication', project: project) }
let(:reference) { label.to_reference(format: :name) }
it 'links to a valid reference' do
doc = reference_filter("See #{reference}")
expect(doc.css('a').first.attr('href')).to eq urls.
namespace_project_issues_url(project.namespace, project, label_name: label.name)
expect(doc.text).to eq 'See 2 factor authentication'
end
it 'links with adjacent text' do
doc = reference_filter("Label (#{reference}.)")
expect(doc.to_html).to match(%r(\(<a.+><span.+>#{label.name}</span></a>\.\)))
end
it 'ignores invalid label names' do
exp = act = "Label #{Label.reference_prefix}#{label.id}#{label.name.reverse}"
expect(reference_filter(act).to_html).to eq exp
end
end
context 'String-based multi-word references with special characters in quotes' do context 'String-based multi-word references with special characters in quotes' do
let(:label) { create(:label, name: 'gfm & references?', project: project) } let(:label) { create(:label, name: 'g.fm & references?', project: project) }
let(:reference) { label.to_reference(format: :name) } let(:reference) { label.to_reference(format: :name) }
it 'links to a valid reference' do it 'links to a valid reference' do
...@@ -162,22 +210,62 @@ describe Banzai::Filter::LabelReferenceFilter, lib: true do ...@@ -162,22 +210,62 @@ describe Banzai::Filter::LabelReferenceFilter, lib: true do
expect(doc.css('a').first.attr('href')).to eq urls. expect(doc.css('a').first.attr('href')).to eq urls.
namespace_project_issues_url(project.namespace, project, label_name: label.name) namespace_project_issues_url(project.namespace, project, label_name: label.name)
expect(doc.text).to eq 'See gfm & references?' expect(doc.text).to eq 'See g.fm & references?'
end end
it 'links with adjacent text' do it 'links with adjacent text' do
doc = reference_filter("Label (#{reference}.)") doc = reference_filter("Label (#{reference}.)")
expect(doc.to_html).to match(%r(\(<a.+><span.+>gfm &amp; references\?</span></a>\.\))) expect(doc.to_html).to match(%r(\(<a.+><span.+>g\.fm &amp; references\?</span></a>\.\)))
end end
it 'ignores invalid label names' do it 'ignores invalid label names' do
act = %(Label #{Label.reference_prefix}"#{label.name.reverse}") act = %(Label #{Label.reference_prefix}"#{label.name.reverse}")
exp = %(Label #{Label.reference_prefix}"?secnerefer &amp; mfg\") exp = %(Label #{Label.reference_prefix}"?secnerefer &amp; mf.g\")
expect(reference_filter(act).to_html).to eq exp expect(reference_filter(act).to_html).to eq exp
end end
end end
describe 'consecutive references' do
let(:bug) { create(:label, name: 'bug', project: project) }
let(:feature_proposal) { create(:label, name: 'feature proposal', project: project) }
let(:technical_debt) { create(:label, name: 'technical debt', project: project) }
let(:bug_reference) { "#{Label.reference_prefix}#{bug.name}" }
let(:feature_proposal_reference) { feature_proposal.to_reference(format: :name) }
let(:technical_debt_reference) { technical_debt.to_reference(format: :name) }
context 'separated with a comma' do
let(:references) { "#{bug_reference}, #{feature_proposal_reference}, #{technical_debt_reference}" }
it 'links to valid references' do
doc = reference_filter("See #{references}")
expect(doc.css('a').map { |a| a.attr('href') }).to match_array([
urls.namespace_project_issues_url(project.namespace, project, label_name: bug.name),
urls.namespace_project_issues_url(project.namespace, project, label_name: feature_proposal.name),
urls.namespace_project_issues_url(project.namespace, project, label_name: technical_debt.name)
])
expect(doc.text).to eq 'See bug, feature proposal, technical debt'
end
end
context 'separated with a space' do
let(:references) { "#{bug_reference} #{feature_proposal_reference} #{technical_debt_reference}" }
it 'links to valid references' do
doc = reference_filter("See #{references}")
expect(doc.css('a').map { |a| a.attr('href') }).to match_array([
urls.namespace_project_issues_url(project.namespace, project, label_name: bug.name),
urls.namespace_project_issues_url(project.namespace, project, label_name: feature_proposal.name),
urls.namespace_project_issues_url(project.namespace, project, label_name: technical_debt.name)
])
expect(doc.text).to eq 'See bug feature proposal technical debt'
end
end
end
describe 'edge cases' do describe 'edge cases' do
it 'gracefully handles non-references matching the pattern' do it 'gracefully handles non-references matching the pattern' do
exp = act = '(format nil "~0f" 3.0) ; 3.0' exp = act = '(format nil "~0f" 3.0) ; 3.0'
......
...@@ -30,6 +30,14 @@ describe Gitlab::ImportExport::ProjectTreeRestorer, services: true do ...@@ -30,6 +30,14 @@ describe Gitlab::ImportExport::ProjectTreeRestorer, services: true do
expect(Event.where.not(data: nil).first.data[:ref]).not_to be_empty expect(Event.where.not(data: nil).first.data[:ref]).not_to be_empty
end end
it 'preserves updated_at on issues' do
restored_project_json
issue = Issue.where(description: 'Aliquam enim illo et possimus.').first
expect(issue.reload.updated_at.to_s).to eq('2016-06-14 15:02:47 UTC')
end
context 'event at forth level of the tree' do context 'event at forth level of the tree' do
let(:event) { Event.where(title: 'test levels').first } let(:event) { Event.where(title: 'test levels').first }
......
...@@ -130,17 +130,35 @@ describe Project, models: true do ...@@ -130,17 +130,35 @@ describe Project, models: true do
end end
end end
it 'should not allow an invalid URI as import_url' do it 'does not allow an invalid URI as import_url' do
project2 = build(:project, import_url: 'invalid://') project2 = build(:project, import_url: 'invalid://')
expect(project2).not_to be_valid expect(project2).not_to be_valid
end end
it 'should allow a valid URI as import_url' do it 'does allow a valid URI as import_url' do
project2 = build(:project, import_url: 'ssh://test@gitlab.com/project.git') project2 = build(:project, import_url: 'ssh://test@gitlab.com/project.git')
expect(project2).to be_valid expect(project2).to be_valid
end end
it 'does not allow to introduce an empty URI' do
project2 = build(:project, import_url: '')
expect(project2).not_to be_valid
end
it 'does not produce import data on an empty URI' do
project2 = build(:project, import_url: '')
expect(project2.import_data).to be_nil
end
it 'does not produce import data on an invalid URI' do
project2 = build(:project, import_url: 'test://')
expect(project2.import_data).to be_nil
end
end end
describe 'default_scope' do describe 'default_scope' do
......
...@@ -16,7 +16,10 @@ describe GitGarbageCollectWorker do ...@@ -16,7 +16,10 @@ describe GitGarbageCollectWorker do
project.repository_storage_path, project.repository_storage_path,
project.path_with_namespace). project.path_with_namespace).
and_return(true) and_return(true)
expect_any_instance_of(Repository).to receive(:after_create_branch) expect_any_instance_of(Repository).to receive(:after_create_branch).and_call_original
expect_any_instance_of(Repository).to receive(:branch_names).and_call_original
expect_any_instance_of(Repository).to receive(:branch_count).and_call_original
expect_any_instance_of(Repository).to receive(:has_visible_content?).and_call_original
subject.perform(project.id) subject.perform(project.id)
end end
......
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