Commit 33f8b06c authored by Zeger-Jan van de Weg's avatar Zeger-Jan van de Weg

Merge branch 'master' into assign-to-issuable-opener

parents 77a24965 f76bfed9
...@@ -158,8 +158,7 @@ bundler:audit: ...@@ -158,8 +158,7 @@ bundler:audit:
only: only:
- master - master
script: script:
- "bundle exec bundle-audit update" - "bundle exec bundle-audit check --update --ignore OSVDB-115941"
- "bundle exec bundle-audit check --ignore OSVDB-115941"
tags: tags:
- ruby - ruby
- mysql - mysql
......
...@@ -3,11 +3,12 @@ Please view this file on the master branch, on stable branches it's out of date. ...@@ -3,11 +3,12 @@ Please view this file on the master branch, on stable branches it's out of date.
v 8.7.0 (unreleased) v 8.7.0 (unreleased)
- All images in discussions and wikis now link to their source files !3464 (Connor Shea). - All images in discussions and wikis now link to their source files !3464 (Connor Shea).
- Improved Markdown rendering performance !3389 (Yorick Peterse) - Improved Markdown rendering performance !3389 (Yorick Peterse)
- Don't attempt to look up an avatar in repo if repo directory does not exist (Stan hu) - Don't attempt to look up an avatar in repo if repo directory does not exist (Stan Hu)
- Preserve time notes/comments have been updated at when moving issue - Preserve time notes/comments have been updated at when moving issue
- Make HTTP(s) label consistent on clone bar (Stan Hu) - Make HTTP(s) label consistent on clone bar (Stan Hu)
- Expose label description in API (Mariusz Jachimowicz) - Expose label description in API (Mariusz Jachimowicz)
- Allow back dating on issues when created through the API - Allow back dating on issues when created through the API
- Fix Error 500 after renaming a project path (Stan Hu)
- Fix avatar stretching by providing a cropping feature - Fix avatar stretching by providing a cropping feature
- Add endpoints to archive or unarchive a project !3372 - Add endpoints to archive or unarchive a project !3372
- Add links to CI setup documentation from project settings and builds pages - Add links to CI setup documentation from project settings and builds pages
...@@ -15,16 +16,16 @@ v 8.7.0 (unreleased) ...@@ -15,16 +16,16 @@ v 8.7.0 (unreleased)
- Add default scope to projects to exclude projects pending deletion - Add default scope to projects to exclude projects pending deletion
- Implement 'Groups View' as an option for dashboard preferences !3379 (Elias W.) - Implement 'Groups View' as an option for dashboard preferences !3379 (Elias W.)
- Implement 'TODOs View' as an option for dashboard preferences !3379 (Elias W.) - Implement 'TODOs View' as an option for dashboard preferences !3379 (Elias W.)
- Allow issues and merge requests to be assigned to the author - Allow issues and merge requests to be assigned to the author !2765
v 8.6.2 (unreleased)
- Comments on confidential issues don't show up in activity feed to non-members
- Fix NoMethodError when visiting CI root path at `/ci`
- Gracefully handle notes on deleted commits in merge requests (Stan Hu) - Gracefully handle notes on deleted commits in merge requests (Stan Hu)
- Fix creation of merge requests for orphaned branches (Stan Hu) - Fix creation of merge requests for orphaned branches (Stan Hu)
- Fall back to `In-Reply-To` and `References` headers when sub-addressing is not available (David Padilla) - Fall back to `In-Reply-To` and `References` headers when sub-addressing is not available (David Padilla)
- Remove "Congratulations!" tweet button on newly-created project. (Connor Shea) - Remove "Congratulations!" tweet button on newly-created project. (Connor Shea)
- Improved UX of the navigation sidebar - Improved UX of the navigation sidebar
- Build status notifications
v 8.6.5 (unreleased)
- Check permissions when user attempts to import members from another project
v 8.6.4 v 8.6.4
- Don't attempt to fetch any tags from a forked repo (Stan Hu) - Don't attempt to fetch any tags from a forked repo (Stan Hu)
...@@ -144,6 +145,9 @@ v 8.6.0 ...@@ -144,6 +145,9 @@ v 8.6.0
- Trigger a todo for mentions on commits page - Trigger a todo for mentions on commits page
- Let project owners and admins soft delete issues and merge requests - Let project owners and admins soft delete issues and merge requests
v 8.5.9
- Don't attempt to fetch any tags from a forked repo (Stan Hu).
v 8.5.8 v 8.5.8
- Bump Git version requirement to 2.7.4 - Bump Git version requirement to 2.7.4
...@@ -285,6 +289,12 @@ v 8.5.0 ...@@ -285,6 +289,12 @@ v 8.5.0
- Show label row when filtering issues or merge requests by label (Nuttanart Pornprasitsakul) - Show label row when filtering issues or merge requests by label (Nuttanart Pornprasitsakul)
- Add Todos - Add Todos
v 8.4.7
- Don't attempt to fetch any tags from a forked repo (Stan Hu).
v 8.4.6
- Bump Git version requirement to 2.7.4
v 8.4.5 v 8.4.5
- No CE-specific changes - No CE-specific changes
...@@ -398,6 +408,12 @@ v 8.4.0 ...@@ -398,6 +408,12 @@ v 8.4.0
- Add IP check against DNSBLs at account sign-up - Add IP check against DNSBLs at account sign-up
- Added cache:key to .gitlab-ci.yml allowing to fine tune the caching - Added cache:key to .gitlab-ci.yml allowing to fine tune the caching
v 8.3.6
- Don't attempt to fetch any tags from a forked repo (Stan Hu).
v 8.3.5
- Bump Git version requirement to 2.7.4
v 8.3.4 v 8.3.4
- Use gitlab-workhorse 0.5.4 (fixes API routing bug) - Use gitlab-workhorse 0.5.4 (fixes API routing bug)
......
...@@ -99,7 +99,7 @@ GEM ...@@ -99,7 +99,7 @@ GEM
bullet (5.0.0) bullet (5.0.0)
activesupport (>= 3.0.0) activesupport (>= 3.0.0)
uniform_notifier (~> 1.9.0) uniform_notifier (~> 1.9.0)
bundler-audit (0.4.0) bundler-audit (0.5.0)
bundler (~> 1.2) bundler (~> 1.2)
thor (~> 0.18) thor (~> 0.18)
byebug (8.2.1) byebug (8.2.1)
......
class GitLabDropdownFilter class GitLabDropdownFilter
BLUR_KEYCODES = [27, 40] BLUR_KEYCODES = [27, 40]
ARROW_KEY_CODES = [38, 40]
HAS_VALUE_CLASS = "has-value" HAS_VALUE_CLASS = "has-value"
constructor: (@input, @options) -> constructor: (@input, @options) ->
...@@ -22,19 +23,23 @@ class GitLabDropdownFilter ...@@ -22,19 +23,23 @@ class GitLabDropdownFilter
# Key events # Key events
timeout = "" timeout = ""
@input.on "keyup", (e) => @input.on "keyup", (e) =>
keyCode = e.which
return if ARROW_KEY_CODES.indexOf(keyCode) >= 0
if @input.val() isnt "" and !$inputContainer.hasClass HAS_VALUE_CLASS if @input.val() isnt "" and !$inputContainer.hasClass HAS_VALUE_CLASS
$inputContainer.addClass HAS_VALUE_CLASS $inputContainer.addClass HAS_VALUE_CLASS
else if @input.val() is "" and $inputContainer.hasClass HAS_VALUE_CLASS else if @input.val() is "" and $inputContainer.hasClass HAS_VALUE_CLASS
$inputContainer.removeClass HAS_VALUE_CLASS $inputContainer.removeClass HAS_VALUE_CLASS
if e.keyCode is 13 and @input.val() isnt "" if keyCode is 13 and @input.val() isnt ""
if @options.enterCallback if @options.enterCallback
@options.enterCallback() @options.enterCallback()
return return
clearTimeout timeout clearTimeout timeout
timeout = setTimeout => timeout = setTimeout =>
blur_field = @shouldBlur e.keyCode blur_field = @shouldBlur keyCode
search_text = @input.val() search_text = @input.val()
if blur_field and @filterInputBlur if blur_field and @filterInputBlur
...@@ -96,6 +101,7 @@ class GitLabDropdown ...@@ -96,6 +101,7 @@ class GitLabDropdown
LOADING_CLASS = "is-loading" LOADING_CLASS = "is-loading"
PAGE_TWO_CLASS = "is-page-two" PAGE_TWO_CLASS = "is-page-two"
ACTIVE_CLASS = "is-active" ACTIVE_CLASS = "is-active"
currentIndex = -1
FILTER_INPUT = '.dropdown-input .dropdown-input-field' FILTER_INPUT = '.dropdown-input .dropdown-input-field'
...@@ -145,11 +151,11 @@ class GitLabDropdown ...@@ -145,11 +151,11 @@ class GitLabDropdown
data: => data: =>
return @fullData return @fullData
callback: (data) => callback: (data) =>
currentIndex = -1
@parseData data @parseData data
@highlightRow 1
enterCallback: => enterCallback: =>
if @enterCallback if @enterCallback
@selectFirstRow() @selectRowAtIndex 0
# Event listeners # Event listeners
...@@ -218,6 +224,8 @@ class GitLabDropdown ...@@ -218,6 +224,8 @@ class GitLabDropdown
return true return true
opened: => opened: =>
@addArrowKeyEvent()
contentHtml = $('.dropdown-content', @dropdown).html() contentHtml = $('.dropdown-content', @dropdown).html()
if @remote && contentHtml is "" if @remote && contentHtml is ""
@remote.execute() @remote.execute()
...@@ -228,6 +236,7 @@ class GitLabDropdown ...@@ -228,6 +236,7 @@ class GitLabDropdown
@dropdown.trigger('shown.gl.dropdown') @dropdown.trigger('shown.gl.dropdown')
hidden: (e) => hidden: (e) =>
@removeArrayKeyEvent()
if @options.filterable if @options.filterable
@dropdown @dropdown
.find(".dropdown-input-field") .find(".dropdown-input-field")
...@@ -307,11 +316,11 @@ class GitLabDropdown ...@@ -307,11 +316,11 @@ class GitLabDropdown
if @highlight if @highlight
text = @highlightTextMatches(text, @filterInput.val()) text = @highlightTextMatches(text, @filterInput.val())
html = "<li>" html = "<li>
html += "<a href='#{url}' class='#{cssClass}'>" <a href='#{url}' class='#{cssClass}'>
html += text #{text}
html += "</a>" </a>
html += "</li>" </li>"
return html return html
...@@ -322,11 +331,11 @@ class GitLabDropdown ...@@ -322,11 +331,11 @@ class GitLabDropdown
).join('') ).join('')
noResults: -> noResults: ->
html = "<li>" html = "<li class='dropdown-menu-empty-link'>
html += "<a class='dropdown-menu-empty-link is-focused'>" <a href='#' class='is-focused'>
html += "No matching results." No matching results.
html += "</a>" </a>
html += "</li>" </li>"
highlightRow: (index) -> highlightRow: (index) ->
if @filterInput.val() isnt "" if @filterInput.val() isnt ""
...@@ -378,16 +387,81 @@ class GitLabDropdown ...@@ -378,16 +387,81 @@ class GitLabDropdown
return selectedObject return selectedObject
selectFirstRow: -> selectRowAtIndex: (index) ->
selector = '.dropdown-content li:first-child a' selector = ".dropdown-content li:not(.divider):eq(#{index}) a"
if @dropdown.find(".dropdown-toggle-page").length if @dropdown.find(".dropdown-toggle-page").length
selector = ".dropdown-page-one .dropdown-content li:first-child a" selector = ".dropdown-page-one #{selector}"
# simulate a click on the first link # simulate a click on the first link
$(selector).trigger "click" $(selector).trigger "click"
addArrowKeyEvent: ->
ARROW_KEY_CODES = [38, 40]
$input = @dropdown.find(".dropdown-input-field")
selector = '.dropdown-content li:not(.divider)'
if @dropdown.find(".dropdown-toggle-page").length
selector = ".dropdown-page-one #{selector}"
$('body').on 'keydown', (e) =>
currentKeyCode = e.which
if ARROW_KEY_CODES.indexOf(currentKeyCode) >= 0
e.preventDefault()
e.stopImmediatePropagation()
PREV_INDEX = currentIndex
$listItems = $(selector, @dropdown)
# if @options.filterable
# $input.blur()
if currentKeyCode is 40
# Move down
currentIndex += 1 if currentIndex < ($listItems.length - 1)
else if currentKeyCode is 38
# Move up
currentIndex -= 1 if currentIndex > 0
@highlightRowAtIndex($listItems, currentIndex) if currentIndex isnt PREV_INDEX
return false
if currentKeyCode is 13
@selectRowAtIndex currentIndex
removeArrayKeyEvent: ->
$('body').off 'keydown'
highlightRowAtIndex: ($listItems, index) ->
# Remove the class for the previously focused row
$('.is-focused', @dropdown).removeClass 'is-focused'
# Update the class for the row at the specific index
$listItem = $listItems.eq(index)
$listItem.find('a:first-child').addClass "is-focused"
# Dropdown content scroll area
$dropdownContent = $listItem.closest('.dropdown-content')
dropdownScrollTop = $dropdownContent.scrollTop()
dropdownContentHeight = $dropdownContent.outerHeight()
dropdownContentTop = $dropdownContent.prop('offsetTop')
dropdownContentBottom = dropdownContentTop + dropdownContentHeight
# Get the offset bottom of the list item
listItemHeight = $listItem.outerHeight()
listItemTop = $listItem.prop('offsetTop')
listItemBottom = listItemTop + listItemHeight
if listItemBottom > dropdownContentBottom + dropdownScrollTop
# Scroll the dropdown content down
$dropdownContent.scrollTop(listItemBottom - dropdownContentBottom)
else if listItemTop < dropdownContentTop + dropdownScrollTop
# Scroll the dropdown content up
$dropdownContent.scrollTop(listItemTop - dropdownContentTop)
$.fn.glDropdown = (opts) -> $.fn.glDropdown = (opts) ->
return @.each -> return @.each ->
if (!$.data @, 'glDropdown') if (!$.data @, 'glDropdown')
$.data(@, 'glDropdown', new GitLabDropdown @, opts) $.data(@, 'glDropdown', new GitLabDropdown @, opts)
...@@ -6,25 +6,10 @@ class @Issue ...@@ -6,25 +6,10 @@ class @Issue
constructor: -> constructor: ->
# Prevent duplicate event bindings # Prevent duplicate event bindings
@disableTaskList() @disableTaskList()
@fixAffixScroll()
if $('a.btn-close').length if $('a.btn-close').length
@initTaskList() @initTaskList()
@initIssueBtnEventListeners() @initIssueBtnEventListeners()
fixAffixScroll: ->
fixAffix = ->
$discussion = $('.issuable-discussion')
$sidebar = $('.issuable-sidebar')
if $sidebar.hasClass('no-affix')
$sidebar.removeClass(['affix-top','affix'])
discussionHeight = $discussion.height()
sidebarHeight = $sidebar.height()
if sidebarHeight > discussionHeight
$discussion.height(sidebarHeight + 50)
$sidebar.addClass('no-affix')
$(window).on('resize', fixAffix)
fixAffix()
initTaskList: -> initTaskList: ->
$('.detail-page-description .js-task-list-container').taskList('enable') $('.detail-page-description .js-task-list-container').taskList('enable')
$(document).on 'tasklist:changed', '.detail-page-description .js-task-list-container', @updateTaskList $(document).on 'tasklist:changed', '.detail-page-description .js-task-list-container', @updateTaskList
...@@ -49,7 +34,7 @@ class @Issue ...@@ -49,7 +34,7 @@ class @Issue
issueStatus = if isClose then 'close' else 'open' issueStatus = if isClose then 'close' else 'open'
new Flash(issueFailMessage, 'alert') new Flash(issueFailMessage, 'alert')
success: (data, textStatus, jqXHR) -> success: (data, textStatus, jqXHR) ->
if data.saved if 'id' of data
$(document).trigger('issuable:change'); $(document).trigger('issuable:change');
if isClose if isClose
$('a.btn-close').addClass('hidden') $('a.btn-close').addClass('hidden')
......
...@@ -15,8 +15,6 @@ class @MergeRequest ...@@ -15,8 +15,6 @@ class @MergeRequest
this.$('.show-all-commits').on 'click', => this.$('.show-all-commits').on 'click', =>
this.showAllCommits() this.showAllCommits()
@fixAffixScroll();
@initTabs() @initTabs()
# Prevent duplicate event bindings # Prevent duplicate event bindings
...@@ -30,20 +28,6 @@ class @MergeRequest ...@@ -30,20 +28,6 @@ class @MergeRequest
$: (selector) -> $: (selector) ->
this.$el.find(selector) this.$el.find(selector)
fixAffixScroll: ->
fixAffix = ->
$discussion = $('.issuable-discussion')
$sidebar = $('.issuable-sidebar')
if $sidebar.hasClass('no-affix')
$sidebar.removeClass(['affix-top','affix'])
discussionHeight = $discussion.height()
sidebarHeight = $sidebar.height()
if sidebarHeight > discussionHeight
$discussion.height(sidebarHeight + 50)
$sidebar.addClass('no-affix')
$(window).on('resize', fixAffix)
fixAffix()
initTabs: -> initTabs: ->
if @opts.action != 'new' if @opts.action != 'new'
# `MergeRequests#new` has no tab-persisting or lazy-loading behavior # `MergeRequests#new` has no tab-persisting or lazy-loading behavior
......
...@@ -251,13 +251,11 @@ class @Notes ...@@ -251,13 +251,11 @@ class @Notes
Sets some hidden fields in the form. Sets some hidden fields in the form.
### ###
setupMainTargetNoteForm: -> setupMainTargetNoteForm: ->
# find the form # find the form
form = $(".js-new-note-form") form = $(".js-new-note-form")
# insert the form after the button # Set a global clone of the form for later cloning
form.clone().replaceAll $(".js-main-target-form") @formClone = form.clone()
form = form.prev("form")
# show the form # show the form
@setupNoteForm(form) @setupNoteForm(form)
...@@ -266,9 +264,7 @@ class @Notes ...@@ -266,9 +264,7 @@ class @Notes
form.removeClass "js-new-note-form" form.removeClass "js-new-note-form"
form.addClass "js-main-target-form" form.addClass "js-main-target-form"
# remove unnecessary fields and buttons
form.find("#note_line_code").remove() form.find("#note_line_code").remove()
form.find(".js-close-discussion-note-form").remove()
### ###
General note form setup. General note form setup.
...@@ -297,7 +293,14 @@ class @Notes ...@@ -297,7 +293,14 @@ class @Notes
else else
previewButton.removeClass("turn-on").addClass "turn-off" previewButton.removeClass("turn-on").addClass "turn-off"
textarea.on 'focus', ->
$(this).closest('.md-area').addClass 'is-focused'
textarea.on 'blur', ->
$(this).closest('.md-area').removeClass 'is-focused'
autosize(textarea) autosize(textarea)
new Autosave textarea, [ new Autosave textarea, [
"Note" "Note"
form.find("#note_commit_id").val() form.find("#note_commit_id").val()
...@@ -307,7 +310,6 @@ class @Notes ...@@ -307,7 +310,6 @@ class @Notes
] ]
# remove notify commit author checkbox for non-commit notes # remove notify commit author checkbox for non-commit notes
form.find(".js-notify-commit-author").remove() if form.find("#note_noteable_type").val() isnt "Commit"
GitLab.GfmAutoComplete.setup() GitLab.GfmAutoComplete.setup()
new DropzoneInput(form) new DropzoneInput(form)
form.show() form.show()
...@@ -455,15 +457,15 @@ class @Notes ...@@ -455,15 +457,15 @@ class @Notes
Shows the note form below the notes. Shows the note form below the notes.
### ###
replyToDiscussionNote: (e) => replyToDiscussionNote: (e) =>
form = $(".js-new-note-form") form = @formClone.clone()
replyLink = $(e.target).closest(".js-discussion-reply-button") replyLink = $(e.target).closest(".js-discussion-reply-button")
replyLink.hide() replyLink.hide()
# insert the form after the button # insert the form after the button
form.clone().insertAfter replyLink replyLink.after form
# show the form # show the form
@setupDiscussionNoteForm(replyLink, replyLink.next("form")) @setupDiscussionNoteForm(replyLink, form)
### ###
Shows the diff or discussion form and does some setup on it. Shows the diff or discussion form and does some setup on it.
...@@ -488,7 +490,9 @@ class @Notes ...@@ -488,7 +490,9 @@ class @Notes
.text(form.find('.js-close-discussion-note-form').data('cancel-text')) .text(form.find('.js-close-discussion-note-form').data('cancel-text'))
@setupNoteForm form @setupNoteForm form
form.find(".js-note-text").focus() form.find(".js-note-text").focus()
form.addClass "js-discussion-note-form" form
.removeClass('js-main-target-form')
.addClass("discussion-form js-discussion-note-form")
### ###
Called when clicking on the "add a comment" button on the side of a diff line. Called when clicking on the "add a comment" button on the side of a diff line.
...@@ -498,9 +502,8 @@ class @Notes ...@@ -498,9 +502,8 @@ class @Notes
### ###
addDiffNote: (e) => addDiffNote: (e) =>
e.preventDefault() e.preventDefault()
link = e.currentTarget $link = $(e.currentTarget)
form = $(".js-new-note-form") row = $link.closest("tr")
row = $(link).closest("tr")
nextRow = row.next() nextRow = row.next()
hasNotes = nextRow.is(".notes_holder") hasNotes = nextRow.is(".notes_holder")
addForm = false addForm = false
...@@ -509,7 +512,7 @@ class @Notes ...@@ -509,7 +512,7 @@ class @Notes
# In parallel view, look inside the correct left/right pane # In parallel view, look inside the correct left/right pane
if @isParallelView() if @isParallelView()
lineType = $(link).data("lineType") lineType = $link.data("lineType")
targetContent += "." + lineType targetContent += "." + lineType
rowCssToAdd = "<tr class=\"notes_holder js-temp-notes-holder\"><td class=\"notes_line\"></td><td class=\"notes_content parallel old\"></td><td class=\"notes_line\"></td><td class=\"notes_content parallel new\"></td></tr>" rowCssToAdd = "<tr class=\"notes_holder js-temp-notes-holder\"><td class=\"notes_line\"></td><td class=\"notes_content parallel old\"></td><td class=\"notes_line\"></td><td class=\"notes_content parallel new\"></td></tr>"
...@@ -531,11 +534,11 @@ class @Notes ...@@ -531,11 +534,11 @@ class @Notes
addForm = true addForm = true
if addForm if addForm
newForm = form.clone() newForm = @formClone.clone()
newForm.appendTo row.next().find(targetContent) newForm.appendTo row.next().find(targetContent)
# show the form # show the form
@setupDiscussionNoteForm $(link), newForm @setupDiscussionNoteForm $link, newForm
### ###
Called in response to "cancel" on a diff note form. Called in response to "cancel" on a diff note form.
...@@ -560,7 +563,6 @@ class @Notes ...@@ -560,7 +563,6 @@ class @Notes
cancelDiscussionForm: (e) => cancelDiscussionForm: (e) =>
e.preventDefault() e.preventDefault()
form = $(".js-new-note-form")
form = $(e.target).closest(".js-discussion-note-form") form = $(e.target).closest(".js-discussion-note-form")
@removeDiscussionNoteForm(form) @removeDiscussionNoteForm(form)
......
...@@ -42,7 +42,7 @@ class @ZenMode ...@@ -42,7 +42,7 @@ class @ZenMode
$(e.currentTarget).trigger('zen_mode:leave') $(e.currentTarget).trigger('zen_mode:leave')
$(document).on 'zen_mode:enter', (e) => $(document).on 'zen_mode:enter', (e) =>
@enter(e.target.parentNode) @enter($(e.target).closest('.md-area').find('.zen-backdrop'))
$(document).on 'zen_mode:leave', (e) => $(document).on 'zen_mode:leave', (e) =>
@exit() @exit()
......
...@@ -125,13 +125,6 @@ p.time { ...@@ -125,13 +125,6 @@ p.time {
height: 150px; height: 150px;
} }
// Fixes alignment on notes.
.new_note {
label {
text-align: left;
}
}
// Fix issue with notes & lists creating a bunch of bottom borders. // Fix issue with notes & lists creating a bunch of bottom borders.
li.note { li.note {
img { max-width: 100% } img { max-width: 100% }
......
.div-dropzone-wrapper { .div-dropzone-wrapper {
.div-dropzone { .div-dropzone {
position: relative; position: relative;
padding: 0; margin-bottom: -5px;
border: 0;
margin-bottom: 5px;
.div-dropzone-focus { .div-dropzone-focus {
border-color: #66afe9 !important; border-color: #66afe9 !important;
...@@ -25,12 +23,10 @@ ...@@ -25,12 +23,10 @@
.div-dropzone-spinner { .div-dropzone-spinner {
position: absolute; position: absolute;
top: 100%; bottom: 10px;
left: 100%; right: 5px;
margin-top: -1.1em;
margin-left: -1.1em;
opacity: 0; opacity: 0;
font-size: 30px; font-size: 20px;
transition: opacity 200ms ease-in-out; transition: opacity 200ms ease-in-out;
} }
...@@ -65,17 +61,29 @@ ...@@ -65,17 +61,29 @@
position: relative; position: relative;
} }
.md-header {
.nav-links {
.active {
a {
border-bottom-color: #000;
}
}
a {
padding-top: 0;
line-height: 1;
}
}
}
.referenced-users { .referenced-users {
color: #4c4e54; color: #4c4e54;
padding-top: 10px; padding-top: 10px;
} }
.md-preview-holder { .md-preview-holder {
background: #fff; min-height: 167px;
border: 1px solid #ddd; padding: 10px 0;
min-height: 169px;
padding: 5px;
box-shadow: none;
} }
.markdown-area { .markdown-area {
......
...@@ -250,7 +250,7 @@ a > code { ...@@ -250,7 +250,7 @@ a > code {
* Textareas intended for GFM * Textareas intended for GFM
* *
*/ */
textarea.js-gfm-input { .js-gfm-input {
font-family: $monospace_font; font-family: $monospace_font;
color: $gl-text-color; color: $gl-text-color;
} }
......
...@@ -217,3 +217,9 @@ $notes-light-color: #8e8e8e; ...@@ -217,3 +217,9 @@ $notes-light-color: #8e8e8e;
$notes-action-color: #c3c3c3; $notes-action-color: #c3c3c3;
$notes-role-color: #8e8e8e; $notes-role-color: #8e8e8e;
$notes-role-border-color: #e4e4e4; $notes-role-border-color: #e4e4e4;
$note-disabled-comment-color: #b2b2b2;
$note-form-border-color: #e5e5e5;
$note-toolbar-color: #959494;
$zen-control-hover-color: #111;
.zennable { .zen-backdrop {
a.js-zen-enter { &.fullscreen {
color: $gl-gray; background-color: white;
position: absolute; position: fixed;
top: 0; top: 0;
right: 4px; bottom: 0;
line-height: 56px; left: 0;
} right: 0;
z-index: 1031;
a.js-zen-leave { textarea {
display: none; border: none;
color: $gl-text-color; box-shadow: none;
position: absolute; border-radius: 0;
top: 10px; color: #000;
right: 10px; font-size: 20px;
padding: 5px; line-height: 26px;
font-size: 36px; padding: 30px;
display: block;
outline: none;
resize: none;
height: 100vh;
max-width: 900px;
margin: 0 auto;
}
&:hover { .zen-control-leave {
color: #111; display: block;
position: absolute;
top: 0;
} }
} }
}
.zen-backdrop { .zen-cotrol {
&.fullscreen { padding: 0;
background-color: white; color: #555;
position: fixed; background: none;
top: 0; border: 0;
bottom: 0; }
left: 0;
right: 0;
z-index: 1031;
textarea { .zen-control-full {
border: none; color: $note-toolbar-color;
box-shadow: none;
border-radius: 0;
color: #000;
font-size: 20px;
line-height: 26px;
padding: 30px;
display: block;
outline: none;
resize: none;
height: 100vh;
max-width: 900px;
margin: 0 auto;
}
a.js-zen-enter { &:hover {
display: none; color: $gl-link-color;
} text-decoration: none;
}
}
a.js-zen-leave { .zen-control-leave {
display: block; display: none;
position: absolute; color: $gl-text-color;
top: 0; position: absolute;
} right: 10px;
} padding: 5px;
font-size: 36px;
&:hover {
color: $zen-control-hover-color;
} }
} }
...@@ -195,42 +195,6 @@ ...@@ -195,42 +195,6 @@
line-height: 31px; line-height: 31px;
} }
.disabled-comment-area {
padding: 16px 0;
.disabled-profile {
width: 40px;
height: 40px;
background: $border-gray-dark;
border-radius: 20px;
display: inline-block;
margin-right: 10px;
}
.disabled-comment {
background: $gray-light;
display: inline-block;
vertical-align: top;
height: 200px;
border-radius: 4px;
border: 1px solid $border-gray-normal;
padding-top: 90px;
text-align: center;
right: 20px;
position: absolute;
left: 70px;
margin-bottom: 20px;
span {
color: #b2b2b2;
a {
color: $md-link-color;
}
}
}
}
.builds { .builds {
.table-holder { .table-holder {
overflow-x: scroll; overflow-x: scroll;
......
/** /**
* Note Form * Note Form
*/ */
.comment-btn {
@extend .btn-create;
}
.reply-btn { .reply-btn {
@extend .btn-primary; @extend .btn-primary;
margin: 10px $gl-padding; margin: 10px $gl-padding;
...@@ -17,16 +13,17 @@ ...@@ -17,16 +13,17 @@
} }
.diff-file, .diff-file,
.discussion { .discussion {
.new_note { .new-note {
margin: 0; margin: 0;
border: none; border: none;
} }
} }
.new_note {
.new-note {
display: none; display: none;
} }
.new_note, .note-edit-form { .new-note, .note-edit-form {
.note-form-actions { .note-form-actions {
margin-top: $gl-padding; margin-top: $gl-padding;
} }
...@@ -40,21 +37,18 @@ ...@@ -40,21 +37,18 @@
img { img {
max-width: 100%; max-width: 100%;
} }
}
.note_text { .note-textarea {
width: 100%; padding: 10px 0;
} font-family: $regular_font;
border: 0;
.comment-hints { &:focus {
margin-top: -12px; outline: 0;
} }
} }
/* loading indicator */
.notes-busy {
margin: 18px;
}
.note-image-attach { .note-image-attach {
@extend .col-md-4; @extend .col-md-4;
margin-left: 45px; margin-left: 45px;
...@@ -62,38 +56,29 @@ ...@@ -62,38 +56,29 @@
} }
.common-note-form { .common-note-form {
margin: 0; .md-area {
background: #fff; padding: $gl-padding-top $gl-padding;
padding: $gl-padding; border: 1px solid $note-form-border-color;
margin-left: -$gl-padding; border-radius: $border-radius-base;
margin-right: -$gl-padding;
margin-bottom: -$gl-padding; &.is-focused {
} border-color: $focus-border-color;
box-shadow: 0 0 2px rgba(#000, .2),
.note-form-actions { 0 0 4px rgba($focus-border-color, .4);
.note-form-option {
margin-top: 8px; .comment-toolbar,
margin-left: 30px; .nav-links {
@extend .pull-left; border-color: $focus-border-color;
} }
.js-notify-commit-author {
float: left;
}
.write-preview-btn {
// makes the "absolute" position for links relative to this
position: relative;
// preview/edit buttons
> a {
position: absolute;
right: 5px;
top: 8px;
} }
} }
} }
.discussion-form {
padding: $gl-padding-top $gl-padding;
background-color: #fff;
}
.note-edit-form { .note-edit-form {
display: none; display: none;
font-size: 15px; font-size: 15px;
...@@ -152,11 +137,49 @@ ...@@ -152,11 +137,49 @@
} }
} }
.comment-hints { .comment-toolbar {
color: #999; padding-top: $gl-padding-top;
background: #fff; color: $note-toolbar-color;
padding: 7px; border-top: 1px solid $border-color;
margin-top: -7px; }
border: 1px solid $border-color;
font-size: 13px; .toolbar-button {
padding: 0;
background: none;
border: 0;
font-size: 14px;
line-height: 16px;
&:hover,
&:focus {
color: $gl-link-color;
outline: 0;
}
@media (min-width: $screen-md-min) {
float: left;
margin-right: $gl-padding;
&:last-child {
float: right;
margin-right: 0;
}
}
}
.toolbar-button-icon {
position: relative;
top: 1px;
margin-right: 3px;
color: inherit;
font-size: 16px;
}
.toolbar-text {
font-size: 14px;
line-height: 16px;
@media (min-width: $screen-md-min) {
float: left;
}
} }
...@@ -20,6 +20,12 @@ ul.notes { ...@@ -20,6 +20,12 @@ ul.notes {
.timeline-content { .timeline-content {
margin-left: 55px; margin-left: 55px;
&.timeline-content-form {
@media (max-width: $screen-sm-max) {
margin-left: 0;
}
}
} }
.note-created-ago, .note-updated-at { .note-created-ago, .note-updated-at {
...@@ -149,7 +155,7 @@ ul.notes { ...@@ -149,7 +155,7 @@ ul.notes {
&.notes_content { &.notes_content {
background-color: #fff; background-color: #fff;
border-width: 1px 0; border-width: 1px 0;
padding-top: 0; padding: 0;
vertical-align: top; vertical-align: top;
&.parallel { &.parallel {
border-width: 1px; border-width: 1px;
...@@ -281,3 +287,21 @@ ul.notes { ...@@ -281,3 +287,21 @@ ul.notes {
} }
} }
} }
.disabled-comment {
margin-left: -$gl-padding-top;
margin-right: -$gl-padding-top;
background-color: $gray-light;
border-radius: $border-radius-base;
border: 1px solid $border-gray-normal;
color: $note-disabled-comment-color;
line-height: 200px;
.disabled-comment-text {
line-height: normal;
}
a {
color: $gl-link-color;
}
}
...@@ -401,7 +401,7 @@ pre.light-well { ...@@ -401,7 +401,7 @@ pre.light-well {
} }
.commit_short_id { .commit_short_id {
margin-right: 5px; margin: 0 5px;
color: $gl-link-color; color: $gl-link-color;
font-weight: 600; font-weight: 600;
} }
......
.container-fluid .content { .container-fluid {
.ci-status { .ci-status {
padding: 2px 7px; padding: 2px 7px;
margin-right: 5px; margin-right: 5px;
......
...@@ -15,8 +15,9 @@ class AutocompleteController < ApplicationController ...@@ -15,8 +15,9 @@ class AutocompleteController < ApplicationController
@users = [*@users, current_user] @users = [*@users, current_user]
end end
if params[:author_id] && params[:author_id] != "false" if params[:author_id].present?
@users = [User.find(params[:author_id]), *@users] author = User.find_by_id(params[:author_id])
@users = [author, *@users] if author
end end
@users.uniq! @users.uniq!
......
...@@ -94,9 +94,14 @@ class Projects::ProjectMembersController < Projects::ApplicationController ...@@ -94,9 +94,14 @@ class Projects::ProjectMembersController < Projects::ApplicationController
end end
def apply_import def apply_import
giver = Project.find(params[:source_project_id]) source_project = Project.find(params[:source_project_id])
status = @project.team.import(giver, current_user)
notice = status ? "Successfully imported" : "Import failed" if can?(current_user, :read_project_member, source_project)
status = @project.team.import(source_project, current_user)
notice = status ? "Successfully imported" : "Import failed"
else
return render_404
end
redirect_to(namespace_project_project_members_path(project.namespace, project), redirect_to(namespace_project_project_members_path(project.namespace, project),
notice: notice) notice: notice)
......
...@@ -40,6 +40,9 @@ class ProjectsController < Projects::ApplicationController ...@@ -40,6 +40,9 @@ class ProjectsController < Projects::ApplicationController
def update def update
status = ::Projects::UpdateService.new(@project, current_user, project_params).execute status = ::Projects::UpdateService.new(@project, current_user, project_params).execute
# Refresh the repo in case anything changed
@repository = project.repository
respond_to do |format| respond_to do |format|
if status if status
flash[:notice] = "Project '#{@project.name}' was successfully updated." flash[:notice] = "Project '#{@project.name}' was successfully updated."
......
...@@ -11,7 +11,7 @@ module SelectsHelper ...@@ -11,7 +11,7 @@ module SelectsHelper
email_user = opts[:email_user] || false email_user = opts[:email_user] || false
first_user = opts[:first_user] && current_user ? current_user.username : false first_user = opts[:first_user] && current_user ? current_user.username : false
current_user = opts[:current_user] || false current_user = opts[:current_user] || false
author_id = opts[:author_id] || false author_id = opts[:author_id] || ''
project = opts[:project] || @project project = opts[:project] || @project
html = { html = {
......
...@@ -43,17 +43,21 @@ class GitPushService < BaseService ...@@ -43,17 +43,21 @@ class GitPushService < BaseService
@push_commits = @project.repository.commits_between(params[:oldrev], params[:newrev]) @push_commits = @project.repository.commits_between(params[:oldrev], params[:newrev])
process_commit_messages process_commit_messages
end end
# Checks if the main language has changed in the project and if so
# it updates it accordingly
update_main_language
# Update merge requests that may be affected by this push. A new branch # Update merge requests that may be affected by this push. A new branch
# could cause the last commit of a merge request to change. # could cause the last commit of a merge request to change.
update_merge_requests update_merge_requests
# Checks if the main language has changed in the project and if so
# it updates it accordingly
update_main_language
perform_housekeeping perform_housekeeping
end end
def update_main_language def update_main_language
return unless is_default_branch?
return unless push_to_new_branch? || push_to_existing_branch?
current_language = @project.repository.main_language current_language = @project.repository.main_language
unless current_language == @project.main_language unless current_language == @project.main_language
......
.md-area .md-area
.md-header.clearfix .md-header
%ul.nav-links %ul.nav-links
%li.active %li.active
%a.js-md-write-button(href="#md-write-holder" tabindex="-1") %a.js-md-write-button{ href: "#md-write-holder" }
Write Write
%li %li
%a.js-md-preview-button(href="#md-preview-holder" tabindex="-1") %a.js-md-preview-button{ href: "#md-preview-holder" }
Preview Preview
%li.pull-right
%button.zen-cotrol.zen-control-full.js-zen-enter{ type: 'button' }
Go full screen
%div .md-write-holder
.md-write-holder = yield
= yield .md.md-preview-holder.js-md-preview.hide{class: (preview_class if defined?(preview_class))}
.md.md-preview-holder.hide
.js-md-preview{class: (preview_class if defined?(preview_class))}
- if defined?(referenced_users) && referenced_users - if defined?(referenced_users) && referenced_users
%div.referenced-users.hide %div.referenced-users.hide
......
.zennable .zen-backdrop
.zen-backdrop - classes << ' js-gfm-input js-autosize markdown-area'
- classes << ' js-gfm-input js-autosize markdown-area' - if defined?(f) && f
- if defined?(f) && f = f.text_area attr, class: classes, placeholder: "Write a comment or drag your files here..."
= f.text_area attr, class: classes - else
- else = text_area_tag attr, nil, class: classes, placeholder: "Write a comment or drag your files here..."
= text_area_tag attr, nil, class: classes %a.zen-cotrol.zen-control-leave.js-zen-leave{ href: "#" }
%a.js-zen-enter(tabindex="-1" href="#") = icon('compress')
= icon('expand')
Edit in fullscreen
%a.js-zen-leave(tabindex="-1" href="#")
= icon('compress')
.note-edit-form .note-edit-form
= form_for note, url: namespace_project_note_path(@project.namespace, @project, note), method: :put, remote: true, authenticity_token: true, html: { class: 'edit-note js-quick-submit' } do |f| = form_for note, url: namespace_project_note_path(@project.namespace, @project, note), method: :put, remote: true, authenticity_token: true, html: { class: 'edit-note common-note-form js-quick-submit' } do |f|
= note_target_fields(note) = note_target_fields(note)
= render layout: 'projects/md_preview', locals: { preview_class: 'md-preview' } do = render layout: 'projects/md_preview', locals: { preview_class: 'md-preview' } do
= render 'projects/zen', f: f, attr: :note, classes: 'note_text js-note-text js-task-list-field' = render 'projects/zen', f: f, attr: :note, classes: 'note-textarea js-note-text js-task-list-field'
= render 'projects/notes/hints' = render 'projects/notes/hints'
.note-form-actions.clearfix .note-form-actions.clearfix
= f.submit 'Save Comment', class: 'btn btn-nr btn-save btn-grouped js-comment-button' = f.submit 'Save Comment', class: 'btn btn-nr btn-save btn-grouped js-comment-button'
= link_to 'Cancel', '#', class: 'btn btn-nr btn-cancel note-edit-cancel' %button.btn.btn-nr.btn-cancel.note-edit-cancel{ type: 'button' }
Cancel
= form_for [@project.namespace.becomes(Namespace), @project, @note], remote: true, html: { :'data-type' => 'json', multipart: true, id: nil, class: "new_note js-new-note-form js-quick-submit common-note-form gfm-form" }, authenticity_token: true do |f| = form_for [@project.namespace.becomes(Namespace), @project, @note], remote: true, html: { :'data-type' => 'json', multipart: true, id: nil, class: "new-note js-new-note-form js-quick-submit common-note-form gfm-form" }, authenticity_token: true do |f|
= hidden_field_tag :view, diff_view = hidden_field_tag :view, diff_view
= hidden_field_tag :line_type = hidden_field_tag :line_type
= note_target_fields(@note) = note_target_fields(@note)
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
= f.hidden_field :noteable_type = f.hidden_field :noteable_type
= render layout: 'projects/md_preview', locals: { preview_class: "md-preview", referenced_users: true } do = render layout: 'projects/md_preview', locals: { preview_class: "md-preview", referenced_users: true } do
= render 'projects/zen', f: f, attr: :note, classes: 'note_text js-note-text' = render 'projects/zen', f: f, attr: :note, classes: 'note-textarea js-note-text'
= render 'projects/notes/hints' = render 'projects/notes/hints'
.error-alert .error-alert
......
.comment-hints.clearfix .comment-toolbar.clearfix
.pull-left .toolbar-text
Styling with
= link_to 'Markdown', help_page_path('markdown', 'markdown'), target: '_blank', tabindex: -1 = link_to 'Markdown', help_page_path('markdown', 'markdown'), target: '_blank', tabindex: -1
tip: is supported
= random_markdown_tip %button.toolbar-button.markdown-selector{ type: 'button', tabindex: '-1' }
.pull-right = icon('file-image-o', class: 'toolbar-button-icon')
= link_to '#', class: 'markdown-selector', tabindex: -1 do Attach a file
= icon('paperclip')
Attach a file
%ul#notes-list.notes.main-notes-list.timeline %ul#notes-list.notes.main-notes-list.timeline
= render "projects/notes/notes" = render "projects/notes/notes"
.js-notes-busy %ul.notes.timeline
%li.timeline-entry
.js-main-target-form - if can? current_user, :create_note, @project
- if can? current_user, :create_note, @project .timeline-icon.hidden-xs.hidden-sm
= render "projects/notes/form", view: diff_view %a.author_link{ href: user_path(current_user) }
- else = image_tag avatar_icon(current_user), alt: current_user.to_reference, class: 'avatar s40'
.disabled-comment-area .timeline-content.timeline-content-form
.disabled-profile = render "projects/notes/form", view: diff_view
.disabled-comment - else
%span .disabled-comment.text-center
Please .disabled-comment-text.inline
= link_to "register",new_user_session_path Please
or = link_to "register",new_user_session_path
= link_to "login",new_user_session_path or
to post a comment = link_to "login",new_user_session_path
to post a comment
:javascript :javascript
var notes = new Notes("#{namespace_project_notes_path(namespace_id: @project.namespace, target_id: @noteable.id, target_type: @noteable.class.name.underscore)}", #{@notes.map(&:id).to_json}, #{Time.now.to_i}, "#{diff_view}") var notes = new Notes("#{namespace_project_notes_path(namespace_id: @project.namespace, target_id: @noteable.id, target_type: @noteable.class.name.underscore)}", #{@notes.map(&:id).to_json}, #{Time.now.to_i}, "#{diff_view}")
...@@ -3,5 +3,6 @@ Premailer::Rails.config.merge!( ...@@ -3,5 +3,6 @@ Premailer::Rails.config.merge!(
generate_text_part: false, generate_text_part: false,
preserve_styles: true, preserve_styles: true,
remove_comments: true, remove_comments: true,
remove_ids: true remove_ids: true,
remove_scripts: false
) )
...@@ -261,13 +261,13 @@ tree and traverse it. ...@@ -261,13 +261,13 @@ tree and traverse it.
- Run the following check command to make sure that the LDAP settings are - Run the following check command to make sure that the LDAP settings are
correct and GitLab can see your users: correct and GitLab can see your users:
```bash ```bash
# For Omnibus installations # For Omnibus installations
sudo gitlab-rake gitlab:ldap:check sudo gitlab-rake gitlab:ldap:check
# For installations from source # For installations from source
sudo -u git -H bundle exec rake gitlab:ldap:check RAILS_ENV=production sudo -u git -H bundle exec rake gitlab:ldap:check RAILS_ENV=production
``` ```
### Connection Refused ### Connection Refused
......
# GitLab LDAP integration # GitLab LDAP integration
This document was moved under [`administration/auth/ldap`](administration/auth/ldap.md). This document was moved under [`administration/auth/ldap`](../administration/auth/ldap.md).
...@@ -31,7 +31,7 @@ ...@@ -31,7 +31,7 @@
_GitLab uses the [Redcarpet Ruby library][redcarpet] for Markdown processing._ _GitLab uses the [Redcarpet Ruby library][redcarpet] for Markdown processing._
For GitLab we developed something we call "GitLab Flavored Markdown" (GFM). It extends the standard Markdown in a few significant ways to add some useful functionality. GitLab uses "GitLab Flavored Markdown" (GFM). It extends the standard Markdown in a few significant ways to add some useful functionality. It was inspired by [GitHub Flavored Markdown](https://help.github.com/articles/basic-writing-and-formatting-syntax/).
You can use GFM in You can use GFM in
...@@ -47,10 +47,10 @@ You can also use other rich text files in GitLab. You might have to install a de ...@@ -47,10 +47,10 @@ You can also use other rich text files in GitLab. You might have to install a de
GFM honors the markdown specification in how [paragraphs and line breaks are handled](https://daringfireball.net/projects/markdown/syntax#p). GFM honors the markdown specification in how [paragraphs and line breaks are handled](https://daringfireball.net/projects/markdown/syntax#p).
A paragraph is simply one or more consecutive lines of text, separated by one or more blank lines. A paragraph is simply one or more consecutive lines of text, separated by one or more blank lines.
Line-breaks, or softreturns, are rendered if you end a line with two or more spaces Line-breaks, or softreturns, are rendered if you end a line with two or more spaces
Roses are red [followed by two or more spaces] Roses are red [followed by two or more spaces]
Violets are blue Violets are blue
Sugar is sweet Sugar is sweet
...@@ -67,7 +67,7 @@ It is not reasonable to italicize just _part_ of a word, especially when you're ...@@ -67,7 +67,7 @@ It is not reasonable to italicize just _part_ of a word, especially when you're
perform_complicated_task perform_complicated_task
do_this_and_do_that_and_another_thing do_this_and_do_that_and_another_thing
perform_complicated_task perform_complicated_task
do_this_and_do_that_and_another_thing do_this_and_do_that_and_another_thing
## URL auto-linking ## URL auto-linking
......
...@@ -125,7 +125,7 @@ module SharedDiffNote ...@@ -125,7 +125,7 @@ module SharedDiffNote
step 'I should only see one diff form' do step 'I should only see one diff form' do
page.within(diff_file_selector) do page.within(diff_file_selector) do
expect(page).to have_css("form.new_note", count: 1) expect(page).to have_css("form.new-note", count: 1)
end end
end end
...@@ -161,7 +161,7 @@ module SharedDiffNote ...@@ -161,7 +161,7 @@ module SharedDiffNote
step 'I should see a temporary diff comment form' do step 'I should see a temporary diff comment form' do
page.within(diff_file_selector) do page.within(diff_file_selector) do
expect(page).to have_css(".js-temp-notes-holder form.new_note") expect(page).to have_css(".js-temp-notes-holder form.new-note")
end end
end end
......
...@@ -2,7 +2,7 @@ module SharedNote ...@@ -2,7 +2,7 @@ module SharedNote
include Spinach::DSL include Spinach::DSL
step 'I delete a comment' do step 'I delete a comment' do
page.within('.notes') do page.within('.main-notes-list') do
find('.note').hover find('.note').hover
find(".js-note-delete").click find(".js-note-delete").click
end end
...@@ -128,7 +128,7 @@ module SharedNote ...@@ -128,7 +128,7 @@ module SharedNote
end end
step 'I edit the last comment with a +1' do step 'I edit the last comment with a +1' do
page.within(".notes") do page.within(".main-notes-list") do
find(".note").hover find(".note").hover
find('.js-note-edit').click find('.js-note-edit').click
end end
......
...@@ -147,13 +147,20 @@ describe AutocompleteController do ...@@ -147,13 +147,20 @@ describe AutocompleteController do
context 'author of issuable included' do context 'author of issuable included' do
before do before do
sign_in(user) sign_in(user)
get(:users, author_id: non_member.id)
end end
let(:body) { JSON.parse(response.body) } let(:body) { JSON.parse(response.body) }
it 'should also return the author' do it 'includes the author' do
get(:users, author_id: non_member.id)
expect(body.first["username"]).to eq non_member.username expect(body.first["username"]).to eq non_member.username
end end
it 'rejects non existent user ids' do
get(:users, author_id: 99999)
expect(body.collect { |u| u['id'] }).not_to include(99999)
end
end end
end end
require('spec_helper')
describe Projects::ProjectMembersController do
let(:project) { create(:project) }
let(:another_project) { create(:project, :private) }
let(:user) { create(:user) }
let(:member) { create(:user) }
before do
project.team << [user, :master]
another_project.team << [member, :guest]
sign_in(user)
end
describe '#apply_import' do
shared_context 'import applied' do
before do
post(:apply_import, namespace_id: project.namespace.to_param,
project_id: project.to_param,
source_project_id: another_project.id)
end
end
context 'when user can access source project members' do
before { another_project.team << [user, :guest] }
include_context 'import applied'
it 'imports source project members' do
expect(project.team_members).to include member
expect(response).to set_flash.to 'Successfully imported'
expect(response).to redirect_to(
namespace_project_project_members_path(project.namespace, project)
)
end
end
context 'when user is not member of a source project' do
include_context 'import applied'
it 'does not import team members' do
expect(project.team_members).to_not include member
end
it 'responds with not found' do
expect(response.status).to eq 404
end
end
end
end
...@@ -83,6 +83,28 @@ describe ProjectsController do ...@@ -83,6 +83,28 @@ describe ProjectsController do
end end
end end
describe "#update" do
render_views
let(:admin) { create(:admin) }
it "sets the repository to the right path after a rename" do
new_path = 'renamed_path'
project_params = { path: new_path }
controller.instance_variable_set(:@project, project)
sign_in(admin)
put :update,
namespace_id: project.namespace.to_param,
id: project.id,
project: project_params
expect(project.repository.path).to include(new_path)
expect(assigns(:repository).path).to eq(project.repository.path)
expect(response.status).to eq(200)
end
end
describe "#destroy" do describe "#destroy" do
let(:admin) { create(:admin) } let(:admin) { create(:admin) }
......
...@@ -22,7 +22,7 @@ describe 'Issues', feature: true do ...@@ -22,7 +22,7 @@ describe 'Issues', feature: true do
before do before do
visit edit_namespace_project_issue_path(project.namespace, project, issue) visit edit_namespace_project_issue_path(project.namespace, project, issue)
click_link "Edit" click_button "Go full screen"
end end
it 'should open new issue popup' do it 'should open new issue popup' do
......
...@@ -152,7 +152,7 @@ describe 'Comments', feature: true do ...@@ -152,7 +152,7 @@ describe 'Comments', feature: true do
it 'has .new_note css class' do it 'has .new_note css class' do
page.within('.js-temp-notes-holder') do page.within('.js-temp-notes-holder') do
expect(subject).to have_css('.new_note') expect(subject).to have_css('.new-note')
end end
end end
end end
...@@ -225,6 +225,6 @@ describe 'Comments', feature: true do ...@@ -225,6 +225,6 @@ describe 'Comments', feature: true do
end end
def click_diff_line(data = line_code) def click_diff_line(data = line_code)
page.find(%Q{button[data-line-code="#{data}"]}, visible: false).click execute_script("$('button[data-line-code=\"#{data}\"]').click()")
end end
end end
.zennable .md-area
.zen-backdrop .zen-backdrop
%textarea#note_note.js-gfm-input.markdown-area %textarea#note_note.js-gfm-input.markdown-area
%a.js-zen-enter(tabindex="-1" href="#") %a.js-zen-enter(tabindex="-1" href="#")
......
...@@ -29,8 +29,8 @@ describe 'reopen/close issue', -> ...@@ -29,8 +29,8 @@ describe 'reopen/close issue', ->
spyOn(jQuery, 'ajax').and.callFake (req) -> spyOn(jQuery, 'ajax').and.callFake (req) ->
expect(req.type).toBe('PUT') expect(req.type).toBe('PUT')
expect(req.url).toBe('http://gitlab.com/issues/6/close') expect(req.url).toBe('http://gitlab.com/issues/6/close')
req.success saved: true req.success id: 34
$btnClose = $('a.btn-close') $btnClose = $('a.btn-close')
$btnReopen = $('a.btn-reopen') $btnReopen = $('a.btn-reopen')
expect($btnReopen).toBeHidden() expect($btnReopen).toBeHidden()
...@@ -94,7 +94,7 @@ describe 'reopen/close issue', -> ...@@ -94,7 +94,7 @@ describe 'reopen/close issue', ->
spyOn(jQuery, 'ajax').and.callFake (req) -> spyOn(jQuery, 'ajax').and.callFake (req) ->
expect(req.type).toBe('PUT') expect(req.type).toBe('PUT')
expect(req.url).toBe('http://gitlab.com/issues/6/reopen') expect(req.url).toBe('http://gitlab.com/issues/6/reopen')
req.success saved: true req.success id: 34
$btnClose = $('a.btn-close') $btnClose = $('a.btn-close')
$btnReopen = $('a.btn-reopen') $btnReopen = $('a.btn-reopen')
......
...@@ -141,10 +141,12 @@ shared_examples 'a new user email' do ...@@ -141,10 +141,12 @@ shared_examples 'a new user email' do
end end
shared_examples 'it should have Gmail Actions links' do shared_examples 'it should have Gmail Actions links' do
it { is_expected.to have_body_text '<script type="application/ld+json">' }
it { is_expected.to have_body_text /ViewAction/ } it { is_expected.to have_body_text /ViewAction/ }
end end
shared_examples 'it should not have Gmail Actions links' do shared_examples 'it should not have Gmail Actions links' do
it { is_expected.to_not have_body_text '<script type="application/ld+json">' }
it { is_expected.to_not have_body_text /ViewAction/ } it { is_expected.to_not have_body_text /ViewAction/ }
end end
......
...@@ -159,18 +159,28 @@ describe GitPushService, services: true do ...@@ -159,18 +159,28 @@ describe GitPushService, services: true do
end end
describe "Updates main language" do describe "Updates main language" do
context "before push" do context "before push" do
it { expect(project.main_language).to eq(nil) } it { expect(project.main_language).to eq(nil) }
end end
context "after push" do context "after push" do
before do before do
@service = execute_service(project, user, @oldrev, @newrev, @ref) @service = execute_service(project, user, @oldrev, @newrev, ref)
end
context "to master" do
let(:ref) { @ref }
it { expect(@service.update_main_language).to eq(true) }
it { expect(project.main_language).to eq("Ruby") }
end end
it { expect(@service.update_main_language).to eq(true) } context "to other branch" do
it { expect(project.main_language).to eq("Ruby") } let(:ref) { 'refs/heads/feature/branch' }
it { expect(@service.update_main_language).to eq(nil) }
it { expect(project.main_language).to eq(nil) }
end
end end
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