Merge remote-tracking branch 'origin/master' into 2979-personal-access-tokens
This commit is contained in:
commit
d754d99179
17
CHANGELOG
17
CHANGELOG
|
@ -12,6 +12,7 @@ v 8.9.0 (unreleased)
|
|||
- Allow customisable text on the 'nearly there' page after a user signs up
|
||||
- Bump recaptcha gem to 3.0.0 to remove deprecated stoken support
|
||||
- Allow forking projects with restricted visibility level
|
||||
- Added descriptions to notification settings dropdown
|
||||
- Improve note validation to prevent errors when creating invalid note via API
|
||||
- Reduce number of fog gem dependencies
|
||||
- Remove project notification settings associated with deleted projects
|
||||
|
@ -22,6 +23,7 @@ v 8.9.0 (unreleased)
|
|||
- `git clone https://host/namespace/project` now works, in addition to using the `.git` suffix
|
||||
- Bump nokogiri to 1.6.8
|
||||
- Use gitlab-shell v3.0.0
|
||||
- Upgrade to jQuery 2
|
||||
- Use Knapsack to evenly distribute tests across multiple nodes
|
||||
- Add `sha` parameter to MR merge API, to ensure only reviewed changes are merged
|
||||
- Don't allow MRs to be merged when commits were added since the last review / page load
|
||||
|
@ -40,11 +42,13 @@ v 8.9.0 (unreleased)
|
|||
- Use downcased path to container repository as this is expected path by Docker
|
||||
- Projects pending deletion will render a 404 page
|
||||
- Measure queue duration between gitlab-workhorse and Rails
|
||||
- Make Omniauth providers specs to not modify global configuration
|
||||
- Make authentication service for Container Registry to be compatible with < Docker 1.11
|
||||
- Add Application Setting to configure Container Registry token expire delay (default 5min)
|
||||
- Cache assigned issue and merge request counts in sidebar nav
|
||||
- Use Knapsack only in CI environment
|
||||
- Cache project build count in sidebar nav
|
||||
- Add milestone expire date to the right sidebar
|
||||
- Fix markdown_spec to use before instead of before(:all) to properly cleanup database after testing
|
||||
- Reduce number of queries needed to render issue labels in the sidebar
|
||||
- Improve error handling importing projects
|
||||
|
@ -55,19 +59,21 @@ v 8.9.0 (unreleased)
|
|||
- RepositoryCheck::SingleRepositoryWorker public and private methods are now instrumented
|
||||
- Improve issuables APIs performance when accessing notes !4471
|
||||
- External links now open in a new tab
|
||||
- Markdown editor now correctly resets the input value on edit cancellation !4175
|
||||
- Toggling a task list item in a issue/mr description does not creates a Todo for mentions
|
||||
- Improved UX of date pickers on issue & milestone forms
|
||||
|
||||
v 8.8.4 (unreleased)
|
||||
v 8.8.5 (unreleased)
|
||||
- Ensure branch cleanup regardless of whether the GitHub import process succeeds
|
||||
- Fix issue with arrow keys not working in search autocomplete dropdown
|
||||
- Fix todos page throwing errors when you have a project pending deletion
|
||||
- Reduce number of SQL queries when rendering user references
|
||||
- Upgrade to jQuery 2
|
||||
- Remove prev/next buttons on issues and merge requests
|
||||
- Import GitHub repositories respecting the API rate limit
|
||||
- Fix importer for GitHub comments on diff
|
||||
- Disable Webhooks before proceeding with the GitHub import
|
||||
- Added descriptions to notification settings dropdown
|
||||
- Markdown editor now correctly resets the input value on edit cancellation !4175
|
||||
|
||||
v 8.8.4
|
||||
- Fix LDAP-based login for users with 2FA enabled. !4493
|
||||
|
||||
v 8.8.3
|
||||
- Fix 404 page when viewing TODOs that contain milestones or labels in different projects. !4312
|
||||
|
@ -179,6 +185,7 @@ v 8.8.0
|
|||
- Fixed advice on invalid permissions on upload path !2948 (Ludovic Perrine)
|
||||
- Allows MR authors to have the source branch removed when merging the MR. !2801 (Jeroen Jacobs)
|
||||
- When creating a .gitignore file a dropdown with templates will be provided
|
||||
- Shows the issue/MR list search/filter form and corrects the mobile styling for guest users. #17562
|
||||
|
||||
v 8.7.7
|
||||
- Fix import by `Any Git URL` broken if the URL contains a space
|
||||
|
|
|
@ -1,11 +1,14 @@
|
|||
class @Activities
|
||||
constructor: ->
|
||||
Pager.init 20, true
|
||||
Pager.init 20, true, false, @updateTooltips
|
||||
$(".event-filter-link").on "click", (event) =>
|
||||
event.preventDefault()
|
||||
@toggleFilter($(event.currentTarget))
|
||||
@reloadActivities()
|
||||
|
||||
updateTooltips: ->
|
||||
gl.utils.localTimeAgo($('.js-timeago', '#activity'))
|
||||
|
||||
reloadActivities: ->
|
||||
$(".content_list").html ''
|
||||
Pager.init 20, true
|
||||
|
|
|
@ -35,7 +35,6 @@
|
|||
#= require raphael
|
||||
#= require g.raphael
|
||||
#= require g.bar
|
||||
#= require Chart
|
||||
#= require branch-graph
|
||||
#= require ace/ace
|
||||
#= require ace/ext-searchbox
|
||||
|
@ -226,6 +225,10 @@ $ ->
|
|||
form = btn.closest("form")
|
||||
new ConfirmDangerModal(form, text)
|
||||
|
||||
|
||||
$(document).on 'click', 'button', ->
|
||||
$(this).blur()
|
||||
|
||||
$('input[type="search"]').each ->
|
||||
$this = $(this)
|
||||
$this.attr 'value', $this.val()
|
||||
|
@ -268,5 +271,6 @@ $ ->
|
|||
.on "resize", (e) ->
|
||||
fitSidebarForSize()
|
||||
|
||||
gl.awardsHandler = new AwardsHandler()
|
||||
checkInitialSidebarSize()
|
||||
new Aside()
|
||||
|
|
|
@ -65,7 +65,7 @@ class @AwardsHandler
|
|||
$addBtn.removeClass 'is-loading'
|
||||
$menu = $('.emoji-menu')
|
||||
@positionMenu($menu, $addBtn)
|
||||
@renderFrequentlyUsedBlock()
|
||||
@renderFrequentlyUsedBlock() unless @frequentEmojiBlockRendered
|
||||
|
||||
setTimeout =>
|
||||
$menu.addClass 'is-visible'
|
||||
|
@ -100,7 +100,7 @@ class @AwardsHandler
|
|||
$menu.css(css)
|
||||
|
||||
|
||||
addAward: (votesBlock, awardUrl, emoji, checkMutuality = yes, callback) ->
|
||||
addAward: (votesBlock, awardUrl, emoji, checkMutuality = true, callback) ->
|
||||
|
||||
emoji = @normilizeEmojiName emoji
|
||||
|
||||
|
@ -111,7 +111,7 @@ class @AwardsHandler
|
|||
$('.emoji-menu').removeClass 'is-visible'
|
||||
|
||||
|
||||
addAwardToEmojiBar: (votesBlock, emoji, checkForMutuality = yes) ->
|
||||
addAwardToEmojiBar: (votesBlock, emoji, checkForMutuality = true) ->
|
||||
|
||||
@checkMutuality votesBlock, emoji if checkForMutuality
|
||||
@addEmojiToFrequentlyUsedList emoji
|
||||
|
@ -153,7 +153,7 @@ class @AwardsHandler
|
|||
|
||||
if isAlreadyVoted
|
||||
@showEmojiLoader $emojiButton
|
||||
@addAward votesBlock, awardUrl, mutualVote, no, ->
|
||||
@addAward votesBlock, awardUrl, mutualVote, false, ->
|
||||
$emojiButton.removeClass 'is-loading'
|
||||
|
||||
|
||||
|
@ -282,7 +282,7 @@ class @AwardsHandler
|
|||
@createEmojiMenu @getAwardMenuUrl(), => @createEmoji_ votesBlock, emoji
|
||||
|
||||
|
||||
getAwardMenuUrl: -> return gl.awardMenuUrl
|
||||
getAwardMenuUrl: -> return gon.award_menu_url
|
||||
|
||||
|
||||
resolveNameToCssClass: (emoji) ->
|
||||
|
@ -336,13 +336,15 @@ class @AwardsHandler
|
|||
if $.cookie 'frequently_used_emojis'
|
||||
frequentlyUsedEmojis = @getFrequentlyUsedEmojis()
|
||||
|
||||
ul = $("<ul class='clearfix emoji-menu-list'>")
|
||||
ul = $("<ul class='clearfix emoji-menu-list frequent-emojis'>")
|
||||
|
||||
for emoji in frequentlyUsedEmojis
|
||||
$(".emoji-menu-content [data-emoji='#{emoji}']").closest('li').clone().appendTo(ul)
|
||||
|
||||
$('input.emoji-search').after(ul).after($('<h5>').text('Frequently used'))
|
||||
|
||||
@frequentEmojiBlockRendered = true
|
||||
|
||||
|
||||
setupSearch: ->
|
||||
|
||||
|
@ -365,4 +367,4 @@ class @AwardsHandler
|
|||
|
||||
searchEmojis: (term) ->
|
||||
|
||||
$(".emoji-menu-content [data-emoji*='#{term}']").closest('li').clone()
|
||||
$(".emoji-menu-list:not(.frequent-emojis) [data-emoji*='#{term}']").closest('li').clone()
|
||||
|
|
|
@ -23,7 +23,6 @@ class Dispatcher
|
|||
new Issue()
|
||||
shortcut_handler = new ShortcutsIssuable()
|
||||
new ZenMode()
|
||||
gl.awardsHandler = new AwardsHandler()
|
||||
when 'projects:milestones:show', 'groups:milestones:show', 'dashboard:milestones:show'
|
||||
new Milestone()
|
||||
when 'dashboard:todos:index'
|
||||
|
@ -54,7 +53,6 @@ class Dispatcher
|
|||
new Diff()
|
||||
shortcut_handler = new ShortcutsIssuable(true)
|
||||
new ZenMode()
|
||||
gl.awardsHandler = new AwardsHandler()
|
||||
when "projects:merge_requests:diffs"
|
||||
new Diff()
|
||||
new ZenMode()
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
window.GitLab ?= {}
|
||||
GitLab.GfmAutoComplete =
|
||||
dataLoading: false
|
||||
dataLoaded: false
|
||||
|
||||
dataSource: ''
|
||||
|
||||
|
@ -35,7 +36,7 @@ GitLab.GfmAutoComplete =
|
|||
|
||||
$.fn.atwho.default.callbacks.filter(query, data, searchKey)
|
||||
beforeInsert: (value) ->
|
||||
if value.indexOf('undefined')
|
||||
if not GitLab.GfmAutoComplete.dataLoaded
|
||||
@at
|
||||
else
|
||||
value
|
||||
|
@ -182,6 +183,8 @@ GitLab.GfmAutoComplete =
|
|||
$.getJSON(dataSource)
|
||||
|
||||
loadData: (data) ->
|
||||
@dataLoaded = true
|
||||
|
||||
# load members
|
||||
@input.atwho 'load', '@', data.members
|
||||
# load issues
|
||||
|
|
|
@ -4,4 +4,5 @@
|
|||
# It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
|
||||
# the compiled file.
|
||||
#
|
||||
#= require Chart
|
||||
#= require_tree .
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
((w) ->
|
||||
|
||||
jQuery.timefor = (time, suffix, expiredLabel) ->
|
||||
|
||||
return '' unless time
|
||||
|
||||
suffix or= 'remaining'
|
||||
expiredLabel or= 'Past due'
|
||||
|
||||
jQuery.timeago.settings.allowFuture = yes
|
||||
|
||||
{ suffixFromNow } = jQuery.timeago.settings.strings
|
||||
jQuery.timeago.settings.strings.suffixFromNow = suffix
|
||||
|
||||
timefor = $.timeago time
|
||||
|
||||
if timefor.indexOf('ago') > -1
|
||||
timefor = expiredLabel
|
||||
|
||||
jQuery.timeago.settings.strings.suffixFromNow = suffixFromNow
|
||||
|
||||
return timefor
|
||||
|
||||
) window
|
|
@ -12,6 +12,13 @@
|
|||
$el.attr('title', gl.utils.formatDate($el.attr('datetime')))
|
||||
)
|
||||
|
||||
$timeagoEls.timeago() if setTimeago
|
||||
if setTimeago
|
||||
$timeagoEls.timeago()
|
||||
$timeagoEls.tooltip('destroy')
|
||||
|
||||
# Recreate with custom template
|
||||
$timeagoEls.tooltip(
|
||||
template: '<div class="tooltip local-timeago" role="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>'
|
||||
)
|
||||
|
||||
) window
|
||||
|
|
|
@ -24,11 +24,21 @@ class @MilestoneSelect
|
|||
|
||||
if issueUpdateURL
|
||||
milestoneLinkTemplate = _.template(
|
||||
'<a href="/<%= namespace %>/<%= path %>/milestones/<%= iid %>"><%= _.escape(title) %></a>'
|
||||
'<a href="/<%= namespace %>/<%= path %>/milestones/<%= iid %>">
|
||||
<span class="has-tooltip" data-container="body" title="<%= remaining %>">
|
||||
<%= _.escape(title) %>
|
||||
</span>
|
||||
</a>'
|
||||
)
|
||||
|
||||
milestoneLinkNoneTemplate = '<div class="light">None</div>'
|
||||
|
||||
collapsedSidebarLabelTemplate = _.template(
|
||||
'<span class="has-tooltip" data-container="body" title="<%= remaining %>" data-placement="left">
|
||||
<%= _.escape(title) %>
|
||||
</span>'
|
||||
)
|
||||
|
||||
$dropdown.glDropdown(
|
||||
data: (term, callback) ->
|
||||
$.ajax(
|
||||
|
@ -122,8 +132,9 @@ class @MilestoneSelect
|
|||
if data.milestone?
|
||||
data.milestone.namespace = _this.currentProject.namespace
|
||||
data.milestone.path = _this.currentProject.path
|
||||
data.milestone.remaining = $.timefor data.milestone.due_date
|
||||
$value.html(milestoneLinkTemplate(data.milestone))
|
||||
$sidebarCollapsedValue.find('span').text(data.milestone.title)
|
||||
$sidebarCollapsedValue.find('span').html(collapsedSidebarLabelTemplate(data.milestone))
|
||||
else
|
||||
$value.html(milestoneLinkNoneTemplate)
|
||||
$sidebarCollapsedValue.find('span').text('No')
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
@Pager =
|
||||
init: (@limit = 0, preload, @disable = false) ->
|
||||
init: (@limit = 0, preload, @disable = false, @callback = $.noop) ->
|
||||
@loading = $('.loading').first()
|
||||
|
||||
if preload
|
||||
|
@ -19,6 +19,7 @@
|
|||
@loading.hide()
|
||||
success: (data) ->
|
||||
Pager.append(data.count, data.html)
|
||||
Pager.callback()
|
||||
dataType: "json"
|
||||
|
||||
append: (count, html) ->
|
||||
|
|
|
@ -19,3 +19,8 @@ class @Subscription
|
|||
action = if status == 'subscribed' then 'Unsubscribe' else 'Subscribe'
|
||||
btn.find('span').text(action)
|
||||
@subscription_status.find('>div').toggleClass('hidden')
|
||||
|
||||
if btn.attr('data-original-title')
|
||||
btn.tooltip('hide')
|
||||
.attr('data-original-title', action)
|
||||
.tooltip('fixTitle')
|
||||
|
|
|
@ -122,6 +122,9 @@ class @UserTabs
|
|||
@parentEl.find(tabSelector).html(data.html)
|
||||
@loaded[action] = true
|
||||
|
||||
# Fix tooltips
|
||||
gl.utils.localTimeAgo($('.js-timeago', tabSelector))
|
||||
|
||||
loadActivities: (source) ->
|
||||
return if @loaded['activity'] is true
|
||||
|
||||
|
|
|
@ -79,6 +79,23 @@
|
|||
@include btn-color($white-light, $border-color, $white-normal, $border-white-normal, $white-dark, $border-white-dark, $btn-white-active);
|
||||
}
|
||||
|
||||
@mixin btn-with-margin {
|
||||
margin-left: $btn-side-margin;
|
||||
float: left;
|
||||
|
||||
&.inline {
|
||||
float: none;
|
||||
}
|
||||
|
||||
&.btn-sm {
|
||||
margin-left: $btn-sm-side-margin;
|
||||
}
|
||||
|
||||
&.btn-xs {
|
||||
margin-left: $btn-xs-side-margin;
|
||||
}
|
||||
}
|
||||
|
||||
.btn {
|
||||
@include btn-default;
|
||||
@include btn-white;
|
||||
|
@ -142,24 +159,7 @@
|
|||
}
|
||||
|
||||
&.btn-grouped {
|
||||
margin-right: $btn-side-margin;
|
||||
float: left;
|
||||
|
||||
&.inline {
|
||||
float: none;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
&.btn-sm {
|
||||
margin-right: $btn-sm-side-margin;
|
||||
}
|
||||
|
||||
&.btn-xs {
|
||||
margin-right: $btn-xs-side-margin;
|
||||
}
|
||||
@include btn-with-margin;
|
||||
}
|
||||
|
||||
&.disabled {
|
||||
|
@ -203,11 +203,7 @@
|
|||
|
||||
.btn-group {
|
||||
&.btn-grouped {
|
||||
margin-right: 7px;
|
||||
float: left;
|
||||
&:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
@include btn-with-margin;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -76,6 +76,7 @@ label {
|
|||
.form-control {
|
||||
@include box-shadow(none);
|
||||
border-radius: 3px;
|
||||
padding: $gl-vert-padding $gl-input-padding;
|
||||
}
|
||||
|
||||
.select-wrapper {
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
font-family: $regular_font;
|
||||
font-size: $font-size-base;
|
||||
|
||||
&.ui-datepicker,
|
||||
&.ui-datepicker-inline {
|
||||
border: 1px solid #ddd;
|
||||
padding: 10px;
|
||||
|
@ -10,6 +11,25 @@
|
|||
.ui-datepicker-header {
|
||||
background: #fff;
|
||||
border-color: #ddd;
|
||||
|
||||
.ui-datepicker-prev,
|
||||
.ui-datepicker-next {
|
||||
top: 4px;
|
||||
}
|
||||
|
||||
.ui-datepicker-prev {
|
||||
left: 2px;
|
||||
}
|
||||
|
||||
.ui-datepicker-next {
|
||||
right: 2px;
|
||||
}
|
||||
|
||||
.ui-state-hover {
|
||||
background: transparent;
|
||||
border: 0;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
.ui-datepicker-calendar td a {
|
||||
|
@ -36,21 +56,18 @@
|
|||
}
|
||||
|
||||
.ui-state-highlight {
|
||||
border: 1px solid #eee;
|
||||
background: #eee;
|
||||
border: 0;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.ui-state-active {
|
||||
border: 1px solid $gl-primary;
|
||||
background: $gl-primary;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.ui-state-hover,
|
||||
.ui-state-focus {
|
||||
border: 1px solid $row-hover;
|
||||
background: $row-hover;
|
||||
color: #333;
|
||||
.ui-datepicker-calendar {
|
||||
.ui-state-active,
|
||||
.ui-state-hover,
|
||||
.ui-state-focus {
|
||||
border: 1px solid $gl-primary;
|
||||
background: $gl-primary;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -137,8 +137,16 @@ ul.content-list {
|
|||
padding-top: 1px;
|
||||
float: right;
|
||||
|
||||
.btn {
|
||||
padding: 10px 14px;
|
||||
> .btn,
|
||||
> .btn-group {
|
||||
margin-right: $gl-padding-top;
|
||||
display: inline-block;
|
||||
margin-top: 4px;
|
||||
margin-bottom: 4px;
|
||||
|
||||
&:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -171,6 +171,7 @@
|
|||
> form {
|
||||
display: inline-block;
|
||||
margin-top: -1px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.icon-label {
|
||||
|
@ -207,7 +208,7 @@
|
|||
|
||||
@media (max-width: $screen-xs-max) {
|
||||
padding-bottom: 0;
|
||||
|
||||
width: 100%;
|
||||
.btn, form, .dropdown, .dropdown-menu-toggle, .form-control {
|
||||
margin: 0 0 10px;
|
||||
display: block;
|
||||
|
@ -238,16 +239,6 @@
|
|||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Small devices (tablets, 768px and lower) */
|
||||
@media (max-width: $screen-sm-max) {
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
|
||||
input {
|
||||
width: 300px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
background: #fff;
|
||||
border-color: $input-border;
|
||||
height: 35px;
|
||||
padding: $gl-vert-padding $gl-btn-padding;
|
||||
padding: $gl-vert-padding $gl-input-padding;
|
||||
font-size: $gl-font-size;
|
||||
line-height: 1.42857143;
|
||||
border-radius: $border-radius-base;
|
||||
|
|
|
@ -192,3 +192,8 @@
|
|||
.text-info:hover {
|
||||
color: $brand-info;
|
||||
}
|
||||
|
||||
// Prevent datetimes on tooltips to break into two lines
|
||||
.local-timeago {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
|
|
@ -57,6 +57,7 @@ $code_line_height: 1.5;
|
|||
*/
|
||||
$gl-padding: 16px;
|
||||
$gl-btn-padding: 10px;
|
||||
$gl-input-padding: 10px;
|
||||
$gl-vert-padding: 6px;
|
||||
$gl-padding-top: 10px;
|
||||
|
||||
|
@ -79,8 +80,8 @@ $provider-btn-not-active-color: #4688f1;
|
|||
$link-underline-blue: #4a8bee;
|
||||
$layout-link-gray: #7e7c7c;
|
||||
$todo-alert-blue: #428bca;
|
||||
$btn-side-margin: 7px;
|
||||
$btn-sm-side-margin: 5px;
|
||||
$btn-side-margin: 10px;
|
||||
$btn-sm-side-margin: 7px;
|
||||
$btn-xs-side-margin: 5px;
|
||||
|
||||
/*
|
||||
|
|
|
@ -50,11 +50,26 @@
|
|||
|
||||
.label-row {
|
||||
.label-name {
|
||||
display: inline-block;
|
||||
width: 170px;
|
||||
display: block;
|
||||
margin-bottom: 10px;
|
||||
|
||||
@media (max-width: $screen-xs-min) {
|
||||
display: block;
|
||||
@media (min-width: $screen-sm-min) {
|
||||
display: inline-block;
|
||||
width: 200px;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.label-description {
|
||||
display: block;
|
||||
margin-bottom: 10px;
|
||||
|
||||
@media (min-width: $screen-sm-min) {
|
||||
display: inline-block;
|
||||
width: 40%;
|
||||
margin-left: 10px;
|
||||
margin-bottom: 0;
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -68,10 +83,6 @@
|
|||
padding: 3px 4px;
|
||||
}
|
||||
|
||||
.label-subscription {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.dropdown-labels-error {
|
||||
padding: 5px 10px;
|
||||
margin-bottom: 10px;
|
||||
|
@ -79,62 +90,27 @@
|
|||
color: $white-light;
|
||||
}
|
||||
|
||||
@mixin labels-mobile {
|
||||
@media (max-width: $screen-xs-min) {
|
||||
display: block;
|
||||
width: 100%;
|
||||
margin-left: 0;
|
||||
padding: 10px 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.manage-labels-list {
|
||||
.btn-action {
|
||||
color: $gl-dark-link-color;
|
||||
|
||||
.prepend-left-10, .prepend-description-left {
|
||||
display: inline-block;
|
||||
width: 40%;
|
||||
vertical-align: middle;
|
||||
.fa {
|
||||
font-size: 18px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
@include labels-mobile;
|
||||
&:hover {
|
||||
color: $gl-link-color;
|
||||
|
||||
&.remove-row {
|
||||
color: $gl-danger;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.prepend-description-left {
|
||||
width: 57%;
|
||||
|
||||
@include labels-mobile;
|
||||
}
|
||||
|
||||
.pull-info-right {
|
||||
float: right;
|
||||
|
||||
@media (max-width: $screen-xs-min) {
|
||||
float: none;
|
||||
}
|
||||
|
||||
.action-buttons {
|
||||
border-color: transparent;
|
||||
padding: 6px;
|
||||
color: $gl-text-color;
|
||||
|
||||
&.label-subscribe-button {
|
||||
padding-left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
i {
|
||||
color: $gl-text-color;
|
||||
}
|
||||
|
||||
.append-right-20 {
|
||||
a {
|
||||
color: $gl-text-color;
|
||||
}
|
||||
|
||||
@media (max-width: $screen-xs-min) {
|
||||
display: block;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.dropdown {
|
||||
@media (min-width: $screen-sm-min) {
|
||||
float: right;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -186,3 +162,23 @@
|
|||
color: inherit;
|
||||
}
|
||||
}
|
||||
|
||||
.label-options-toggle {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.label-subscribe-button {
|
||||
.label-subscribe-button-loading {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&.disabled {
|
||||
.label-subscribe-button-icon {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.label-subscribe-button-loading {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -108,6 +108,11 @@
|
|||
font-size: 17px;
|
||||
margin: 5px 0;
|
||||
color: $gl-gray-dark;
|
||||
|
||||
&.has-conflicts .fa-exclamation-triangle {
|
||||
color: $gl-warning;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
p:last-child {
|
||||
|
|
|
@ -129,17 +129,8 @@
|
|||
display: none;
|
||||
font-size: 15px;
|
||||
|
||||
.form-actions {
|
||||
padding-left: 20px;
|
||||
|
||||
.btn-save {
|
||||
float: left;
|
||||
}
|
||||
|
||||
.note-form-option {
|
||||
float: left;
|
||||
padding: 2px 0 0 25px;
|
||||
}
|
||||
.md-area {
|
||||
background-color: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -32,7 +32,7 @@ module LabelsHelper
|
|||
# link_to_label(label) { "My Custom Label Text" }
|
||||
#
|
||||
# Returns a String
|
||||
def link_to_label(label, project: nil, type: :issue, tooltip: true, &block)
|
||||
def link_to_label(label, project: nil, type: :issue, tooltip: true, css_class: nil, &block)
|
||||
project ||= @project || label.project
|
||||
link = send("namespace_project_#{type.to_s.pluralize}_path",
|
||||
project.namespace,
|
||||
|
@ -40,9 +40,9 @@ module LabelsHelper
|
|||
label_name: [label.name])
|
||||
|
||||
if block_given?
|
||||
link_to link, &block
|
||||
link_to link, class: css_class, &block
|
||||
else
|
||||
link_to render_colored_label(label, tooltip: tooltip), link
|
||||
link_to render_colored_label(label, tooltip: tooltip), link, class: css_class
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -56,7 +56,7 @@ module MilestonesHelper
|
|||
|
||||
def milestone_remaining_days(milestone)
|
||||
if milestone.expired?
|
||||
content_tag(:strong, 'expired')
|
||||
content_tag(:strong, 'Past due')
|
||||
elsif milestone.due_date
|
||||
days = milestone.remaining_days
|
||||
content = content_tag(:strong, days)
|
||||
|
|
|
@ -20,7 +20,7 @@ class TodoService
|
|||
# * mark all pending todos related to the issue for the current user as done
|
||||
#
|
||||
def update_issue(issue, current_user)
|
||||
create_mention_todos(issue.project, issue, current_user)
|
||||
update_issuable(issue, current_user)
|
||||
end
|
||||
|
||||
# When close an issue we should:
|
||||
|
@ -53,7 +53,7 @@ class TodoService
|
|||
# * create a todo for each mentioned user on merge request
|
||||
#
|
||||
def update_merge_request(merge_request, current_user)
|
||||
create_mention_todos(merge_request.project, merge_request, current_user)
|
||||
update_issuable(merge_request, current_user)
|
||||
end
|
||||
|
||||
# When close a merge request we should:
|
||||
|
@ -153,6 +153,13 @@ class TodoService
|
|||
create_mention_todos(issuable.project, issuable, author)
|
||||
end
|
||||
|
||||
def update_issuable(issuable, author)
|
||||
# Skip toggling a task list item in a description
|
||||
return if issuable.tasks? && issuable.updated_tasks.any?
|
||||
|
||||
create_mention_todos(issuable.project, issuable, author)
|
||||
end
|
||||
|
||||
def handle_note(note, author)
|
||||
# Skip system notes, and notes on project snippet
|
||||
return if note.system? || note.for_snippet?
|
||||
|
|
|
@ -7,9 +7,6 @@
|
|||
= awards.count
|
||||
|
||||
- if current_user
|
||||
:javascript
|
||||
gl.awardMenuUrl = "#{emojis_path}"
|
||||
|
||||
.award-menu-holder.js-award-holder
|
||||
%button.btn.award-control.js-add-award{ type: "button" }
|
||||
= icon('smile-o', class: "award-control-icon award-control-icon-normal")
|
||||
|
|
|
@ -6,7 +6,8 @@
|
|||
- if @user.two_factor_otp_enabled?
|
||||
%h5 Authenticate via Two-Factor App
|
||||
= form_for(resource, as: resource_name, url: session_path(resource_name), method: :post) do |f|
|
||||
= f.hidden_field :remember_me, value: params[resource_name][:remember_me]
|
||||
- resource_params = params[resource_name].presence || params
|
||||
= f.hidden_field :remember_me, value: resource_params.fetch(:remember_me, 0)
|
||||
= f.text_field :otp_attempt, class: 'form-control', placeholder: 'Two-Factor Authentication code', required: true, autofocus: true, autocomplete: 'off'
|
||||
%p.help-block.hint Enter the code from the two-factor app on your mobile device. If you've lost your device, you may enter one of your recovery codes.
|
||||
.prepend-top-20
|
||||
|
|
|
@ -34,7 +34,7 @@
|
|||
%strong.member-access-level= member.human_access
|
||||
- if show_controls
|
||||
- if can?(current_user, :update_group_member, member)
|
||||
= button_tag class: "btn-xs btn js-toggle-button",
|
||||
= button_tag class: "btn-xs btn btn-grouped inline js-toggle-button",
|
||||
title: 'Edit access level', type: 'button' do
|
||||
= icon('pencil')
|
||||
|
||||
|
|
|
@ -39,9 +39,8 @@
|
|||
.col-md-6
|
||||
.form-group
|
||||
= f.label :due_date, "Due Date", class: "control-label"
|
||||
.col-sm-10= f.hidden_field :due_date
|
||||
.col-sm-10
|
||||
.datepicker
|
||||
= f.text_field :due_date, class: "datepicker form-control", placeholder: "Select due date"
|
||||
|
||||
.form-actions
|
||||
= f.submit 'Create Milestone', class: "btn-create btn"
|
||||
|
|
|
@ -3,31 +3,30 @@
|
|||
= render "projects/commits/head"
|
||||
|
||||
%div{ class: (container_class) }
|
||||
.row-content-block.second-block.content-component-block
|
||||
.pull-right
|
||||
- if can? current_user, :push_code, @project
|
||||
= link_to new_namespace_project_branch_path(@project.namespace, @project), class: 'btn btn-create' do
|
||||
= icon('plus')
|
||||
New branch
|
||||
|
||||
.dropdown.inline
|
||||
%button.dropdown-toggle.btn{type: 'button', 'data-toggle' => 'dropdown'}
|
||||
%span.light
|
||||
- if @sort.present?
|
||||
= @sort.humanize
|
||||
- else
|
||||
Name
|
||||
%b.caret
|
||||
%ul.dropdown-menu.dropdown-menu-align-right
|
||||
%li
|
||||
= link_to namespace_project_branches_path(sort: nil) do
|
||||
Name
|
||||
= link_to namespace_project_branches_path(sort: 'recently_updated') do
|
||||
= sort_title_recently_updated
|
||||
= link_to namespace_project_branches_path(sort: 'last_updated') do
|
||||
= sort_title_oldest_updated
|
||||
.oneline
|
||||
.top-area
|
||||
.nav-text
|
||||
Protected branches can be managed in project settings
|
||||
|
||||
- if can? current_user, :push_code, @project
|
||||
.nav-controls
|
||||
= link_to new_namespace_project_branch_path(@project.namespace, @project), class: 'btn btn-create' do
|
||||
New branch
|
||||
.dropdown.inline
|
||||
%button.dropdown-toggle.btn{type: 'button', 'data-toggle' => 'dropdown'}
|
||||
%span.light
|
||||
- if @sort.present?
|
||||
= @sort.humanize
|
||||
- else
|
||||
Name
|
||||
%b.caret
|
||||
%ul.dropdown-menu.dropdown-menu-align-right
|
||||
%li
|
||||
= link_to namespace_project_branches_path(sort: nil) do
|
||||
Name
|
||||
= link_to namespace_project_branches_path(sort: 'recently_updated') do
|
||||
= sort_title_recently_updated
|
||||
= link_to namespace_project_branches_path(sort: 'last_updated') do
|
||||
= sort_title_oldest_updated
|
||||
- unless @branches.empty?
|
||||
%ul.content-list.all-branches
|
||||
- @branches.each do |branch|
|
||||
|
|
|
@ -34,7 +34,6 @@
|
|||
= link_to 'Get started with Builds', help_page_path('ci/quick_start', 'README'), class: 'btn btn-info'
|
||||
|
||||
= link_to ci_lint_path, class: 'btn btn-default' do
|
||||
= icon('wrench')
|
||||
%span CI Lint
|
||||
|
||||
%ul.content-list
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
= icon('rss')
|
||||
%span.icon-label
|
||||
Subscribe
|
||||
= render 'shared/issuable/search_form', path: namespace_project_issues_path(@project.namespace, @project)
|
||||
= render 'shared/issuable/search_form', path: namespace_project_issues_path(@project.namespace, @project)
|
||||
- if can? current_user, :create_issue, @project
|
||||
= link_to new_namespace_project_issue_path(@project.namespace, @project, issue: { assignee_id: @issuable_finder.assignee.try(:id), milestone_id: @issuable_finder.milestones.try(:first).try(:id) }), class: "btn btn-new", title: "New Issue", id: "new_issue_link" do
|
||||
New Issue
|
||||
|
|
|
@ -1,28 +1,50 @@
|
|||
- label_css_id = dom_id(label)
|
||||
%li{id: label_css_id, data: { id: label.id } }
|
||||
= render "shared/label_row", label: label
|
||||
.pull-info-right
|
||||
%span.append-right-20
|
||||
= link_to_label(label, type: :merge_request) do
|
||||
= pluralize label.open_merge_requests_count, 'merge request'
|
||||
|
||||
%span.append-right-20
|
||||
= link_to_label(label) do
|
||||
= pluralize label.open_issues_count(current_user), 'open issue'
|
||||
.visible-xs.visible-sm-inline-block.visible-md-inline-block.dropdown
|
||||
%button.btn.btn-default.label-options-toggle{ data: { toggle: "dropdown" } }
|
||||
Options
|
||||
%span.caret
|
||||
.dropdown-menu.dropdown-menu-align-right
|
||||
%ul
|
||||
%li
|
||||
= link_to_label(label, type: :merge_request) do
|
||||
= pluralize label.open_merge_requests_count, 'merge request'
|
||||
%li
|
||||
= link_to_label(label) do
|
||||
= pluralize label.open_issues_count(current_user), 'open issue'
|
||||
- if current_user
|
||||
%li.label-subscription{ data: { url: toggle_subscription_namespace_project_label_path(@project.namespace, @project, label) } }
|
||||
%a.js-subscribe-button.label-subscribe-button.subscription-status{ role: "button", href: "#", data: { toggle: "tooltip", status: label_subscription_status(label) } }
|
||||
%span= label_subscription_toggle_button_text(label)
|
||||
- if can? current_user, :admin_label, @project
|
||||
%li
|
||||
= link_to "Edit", edit_namespace_project_label_path(@project.namespace, @project, label)
|
||||
%li
|
||||
= link_to "Delete", namespace_project_label_path(@project.namespace, @project, label), title: "Delete", method: :delete, remote: true, data: {confirm: "Remove this label? Are you sure?"}
|
||||
|
||||
.pull-right.hidden-xs.hidden-sm.hidden-md
|
||||
= link_to_label(label, type: :merge_request, css_class: 'btn btn-transparent btn-action') do
|
||||
= pluralize label.open_merge_requests_count, 'merge request'
|
||||
= link_to_label(label, css_class: 'btn btn-transparent btn-action') do
|
||||
= pluralize label.open_issues_count(current_user), 'open issue'
|
||||
|
||||
- if current_user
|
||||
.label-subscription{ data: { url: toggle_subscription_namespace_project_label_path(@project.namespace, @project, label) } }
|
||||
.subscription-status{ data: { status: label_subscription_status(label) } }
|
||||
.label-subscription.inline{ data: { url: toggle_subscription_namespace_project_label_path(@project.namespace, @project, label) } }
|
||||
%button.js-subscribe-button.label-subscribe-button.btn.btn-transparent.btn-action.subscription-status{ type: "button", title: label_subscription_toggle_button_text(label), data: { toggle: "tooltip", status: label_subscription_status(label) } }
|
||||
%span.sr-only= label_subscription_toggle_button_text(label)
|
||||
= icon('eye', class: 'label-subscribe-button-icon')
|
||||
= icon('spinner spin', class: 'label-subscribe-button-loading')
|
||||
|
||||
%button.js-subscribe-button.label-subscribe-button.btn.action-buttons{ type: "button", data: { toggle: "tooltip" } }
|
||||
%span= label_subscription_toggle_button_text(label)
|
||||
- if can? current_user, :admin_label, @project
|
||||
= link_to edit_namespace_project_label_path(@project.namespace, @project, label), title: "Edit", class: 'btn btn-transparent btn-action', data: {toggle: "tooltip"} do
|
||||
%span.sr-only Edit
|
||||
= icon('pencil-square-o')
|
||||
= link_to namespace_project_label_path(@project.namespace, @project, label), title: "Delete", class: 'btn btn-transparent btn-action remove-row', method: :delete, remote: true, data: {confirm: "Remove this label? Are you sure?", toggle: "tooltip"} do
|
||||
%span.sr-only Delete
|
||||
= icon('trash-o')
|
||||
|
||||
- if can?(current_user, :admin_label, @project)
|
||||
= link_to edit_namespace_project_label_path(@project.namespace, @project, label), title: "Edit", class: 'btn action-buttons', data: { toggle: 'tooltip' } do
|
||||
%i.fa.fa-pencil-square-o
|
||||
= link_to namespace_project_label_path(@project.namespace, @project, label), title: "Delete", class: 'btn action-buttons remove-row', method: :delete, remote: true, data: { confirm: 'Remove this label? Are you sure?', toggle: 'tooltip' } do
|
||||
%i.fa.fa-trash-o
|
||||
|
||||
- if current_user
|
||||
:javascript
|
||||
new Subscription('##{label_css_id} .label-subscription');
|
||||
- if current_user
|
||||
:javascript
|
||||
new Subscription('##{dom_id(label)} .label-subscription');
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
%h4
|
||||
%h4.has-conflicts
|
||||
= icon("exclamation-triangle")
|
||||
This merge request contains merge conflicts
|
||||
|
||||
|
|
|
@ -17,9 +17,8 @@
|
|||
.col-md-6
|
||||
.form-group
|
||||
= f.label :due_date, "Due Date", class: "control-label"
|
||||
.col-sm-10= f.hidden_field :due_date
|
||||
.col-sm-10
|
||||
.datepicker
|
||||
= f.text_field :due_date, class: "datepicker form-control", placeholder: "Select due date"
|
||||
|
||||
.form-actions
|
||||
- if @milestone.new_record?
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
.nav-controls
|
||||
- if can?(current_user, :admin_milestone, @project)
|
||||
= link_to new_namespace_project_milestone_path(@project.namespace, @project), class: "btn btn-new", title: "New Milestone" do
|
||||
= icon('plus')
|
||||
New Milestone
|
||||
|
||||
.milestones
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
- if @milestone.closed?
|
||||
Closed
|
||||
- elsif @milestone.expired?
|
||||
Expired
|
||||
Past due
|
||||
- else
|
||||
Open
|
||||
%span.identifier
|
||||
|
@ -23,11 +23,9 @@
|
|||
= link_to 'Reopen Milestone', namespace_project_milestone_path(@project.namespace, @project, @milestone, milestone: {state_event: :activate }), method: :put, class: "btn btn-reopen btn-nr btn-grouped"
|
||||
|
||||
= link_to edit_namespace_project_milestone_path(@project.namespace, @project, @milestone), class: "btn btn-grouped btn-nr" do
|
||||
= icon('pencil-square-o')
|
||||
Edit
|
||||
|
||||
= link_to namespace_project_milestone_path(@project.namespace, @project, @milestone), data: { confirm: 'Are you sure?' }, method: :delete, class: "btn btn-grouped btn-danger" do
|
||||
= icon('trash-o')
|
||||
Delete
|
||||
|
||||
.detail-page-description.milestone-detail
|
||||
|
|
|
@ -28,14 +28,12 @@
|
|||
.nav-controls
|
||||
- if can? current_user, :create_pipeline, @project
|
||||
= link_to new_namespace_project_pipeline_path(@project.namespace, @project), class: 'btn btn-create' do
|
||||
= icon('plus')
|
||||
New pipeline
|
||||
|
||||
- unless @repository.gitlab_ci_yml
|
||||
= link_to 'Get started with Pipelines', help_page_path('ci/quick_start', 'README'), class: 'btn btn-info'
|
||||
|
||||
= link_to ci_lint_path, class: 'btn btn-default' do
|
||||
= icon('wrench')
|
||||
%span CI Lint
|
||||
|
||||
%ul.content-list.pipelines
|
||||
|
|
|
@ -32,7 +32,7 @@
|
|||
.pull-right
|
||||
%strong= member.human_access
|
||||
- if can?(current_user, :update_project_member, member)
|
||||
= button_tag class: "btn-xs btn js-toggle-button",
|
||||
= button_tag class: "btn-xs btn-grouped inline btn js-toggle-button",
|
||||
title: 'Edit access level', type: 'button' do
|
||||
= icon('pencil')
|
||||
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
%span.btn-group.btn-grouped
|
||||
%span.btn-group
|
||||
= link_to archive_namespace_project_repository_path(project.namespace, project, ref: ref, format: 'zip'), class: 'btn btn-default', rel: 'nofollow' do
|
||||
%i.fa.fa-download
|
||||
%span source code
|
||||
%span Source code
|
||||
%a.btn.btn-default.dropdown-toggle{ 'data-toggle' => 'dropdown' }
|
||||
%span.caret
|
||||
%span.sr-only
|
||||
|
@ -9,9 +8,7 @@
|
|||
%ul.dropdown-menu.dropdown-menu-align-right{ role: 'menu' }
|
||||
%li
|
||||
= link_to archive_namespace_project_repository_path(project.namespace, project, ref: ref, format: 'zip'), rel: 'nofollow' do
|
||||
%i.fa.fa-download
|
||||
%span Download zip
|
||||
%li
|
||||
= link_to archive_namespace_project_repository_path(project.namespace, project, ref: ref, format: 'tar.gz'), rel: 'nofollow' do
|
||||
%i.fa.fa-download
|
||||
%span Download tar.gz
|
||||
|
|
|
@ -15,11 +15,11 @@
|
|||
= render 'projects/tags/download', ref: tag.name, project: @project
|
||||
|
||||
- if can?(current_user, :push_code, @project)
|
||||
= link_to edit_namespace_project_tag_release_path(@project.namespace, @project, tag.name), class: 'btn-grouped btn has-tooltip', title: "Edit release notes" do
|
||||
= link_to edit_namespace_project_tag_release_path(@project.namespace, @project, tag.name), class: 'btn has-tooltip', title: "Edit release notes" do
|
||||
= icon("pencil")
|
||||
|
||||
- if can?(current_user, :admin_project, @project)
|
||||
= link_to namespace_project_tag_path(@project.namespace, @project, tag.name), class: 'btn btn-grouped btn-xs btn-remove remove-row has-tooltip', title: "Delete tag", method: :delete, data: { confirm: "Deleting the '#{tag.name}' tag cannot be undone. Are you sure?", container: 'body' }, remote: true do
|
||||
= link_to namespace_project_tag_path(@project.namespace, @project, tag.name), class: 'btn btn-remove remove-row has-tooltip', title: "Delete tag", method: :delete, data: { confirm: "Deleting the '#{tag.name}' tag cannot be undone. Are you sure?", container: 'body' }, remote: true do
|
||||
= icon("trash-o")
|
||||
|
||||
- if commit
|
||||
|
|
|
@ -3,15 +3,15 @@
|
|||
= render "projects/commits/head"
|
||||
|
||||
%div{ class: (container_class) }
|
||||
.row-content-block.second-block.content-component-block
|
||||
- if can? current_user, :push_code, @project
|
||||
.pull-right
|
||||
= link_to new_namespace_project_tag_path(@project.namespace, @project), class: 'btn btn-create new-tag-btn' do
|
||||
= icon('plus')
|
||||
New tag
|
||||
.oneline
|
||||
.top-area
|
||||
.nav-text
|
||||
Tags give the ability to mark specific points in history as being important
|
||||
|
||||
- if can? current_user, :push_code, @project
|
||||
.nav-controls
|
||||
= link_to new_namespace_project_tag_path(@project.namespace, @project), class: 'btn btn-create new-tag-btn' do
|
||||
New tag
|
||||
|
||||
.tags
|
||||
- unless @tags.empty?
|
||||
%ul.content-list
|
||||
|
|
|
@ -1,11 +1,9 @@
|
|||
- if (@page && @page.persisted?)
|
||||
= link_to namespace_project_wiki_history_path(@project.namespace, @project, @page), class: "btn btn-grouped" do
|
||||
= link_to namespace_project_wiki_history_path(@project.namespace, @project, @page), class: "btn" do
|
||||
Page History
|
||||
- if can?(current_user, :create_wiki, @project)
|
||||
= link_to namespace_project_wiki_edit_path(@project.namespace, @project, @page), class: "btn btn-grouped" do
|
||||
%i.fa.fa-pencil-square-o
|
||||
= link_to namespace_project_wiki_edit_path(@project.namespace, @project, @page), class: "btn" do
|
||||
Edit
|
||||
- if can?(current_user, :admin_wiki, @project)
|
||||
= link_to namespace_project_wiki_path(@project.namespace, @project, @page), data: { confirm: "Are you sure you want to delete this page?"}, method: :delete, class: "btn btn-remove" do
|
||||
= icon('trash')
|
||||
Delete
|
||||
|
|
|
@ -13,7 +13,6 @@
|
|||
.nav-controls
|
||||
- if can?(current_user, :create_wiki, @project)
|
||||
= link_to '#modal-new-wiki', class: "add-new-wiki btn btn-new", "data-toggle" => "modal" do
|
||||
= icon('plus')
|
||||
New Page
|
||||
|
||||
= render 'projects/wikis/new'
|
||||
|
|
|
@ -8,5 +8,6 @@
|
|||
= icon('star')
|
||||
%span.label-name
|
||||
= link_to_label(label, tooltip: false)
|
||||
%span.prepend-left-10
|
||||
= markdown(label.description, pipeline: :single_line)
|
||||
- if label.description
|
||||
%span.label-description
|
||||
= markdown(label.description, pipeline: :single_line)
|
||||
|
|
|
@ -6,10 +6,10 @@
|
|||
- if group_member
|
||||
.controls.hidden-xs
|
||||
- if can?(current_user, :admin_group, group)
|
||||
= link_to edit_group_path(group), class: "btn-sm btn btn-grouped" do
|
||||
%i.fa.fa-cogs
|
||||
= link_to edit_group_path(group), class: "btn" do
|
||||
= icon('cogs')
|
||||
|
||||
= link_to leave_group_group_members_path(group), data: { confirm: leave_group_message(group.name) }, method: :delete, class: "btn-sm btn btn-grouped", title: 'Leave this group' do
|
||||
= link_to leave_group_group_members_path(group), data: { confirm: leave_group_message(group.name) }, method: :delete, class: "btn", title: 'Leave this group' do
|
||||
= icon('sign-out')
|
||||
|
||||
.stats
|
||||
|
|
|
@ -88,9 +88,9 @@
|
|||
.col-lg-6
|
||||
.form-group
|
||||
= f.label :due_date, "Due date", class: "control-label"
|
||||
= f.hidden_field :due_date, id: "issuable-due-date"
|
||||
.col-sm-10
|
||||
.datepicker
|
||||
.issuable-form-select-holder
|
||||
= f.text_field :due_date, id: "issuable-due-date", class: "datepicker form-control", placeholder: "Select due date"
|
||||
|
||||
- if issuable.can_move?(current_user)
|
||||
%hr
|
||||
|
|
|
@ -41,7 +41,8 @@
|
|||
= icon('clock-o')
|
||||
%span
|
||||
- if issuable.milestone
|
||||
= issuable.milestone.title
|
||||
%span.has-tooltip{title: milestone_remaining_days(issuable.milestone), data: {container: 'body', html: 1, placement: 'left'}}
|
||||
= issuable.milestone.title
|
||||
- else
|
||||
None
|
||||
.title.hide-collapsed
|
||||
|
@ -52,7 +53,8 @@
|
|||
.value.bold.hide-collapsed
|
||||
- if issuable.milestone
|
||||
= link_to namespace_project_milestone_path(@project.namespace, @project, issuable.milestone) do
|
||||
= issuable.milestone.title
|
||||
%span.has-tooltip{title: milestone_remaining_days(issuable.milestone), data: {container: 'body', html: 1}}
|
||||
= issuable.milestone.title
|
||||
- else
|
||||
.light None
|
||||
|
||||
|
|
|
@ -35,11 +35,9 @@
|
|||
.col-sm-6= render('shared/milestone_expired', milestone: milestone)
|
||||
.col-sm-6
|
||||
- if can?(current_user, :admin_milestone, milestone.project) and milestone.active?
|
||||
= link_to edit_namespace_project_milestone_path(milestone.project.namespace, milestone.project, milestone), class: "btn btn-xs" do
|
||||
= icon('pencil-square-o')
|
||||
= link_to edit_namespace_project_milestone_path(milestone.project.namespace, milestone.project, milestone), class: "btn btn-xs btn-grouped" do
|
||||
Edit
|
||||
\
|
||||
= link_to 'Close Milestone', namespace_project_milestone_path(@project.namespace, @project, milestone, milestone: {state_event: :close }), method: :put, remote: true, class: "btn btn-xs btn-close"
|
||||
= link_to namespace_project_milestone_path(milestone.project.namespace, milestone.project, milestone), data: { confirm: 'Are you sure?' }, method: :delete, class: "btn btn-xs btn-remove" do
|
||||
= icon('trash-o')
|
||||
= link_to 'Close Milestone', namespace_project_milestone_path(@project.namespace, @project, milestone, milestone: {state_event: :close }), method: :put, remote: true, class: "btn btn-xs btn-close btn-grouped"
|
||||
= link_to namespace_project_milestone_path(milestone.project.namespace, milestone.project, milestone), data: { confirm: 'Are you sure?' }, method: :delete, class: "btn btn-xs btn-remove btn-grouped" do
|
||||
Delete
|
||||
|
|
|
@ -79,10 +79,10 @@
|
|||
%li.js-contributed-tab
|
||||
= link_to user_contributed_projects_path, data: {target: 'div#contributed', action: 'contributed', toggle: 'tab'} do
|
||||
Contributed projects
|
||||
%li.projects-tab
|
||||
%li.js-projects-tab
|
||||
= link_to user_projects_path, data: {target: 'div#projects', action: 'projects', toggle: 'tab'} do
|
||||
Personal projects
|
||||
%li.snippets-tab
|
||||
%li.js-snippets-tab
|
||||
= link_to user_snippets_path, data: {target: 'div#snippets', action: 'snippets', toggle: 'tab'} do
|
||||
Snippets
|
||||
|
||||
|
|
|
@ -449,22 +449,6 @@ Rails.application.routes.draw do
|
|||
resources(:projects, constraints: { id: /[a-zA-Z.0-9_\-]+(?<!\.atom)/ }, except:
|
||||
[:new, :create, :index], path: "/") do
|
||||
|
||||
# Allow /info/refs, /info/refs?service=git-upload-pack, and
|
||||
# /info/refs?service=git-receive-pack, but nothing else.
|
||||
#
|
||||
git_http_handshake = lambda do |request|
|
||||
request.query_string.blank? ||
|
||||
request.query_string.match(/\Aservice=git-(upload|receive)-pack\z/)
|
||||
end
|
||||
|
||||
ref_redirect = redirect do |params, request|
|
||||
path = "#{params[:namespace_id]}/#{params[:project_id]}.git/info/refs"
|
||||
path << "?#{request.query_string}" unless request.query_string.blank?
|
||||
path
|
||||
end
|
||||
|
||||
get '/info/refs', constraints: git_http_handshake, to: ref_redirect
|
||||
|
||||
member do
|
||||
put :transfer
|
||||
delete :remove_fork
|
||||
|
@ -479,12 +463,28 @@ Rails.application.routes.draw do
|
|||
|
||||
scope module: :projects do
|
||||
# Git HTTP clients ('git clone' etc.)
|
||||
scope constraints: { format: /(git|wiki\.git)/ } do
|
||||
scope constraints: { id: /.+\.git/, format: nil } do
|
||||
get '/info/refs', to: 'git_http#info_refs'
|
||||
post '/git-upload-pack', to: 'git_http#git_upload_pack'
|
||||
post '/git-receive-pack', to: 'git_http#git_receive_pack'
|
||||
end
|
||||
|
||||
# Allow /info/refs, /info/refs?service=git-upload-pack, and
|
||||
# /info/refs?service=git-receive-pack, but nothing else.
|
||||
#
|
||||
git_http_handshake = lambda do |request|
|
||||
request.query_string.blank? ||
|
||||
request.query_string.match(/\Aservice=git-(upload|receive)-pack\z/)
|
||||
end
|
||||
|
||||
ref_redirect = redirect do |params, request|
|
||||
path = "#{params[:namespace_id]}/#{params[:project_id]}.git/info/refs"
|
||||
path << "?#{request.query_string}" unless request.query_string.blank?
|
||||
path
|
||||
end
|
||||
|
||||
get '/info/refs', constraints: git_http_handshake, to: ref_redirect
|
||||
|
||||
# Blob routes:
|
||||
get '/new/*id', to: 'blob#new', constraints: { id: /.+/ }, as: 'new_blob'
|
||||
post '/create/*id', to: 'blob#create', constraints: { id: /.+/ }, as: 'create_blob'
|
||||
|
|
|
@ -9,7 +9,7 @@ class Spinach::Features::ProjectIssuesLabels < Spinach::FeatureSteps
|
|||
|
||||
step 'I remove label \'bug\'' do
|
||||
page.within "#label_#{bug_label.id}" do
|
||||
click_link 'Delete'
|
||||
first(:link, 'Delete').click
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -29,6 +29,6 @@ class Spinach::Features::Labels < Spinach::FeatureSteps
|
|||
private
|
||||
|
||||
def subscribe_button
|
||||
first('.label-subscribe-button span')
|
||||
first('.js-subscribe-button', visible: true)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -8,6 +8,7 @@ module Gitlab
|
|||
gon.relative_url_root = Gitlab.config.gitlab.relative_url_root
|
||||
gon.shortcuts_path = help_shortcuts_path
|
||||
gon.user_color_scheme = Gitlab::ColorSchemes.for_user(current_user).css_class
|
||||
gon.award_menu_url = emojis_path
|
||||
|
||||
if current_user
|
||||
gon.current_user_id = current_user.id
|
||||
|
|
|
@ -69,13 +69,20 @@ module Gitlab
|
|||
return unless ldap_person
|
||||
|
||||
# If a corresponding person exists with same uid in a LDAP server,
|
||||
# set up a Gitlab user with dual LDAP and Omniauth identities.
|
||||
if user = Gitlab::LDAP::User.find_by_uid_and_provider(ldap_person.dn, ldap_person.provider)
|
||||
# Case when a LDAP user already exists in Gitlab. Add the Omniauth identity to existing account.
|
||||
# check if the user already has a GitLab account.
|
||||
user = Gitlab::LDAP::User.find_by_uid_and_provider(ldap_person.dn, ldap_person.provider)
|
||||
if user
|
||||
# Case when a LDAP user already exists in Gitlab. Add the OAuth identity to existing account.
|
||||
log.info "LDAP account found for user #{user.username}. Building new #{auth_hash.provider} identity."
|
||||
user.identities.build(extern_uid: auth_hash.uid, provider: auth_hash.provider)
|
||||
else
|
||||
# No account in Gitlab yet: create it and add the LDAP identity
|
||||
user = build_new_user
|
||||
log.info "No existing LDAP account was found in GitLab. Checking for #{auth_hash.provider} account."
|
||||
user = find_by_uid_and_provider
|
||||
if user.nil?
|
||||
log.info "No user found using #{auth_hash.provider} provider. Creating a new one."
|
||||
user = build_new_user
|
||||
end
|
||||
log.info "Correct account has been found. Adding LDAP identity to user: #{user.username}."
|
||||
user.identities.new(provider: ldap_person.provider, extern_uid: ldap_person.dn)
|
||||
end
|
||||
|
||||
|
|
|
@ -12,12 +12,12 @@ module Gitlab
|
|||
end
|
||||
|
||||
def gl_user
|
||||
@user ||= find_by_uid_and_provider
|
||||
|
||||
if auto_link_ldap_user?
|
||||
@user ||= find_or_create_ldap_user
|
||||
end
|
||||
|
||||
@user ||= find_by_uid_and_provider
|
||||
|
||||
if auto_link_saml_user?
|
||||
@user ||= find_by_email
|
||||
end
|
||||
|
|
|
@ -34,7 +34,7 @@ namespace :gitlab do
|
|||
# PG: http://www.postgresql.org/docs/current/static/ddl-depend.html
|
||||
# MySQL: http://dev.mysql.com/doc/refman/5.7/en/drop-table.html
|
||||
# Add `IF EXISTS` because cascade could have already deleted a table.
|
||||
tables.each { |t| connection.execute("DROP TABLE IF EXISTS #{t} CASCADE") }
|
||||
tables.each { |t| connection.execute("DROP TABLE IF EXISTS #{connection.quote_table_name(t)} CASCADE") }
|
||||
end
|
||||
|
||||
desc 'Configures the database by running migrate, or by loading the schema and seeding if needed'
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
require 'spec_helper'
|
||||
require_relative 'import_spec_helper'
|
||||
|
||||
describe Import::BitbucketController do
|
||||
include ImportSpecHelper
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
require 'spec_helper'
|
||||
require_relative 'import_spec_helper'
|
||||
|
||||
describe Import::FogbugzController do
|
||||
include ImportSpecHelper
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
require 'spec_helper'
|
||||
require_relative 'import_spec_helper'
|
||||
|
||||
describe Import::GithubController do
|
||||
include ImportSpecHelper
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
require 'spec_helper'
|
||||
require_relative 'import_spec_helper'
|
||||
|
||||
describe Import::GitlabController do
|
||||
include ImportSpecHelper
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
require 'spec_helper'
|
||||
require_relative 'import_spec_helper'
|
||||
|
||||
describe Import::GitoriousController do
|
||||
include ImportSpecHelper
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
require 'spec_helper'
|
||||
require_relative 'import_spec_helper'
|
||||
|
||||
describe Import::GoogleCodeController do
|
||||
include ImportSpecHelper
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
require 'spec_helper'
|
||||
|
||||
feature 'Tooltips on .timeago dates', feature: true, js: true do
|
||||
include WaitForAjax
|
||||
|
||||
let(:user) { create(:user) }
|
||||
let(:project) { create(:project, name: 'test', namespace: user.namespace) }
|
||||
let(:created_date) { Date.yesterday.to_time }
|
||||
let(:expected_format) { created_date.strftime('%b %-d, %Y %l:%M%P UTC') }
|
||||
|
||||
context 'on the activity tab' do
|
||||
before do
|
||||
project.team << [user, :master]
|
||||
|
||||
Event.create( project: project, author_id: user.id, action: Event::JOINED,
|
||||
updated_at: created_date, created_at: created_date)
|
||||
|
||||
login_as user
|
||||
visit user_path(user)
|
||||
wait_for_ajax()
|
||||
|
||||
page.find('.js-timeago').hover
|
||||
end
|
||||
|
||||
it 'has the datetime formated correctly' do
|
||||
expect(page).to have_selector('.local-timeago', text: expected_format)
|
||||
end
|
||||
end
|
||||
|
||||
context 'on the snippets tab' do
|
||||
before do
|
||||
project.team << [user, :master]
|
||||
create(:snippet, author: user, updated_at: created_date, created_at: created_date)
|
||||
|
||||
login_as user
|
||||
visit user_snippets_path(user)
|
||||
wait_for_ajax()
|
||||
|
||||
page.find('.js-timeago').hover
|
||||
end
|
||||
|
||||
it 'has the datetime formated correctly' do
|
||||
expect(page).to have_selector('.local-timeago', text: expected_format)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -75,12 +75,13 @@ describe 'Issues', feature: true do
|
|||
|
||||
fill_in 'issue_title', with: 'bug 345'
|
||||
fill_in 'issue_description', with: 'bug description'
|
||||
find('#issuable-due-date').click
|
||||
|
||||
page.within '.datepicker' do
|
||||
page.within '.ui-datepicker' do
|
||||
click_link date.day
|
||||
end
|
||||
|
||||
expect(find('#issuable-due-date', visible: false).value).to eq date.to_s
|
||||
expect(find('#issuable-due-date').value).to eq date.to_s
|
||||
|
||||
click_button 'Submit issue'
|
||||
|
||||
|
@ -100,18 +101,19 @@ describe 'Issues', feature: true do
|
|||
it 'should save with due date' do
|
||||
date = Date.today.at_beginning_of_month
|
||||
|
||||
expect(find('#issuable-due-date', visible: false).value).to eq date.to_s
|
||||
expect(find('#issuable-due-date').value).to eq date.to_s
|
||||
|
||||
date = date.tomorrow
|
||||
|
||||
fill_in 'issue_title', with: 'bug 345'
|
||||
fill_in 'issue_description', with: 'bug description'
|
||||
find('#issuable-due-date').click
|
||||
|
||||
page.within '.datepicker' do
|
||||
page.within '.ui-datepicker' do
|
||||
click_link date.day
|
||||
end
|
||||
|
||||
expect(find('#issuable-due-date', visible: false).value).to eq date.to_s
|
||||
expect(find('#issuable-due-date').value).to eq date.to_s
|
||||
|
||||
click_button 'Save changes'
|
||||
|
||||
|
|
|
@ -3,10 +3,11 @@
|
|||
#= require jquery.cookie
|
||||
#= require ./fixtures/emoji_menu
|
||||
|
||||
awardsHandler = null
|
||||
window.gl or= {}
|
||||
gl.emojiAliases = -> return { '+1': 'thumbsup', '-1': 'thumbsdown' }
|
||||
gl.awardMenuUrl = '/emojis'
|
||||
awardsHandler = null
|
||||
window.gl or= {}
|
||||
window.gon or= {}
|
||||
gl.emojiAliases = -> return { '+1': 'thumbsup', '-1': 'thumbsdown' }
|
||||
gon.award_menu_url = '/emojis'
|
||||
|
||||
|
||||
lazyAssert = (done, assertFn) ->
|
||||
|
@ -25,9 +26,7 @@ describe 'AwardsHandler', ->
|
|||
fixture.load 'awards_handler.html'
|
||||
awardsHandler = new AwardsHandler
|
||||
spyOn(awardsHandler, 'postEmoji').and.callFake (url, emoji, cb) => cb()
|
||||
spyOn(jQuery, 'get').and.callFake (req, cb) ->
|
||||
expect(req).toBe '/emojis'
|
||||
cb window.emojiMenu
|
||||
spyOn(jQuery, 'get').and.callFake (req, cb) -> cb window.emojiMenu
|
||||
|
||||
|
||||
describe '::showEmojiMenu', ->
|
||||
|
|
|
@ -1,12 +1,14 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe Gitlab::BitbucketImport::Client, lib: true do
|
||||
include ImportSpecHelper
|
||||
|
||||
let(:token) { '123456' }
|
||||
let(:secret) { 'secret' }
|
||||
let(:client) { Gitlab::BitbucketImport::Client.new(token, secret) }
|
||||
|
||||
before do
|
||||
Gitlab.config.omniauth.providers << OpenStruct.new(app_id: "asd123", app_secret: "asd123", name: "bitbucket")
|
||||
stub_omniauth_provider('bitbucket')
|
||||
end
|
||||
|
||||
it 'all OAuth client options are symbols' do
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe Gitlab::BitbucketImport::Importer, lib: true do
|
||||
include ImportSpecHelper
|
||||
|
||||
before do
|
||||
Gitlab.config.omniauth.providers << OpenStruct.new(app_id: "asd123", app_secret: "asd123", name: "bitbucket")
|
||||
stub_omniauth_provider('bitbucket')
|
||||
end
|
||||
|
||||
let(:statuses) do
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe Gitlab::GitlabImport::Client, lib: true do
|
||||
include ImportSpecHelper
|
||||
|
||||
let(:token) { '123456' }
|
||||
let(:client) { Gitlab::GitlabImport::Client.new(token) }
|
||||
|
||||
before do
|
||||
Gitlab.config.omniauth.providers << OpenStruct.new(app_id: "asd123", app_secret: "asd123", name: "gitlab")
|
||||
stub_omniauth_provider('gitlab')
|
||||
end
|
||||
|
||||
it 'all OAuth2 client options are symbols' do
|
||||
|
|
|
@ -145,6 +145,7 @@ describe Gitlab::Saml::User, lib: true do
|
|||
allow(ldap_user).to receive(:email) { %w(john@mail.com john2@example.com) }
|
||||
allow(ldap_user).to receive(:dn) { 'uid=user1,ou=People,dc=example' }
|
||||
allow(Gitlab::LDAP::Person).to receive(:find_by_uid).and_return(ldap_user)
|
||||
allow(Gitlab::LDAP::Person).to receive(:find_by_dn).and_return(ldap_user)
|
||||
end
|
||||
|
||||
context 'and no account for the LDAP user' do
|
||||
|
@ -177,6 +178,23 @@ describe Gitlab::Saml::User, lib: true do
|
|||
])
|
||||
end
|
||||
end
|
||||
|
||||
context 'user has SAML user, and wants to add their LDAP identity' do
|
||||
it 'adds the LDAP identity to the existing SAML user' do
|
||||
create(:omniauth_user, email: 'john@mail.com', extern_uid: 'uid=user1,ou=People,dc=example', provider: 'saml', username: 'john')
|
||||
local_hash = OmniAuth::AuthHash.new(uid: 'uid=user1,ou=People,dc=example', provider: provider, info: info_hash)
|
||||
local_saml_user = described_class.new(local_hash)
|
||||
local_saml_user.save
|
||||
local_gl_user = local_saml_user.gl_user
|
||||
|
||||
expect(local_gl_user).to be_valid
|
||||
expect(local_gl_user.identities.length).to eql 2
|
||||
identities_as_hash = local_gl_user.identities.map { |id| { provider: id.provider, extern_uid: id.extern_uid } }
|
||||
expect(identities_as_hash).to match_array([ { provider: 'ldapmain', extern_uid: 'uid=user1,ou=People,dc=example' },
|
||||
{ provider: 'saml', extern_uid: 'uid=user1,ou=People,dc=example' }
|
||||
])
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -2,7 +2,7 @@ require "spec_helper"
|
|||
|
||||
describe 'Git HTTP requests', lib: true do
|
||||
let(:user) { create(:user) }
|
||||
let(:project) { create(:project) }
|
||||
let(:project) { create(:project, path: 'project.git-project') }
|
||||
|
||||
it "gives WWW-Authenticate hints" do
|
||||
clone_get('doesnt/exist.git')
|
||||
|
@ -268,6 +268,87 @@ describe 'Git HTTP requests', lib: true do
|
|||
end
|
||||
end
|
||||
|
||||
context "when the project path doesn't end in .git" do
|
||||
context "GET info/refs" do
|
||||
let(:path) { "/#{project.path_with_namespace}/info/refs" }
|
||||
|
||||
context "when no params are added" do
|
||||
before { get path }
|
||||
|
||||
it "redirects to the .git suffix version" do
|
||||
expect(response).to redirect_to("/#{project.path_with_namespace}.git/info/refs")
|
||||
end
|
||||
end
|
||||
|
||||
context "when the upload-pack service is requested" do
|
||||
let(:params) { { service: 'git-upload-pack' } }
|
||||
before { get path, params }
|
||||
|
||||
it "redirects to the .git suffix version" do
|
||||
expect(response).to redirect_to("/#{project.path_with_namespace}.git/info/refs?service=#{params[:service]}")
|
||||
end
|
||||
end
|
||||
|
||||
context "when the receive-pack service is requested" do
|
||||
let(:params) { { service: 'git-receive-pack' } }
|
||||
before { get path, params }
|
||||
|
||||
it "redirects to the .git suffix version" do
|
||||
expect(response).to redirect_to("/#{project.path_with_namespace}.git/info/refs?service=#{params[:service]}")
|
||||
end
|
||||
end
|
||||
|
||||
context "when the params are anything else" do
|
||||
let(:params) { { service: 'git-implode-pack' } }
|
||||
before { get path, params }
|
||||
|
||||
it "redirects to the sign-in page" do
|
||||
expect(response).to redirect_to(new_user_session_path)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "POST git-upload-pack" do
|
||||
it "fails to find a route" do
|
||||
expect { clone_post(project.path_with_namespace) }.to raise_error(ActionController::RoutingError)
|
||||
end
|
||||
end
|
||||
|
||||
context "POST git-receive-pack" do
|
||||
it "failes to find a route" do
|
||||
expect { push_post(project.path_with_namespace) }.to raise_error(ActionController::RoutingError)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "retrieving an info/refs file" do
|
||||
before { project.update_attribute(:visibility_level, Project::PUBLIC) }
|
||||
|
||||
context "when the file exists" do
|
||||
before do
|
||||
# Provide a dummy file in its place
|
||||
allow_any_instance_of(Repository).to receive(:blob_at).and_call_original
|
||||
allow_any_instance_of(Repository).to receive(:blob_at).with('5937ac0a7beb003549fc5fd26fc247adbce4a52e', 'info/refs') do
|
||||
Gitlab::Git::Blob.find(project.repository, 'master', '.gitignore')
|
||||
end
|
||||
|
||||
get "/#{project.path_with_namespace}/blob/master/info/refs"
|
||||
end
|
||||
|
||||
it "returns the file" do
|
||||
expect(response.status).to eq(200)
|
||||
end
|
||||
end
|
||||
|
||||
context "when the file exists" do
|
||||
before { get "/#{project.path_with_namespace}/blob/master/info/refs" }
|
||||
|
||||
it "returns not found" do
|
||||
expect(response.status).to eq(404)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def clone_get(project, options={})
|
||||
get "/#{project}/info/refs", { service: 'git-upload-pack' }, auth_env(*options.values_at(:user, :password))
|
||||
end
|
||||
|
|
|
@ -124,7 +124,7 @@ describe Projects::ImportService, services: true do
|
|||
}
|
||||
)
|
||||
|
||||
Gitlab.config.omniauth.providers << provider
|
||||
allow(Gitlab.config.omniauth).to receive(:providers).and_return([provider])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -18,7 +18,7 @@ describe TodoService, services: true do
|
|||
end
|
||||
|
||||
describe 'Issues' do
|
||||
let(:issue) { create(:issue, project: project, assignee: john_doe, author: author, description: mentions) }
|
||||
let(:issue) { create(:issue, project: project, assignee: john_doe, author: author, description: "- [ ] Task 1\n- [ ] Task 2 #{mentions}") }
|
||||
let(:unassigned_issue) { create(:issue, project: project, assignee: nil) }
|
||||
let(:confidential_issue) { create(:issue, :confidential, project: project, author: author, assignee: assignee, description: mentions) }
|
||||
|
||||
|
@ -101,6 +101,19 @@ describe TodoService, services: true do
|
|||
should_create_todo(user: admin, target: confidential_issue, author: john_doe, action: Todo::MENTIONED)
|
||||
should_not_create_todo(user: john_doe, target: confidential_issue, author: john_doe, action: Todo::MENTIONED)
|
||||
end
|
||||
|
||||
it 'does not create todo when when tasks are marked as completed' do
|
||||
issue.update(description: "- [x] Task 1\n- [X] Task 2 #{mentions}")
|
||||
|
||||
service.update_issue(issue, author)
|
||||
|
||||
should_not_create_todo(user: admin, target: issue, action: Todo::MENTIONED)
|
||||
should_not_create_todo(user: assignee, target: issue, action: Todo::MENTIONED)
|
||||
should_not_create_todo(user: author, target: issue, action: Todo::MENTIONED)
|
||||
should_not_create_todo(user: john_doe, target: issue, action: Todo::MENTIONED)
|
||||
should_not_create_todo(user: member, target: issue, action: Todo::MENTIONED)
|
||||
should_not_create_todo(user: non_member, target: issue, action: Todo::MENTIONED)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#close_issue' do
|
||||
|
@ -210,7 +223,7 @@ describe TodoService, services: true do
|
|||
end
|
||||
|
||||
describe 'Merge Requests' do
|
||||
let(:mr_assigned) { create(:merge_request, source_project: project, author: author, assignee: john_doe, description: mentions) }
|
||||
let(:mr_assigned) { create(:merge_request, source_project: project, author: author, assignee: john_doe, description: "- [ ] Task 1\n- [ ] Task 2 #{mentions}") }
|
||||
let(:mr_unassigned) { create(:merge_request, source_project: project, author: author, assignee: nil) }
|
||||
|
||||
describe '#new_merge_request' do
|
||||
|
@ -253,6 +266,19 @@ describe TodoService, services: true do
|
|||
|
||||
expect { service.update_merge_request(mr_assigned, author) }.not_to change(member.todos, :count)
|
||||
end
|
||||
|
||||
it 'does not create todo when when tasks are marked as completed' do
|
||||
mr_assigned.update(description: "- [x] Task 1\n- [X] Task 2 #{mentions}")
|
||||
|
||||
service.update_merge_request(mr_assigned, author)
|
||||
|
||||
should_not_create_todo(user: admin, target: mr_assigned, action: Todo::MENTIONED)
|
||||
should_not_create_todo(user: assignee, target: mr_assigned, action: Todo::MENTIONED)
|
||||
should_not_create_todo(user: author, target: mr_assigned, action: Todo::MENTIONED)
|
||||
should_not_create_todo(user: john_doe, target: mr_assigned, action: Todo::MENTIONED)
|
||||
should_not_create_todo(user: member, target: mr_assigned, action: Todo::MENTIONED)
|
||||
should_not_create_todo(user: non_member, target: mr_assigned, action: Todo::MENTIONED)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#close_merge_request' do
|
||||
|
|
|
@ -28,6 +28,6 @@ module ImportSpecHelper
|
|||
app_id: 'asd123',
|
||||
app_secret: 'asd123'
|
||||
)
|
||||
Gitlab.config.omniauth.providers << provider
|
||||
allow(Gitlab.config.omniauth).to receive(:providers).and_return([provider])
|
||||
end
|
||||
end
|
Loading…
Reference in New Issue