diff options
author | Douwe Maan <douwe@selenight.nl> | 2016-03-20 14:57:25 +0100 |
---|---|---|
committer | Douwe Maan <douwe@selenight.nl> | 2016-03-20 14:57:25 +0100 |
commit | 2eb19ea3ea36916bbea72a8ccab3e6d15f602ac9 (patch) | |
tree | 55c08bbb50e92ce76028f68a5267401a76bc4b02 /app | |
parent | 8b830b8c3b32774e8ccf562b8bc9dbce3ecf3073 (diff) | |
parent | 01fe50a72513a88f2168c8c0a649661b1382a42b (diff) | |
download | gitlab-ce-2eb19ea3ea36916bbea72a8ccab3e6d15f602ac9.tar.gz |
Merge branch 'master' into issue_12658
Diffstat (limited to 'app')
160 files changed, 1669 insertions, 1093 deletions
diff --git a/app/assets/javascripts/dispatcher.js.coffee b/app/assets/javascripts/dispatcher.js.coffee index 1be86e3b820..f5e1ca9860d 100644 --- a/app/assets/javascripts/dispatcher.js.coffee +++ b/app/assets/javascripts/dispatcher.js.coffee @@ -14,7 +14,6 @@ class Dispatcher path = page.split(':') shortcut_handler = null - switch page when 'projects:issues:index' Issues.init() @@ -25,6 +24,8 @@ class Dispatcher new ZenMode() when 'projects:milestones:show', 'groups:milestones:show', 'dashboard:milestones:show' new Milestone() + when 'dashboard:todos:index' + new Todos() when 'projects:milestones:new', 'projects:milestones:edit' new ZenMode() new DropzoneInput($('.milestone-form')) diff --git a/app/assets/javascripts/gl_dropdown.js.coffee b/app/assets/javascripts/gl_dropdown.js.coffee index 4f038477755..c81e8bf760a 100644 --- a/app/assets/javascripts/gl_dropdown.js.coffee +++ b/app/assets/javascripts/gl_dropdown.js.coffee @@ -246,11 +246,15 @@ class GitLabDropdown if oldValue value = "#{oldValue},#{value}" else - @dropdown.find(ACTIVE_CLASS).removeClass ACTIVE_CLASS + @dropdown.find(".#{ACTIVE_CLASS}").removeClass ACTIVE_CLASS # Toggle active class for the tick mark el.toggleClass "is-active" + # Toggle the dropdown label + if @options.toggleLabel + $(@el).find(".dropdown-toggle-text").text @options.toggleLabel(selectedObject) + if value? if !field.length # Create hidden input for form diff --git a/app/assets/javascripts/issuable_form.js.coffee b/app/assets/javascripts/issuable_form.js.coffee index 48c249943f2..6c1699c178c 100644 --- a/app/assets/javascripts/issuable_form.js.coffee +++ b/app/assets/javascripts/issuable_form.js.coffee @@ -1,4 +1,5 @@ class @IssuableForm + wipRegex: /^\s*(\[WIP\]\s*|WIP:\s*|WIP\s+)+\s*/i constructor: (@form) -> GitLab.GfmAutoComplete.setup() new UsersSelect() @@ -14,6 +15,8 @@ class @IssuableForm @form.on "submit", @resetAutosave @form.on "click", ".btn-cancel", @resetAutosave + @initWip() + initAutosave: -> new Autosave @titleField, [ document.location.pathname, @@ -30,3 +33,41 @@ class @IssuableForm resetAutosave: => @titleField.data("autosave").reset() @descriptionField.data("autosave").reset() + + initWip: -> + @$wipExplanation = @form.find(".js-wip-explanation") + @$noWipExplanation = @form.find(".js-no-wip-explanation") + return unless @$wipExplanation.length and @$noWipExplanation.length + + @form.on "click", ".js-toggle-wip", @toggleWip + + @titleField.on "keyup blur", @renderWipExplanation + + @renderWipExplanation() + + workInProgress: -> + @wipRegex.test @titleField.val() + + renderWipExplanation: => + if @workInProgress() + @$wipExplanation.show() + @$noWipExplanation.hide() + else + @$wipExplanation.hide() + @$noWipExplanation.show() + + toggleWip: (event) => + event.preventDefault() + + if @workInProgress() + @removeWip() + else + @addWip() + + @renderWipExplanation() + + removeWip: -> + @titleField.val @titleField.val().replace(@wipRegex, "") + + addWip: -> + @titleField.val "WIP: #{@titleField.val()}" diff --git a/app/assets/javascripts/issue.js.coffee b/app/assets/javascripts/issue.js.coffee index d663e34871c..f50df1f5ea3 100644 --- a/app/assets/javascripts/issue.js.coffee +++ b/app/assets/javascripts/issue.js.coffee @@ -7,6 +7,7 @@ class @Issue # Prevent duplicate event bindings @disableTaskList() @fixAffixScroll() + @initParticipants() if $('a.btn-close').length @initTaskList() @initIssueBtnEventListeners() @@ -84,3 +85,27 @@ class @Issue type: 'PATCH' url: $('form.js-issuable-update').attr('action') data: patchData + + initParticipants: -> + _this = @ + $(document).on "click", ".js-participants-more", @toggleHiddenParticipants + + $(".js-participants-author").each (i) -> + if i >= _this.PARTICIPANTS_ROW_COUNT + $(@) + .addClass "js-participants-hidden" + .hide() + + toggleHiddenParticipants: (e) -> + e.preventDefault() + + currentText = $(this).text().trim() + lessText = $(this).data("less-text") + originalText = $(this).data("original-text") + + if currentText is originalText + $(this).text(lessText) + else + $(this).text(originalText) + + $(".js-participants-hidden").toggle() diff --git a/app/assets/javascripts/issues.js.coffee b/app/assets/javascripts/issues.js.coffee index a0acf3028bf..1127b289264 100644 --- a/app/assets/javascripts/issues.js.coffee +++ b/app/assets/javascripts/issues.js.coffee @@ -41,24 +41,28 @@ @timer = null $("#issue_search").keyup -> clearTimeout(@timer) - @timer = setTimeout(Issues.filterResults, 500) + @timer = setTimeout( -> + Issues.filterResults $("#issue_search_form") + , 500) - filterResults: => - form = $("#issue_search_form") - search = $("#issue_search").val() - $('.issues-holder').css("opacity", '0.5') - issues_url = form.attr('action') + '?' + form.serialize() + filterResults: (form) => + $('.issues-holder, .merge-requests-holder').css("opacity", '0.5') + formAction = form.attr('action') + formData = form.serialize() + issuesUrl = formAction + issuesUrl += ("#{if formAction.indexOf("?") < 0 then '?' else '&'}") + issuesUrl += formData $.ajax type: "GET" - url: form.attr('action') - data: form.serialize() + url: formAction + data: formData complete: -> - $('.issues-holder').css("opacity", '1.0') + $('.issues-holder, .merge-requests-holder').css("opacity", '1.0') success: (data) -> - $('.issues-holder').html(data.html) + $('.issues-holder, .merge-requests-holder').html(data.html) # Change url so if user reload a page - search results are saved - history.replaceState {page: issues_url}, document.title, issues_url + history.replaceState {page: issuesUrl}, document.title, issuesUrl Issues.reload() dataType: "json" diff --git a/app/assets/javascripts/labels_select.js.coffee b/app/assets/javascripts/labels_select.js.coffee index 5ade2cb66cb..4a0c18a99a6 100644 --- a/app/assets/javascripts/labels_select.js.coffee +++ b/app/assets/javascripts/labels_select.js.coffee @@ -1,30 +1,32 @@ class @LabelsSelect constructor: -> $('.js-label-select').each (i, dropdown) -> - projectId = $(dropdown).data('project-id') - labelUrl = $(dropdown).data("labels") - selectedLabel = $(dropdown).data('selected') + $dropdown = $(dropdown) + projectId = $dropdown.data('project-id') + labelUrl = $dropdown.data('labels') + selectedLabel = $dropdown.data('selected') if selectedLabel - selectedLabel = selectedLabel.split(",") + selectedLabel = selectedLabel.split(',') newLabelField = $('#new_label_name') newColorField = $('#new_label_color') - showNo = $(dropdown).data('show-no') - showAny = $(dropdown).data('show-any') + showNo = $dropdown.data('show-no') + showAny = $dropdown.data('show-any') + defaultLabel = $dropdown.data('default-label') if newLabelField.length - $('.suggest-colors-dropdown a').on "click", (e) -> + $('.suggest-colors-dropdown a').on 'click', (e) -> e.preventDefault() e.stopPropagation() - newColorField.val $(this).data("color") + newColorField.val $(this).data('color') $('.js-dropdown-label-color-preview') - .css 'background-color', $(this).data("color") + .css 'background-color', $(this).data('color') .addClass 'is-active' - $('.js-new-label-btn').on "click", (e) -> + $('.js-new-label-btn').on 'click', (e) -> e.preventDefault() e.stopPropagation() - if newLabelField.val() isnt "" && newColorField.val() isnt "" + if newLabelField.val() isnt '' and newColorField.val() isnt '' $('.js-new-label-btn').disable() # Create new label with API @@ -33,46 +35,38 @@ class @LabelsSelect color: newColorField.val() }, (label) -> $('.js-new-label-btn').enable() - $('.dropdown-menu-back', $(dropdown).parent()).trigger "click" + $('.dropdown-menu-back', $dropdown.parent()).trigger 'click' - $(dropdown).glDropdown( + $dropdown.glDropdown( data: (term, callback) -> - # We have to fetch the JS version of the labels list because there is no - # public facing JSON url for labels $.ajax( url: labelUrl ).done (data) -> - html = $(data) - data = [] - html.find('.label-row a').each -> - data.push( - title: $(@).text().trim() - ) - if showNo data.unshift( - id: "0" - title: 'No label' + id: 0 + title: 'No Label' ) if showAny data.unshift( - title: 'Any label' + isAny: true + title: 'Any Label' ) if data.length > 2 - data.splice 2, 0, "divider" + data.splice 2, 0, 'divider' callback data renderRow: (label) -> if $.isArray(selectedLabel) - selected = "" + selected = '' $.each selectedLabel, (i, selectedLbl) -> selectedLbl = selectedLbl.trim() - if selected is "" && label.title is selectedLbl - selected = "is-active" + if selected is '' and label.title is selectedLbl + selected = 'is-active' else - selected = if label.title is selectedLabel then "is-active" else "" + selected = if label.title is selectedLabel then 'is-active' else '' "<li> <a href='#' class='#{selected}'> @@ -83,10 +77,24 @@ class @LabelsSelect search: fields: ['title'] selectable: true - fieldName: $(dropdown).data('field-name') + toggleLabel: (selected) -> + if selected and selected.title isnt 'Any Label' + selected.title + else + defaultLabel + fieldName: $dropdown.data('field-name') id: (label) -> - label.title + if label.isAny? + '' + else + label.title clicked: -> - if $(dropdown).hasClass "js-filter-submit" - $(dropdown).parents('form').submit() + page = $('body').data 'page' + isIssueIndex = page is 'projects:issues:index' + isMRIndex = page is page is 'projects:merge_requests:index' + + if $dropdown.hasClass('js-filter-submit') and (isIssueIndex or isMRIndex) + Issues.filterResults $dropdown.closest('form') + else if $dropdown.hasClass 'js-filter-submit' + $dropdown.closest('form').submit() ) diff --git a/app/assets/javascripts/milestone_select.js.coffee b/app/assets/javascripts/milestone_select.js.coffee index 5e884454a65..e17a1adb648 100644 --- a/app/assets/javascripts/milestone_select.js.coffee +++ b/app/assets/javascripts/milestone_select.js.coffee @@ -1,60 +1,65 @@ class @MilestoneSelect constructor: -> $('.js-milestone-select').each (i, dropdown) -> - projectId = $(dropdown).data('project-id') - milestonesUrl = $(dropdown).data('milestones') - selectedMilestone = $(dropdown).data('selected') - showNo = $(dropdown).data('show-no') - showAny = $(dropdown).data('show-any') - useId = $(dropdown).data('use-id') + $dropdown = $(dropdown) + projectId = $dropdown.data('project-id') + milestonesUrl = $dropdown.data('milestones') + selectedMilestone = $dropdown.data('selected') + showNo = $dropdown.data('show-no') + showAny = $dropdown.data('show-any') + useId = $dropdown.data('use-id') + defaultLabel = $dropdown.data('default-label') - $(dropdown).glDropdown( + $dropdown.glDropdown( data: (term, callback) -> $.ajax( url: milestonesUrl ).done (data) -> - html = $(data) - data = [] - html.find('.milestone strong a').each -> - link = $(@).attr("href").split("/") - data.push( - id: link[link.length - 1] - title: $(@).text().trim() - ) - if showNo data.unshift( - id: "0" + id: '0' title: 'No Milestone' ) if showAny data.unshift( + isAny: true title: 'Any Milestone' ) if data.length > 2 - data.splice 2, 0, "divider" + data.splice 2, 0, 'divider' callback(data) filterable: true search: fields: ['title'] selectable: true - fieldName: $(dropdown).data('field-name') + toggleLabel: (selected) -> + if selected && 'id' of selected + selected.title + else + defaultLabel + fieldName: $dropdown.data('field-name') text: (milestone) -> milestone.title id: (milestone) -> if !useId - if milestone.title isnt "Any milestone" + if !milestone.isAny? milestone.title else - "" + '' else milestone.id isSelected: (milestone) -> milestone.title is selectedMilestone clicked: -> - if $(dropdown).hasClass "js-filter-submit" - $(dropdown).parents('form').submit() + page = $('body').data 'page' + isIssueIndex = page is 'projects:issues:index' + isMRIndex = page is page is 'projects:merge_requests:index' + + if $dropdown.hasClass('js-filter-submit') and (isIssueIndex or isMRIndex) + Issues.filterResults $dropdown.closest('form') + else if $dropdown.hasClass 'js-filter-submit' + $dropdown.closest('form').submit() ) diff --git a/app/assets/javascripts/notes.js.coffee b/app/assets/javascripts/notes.js.coffee index 75d7f52bbb6..82532216589 100644 --- a/app/assets/javascripts/notes.js.coffee +++ b/app/assets/javascripts/notes.js.coffee @@ -343,6 +343,7 @@ class @Notes updateNote: (_xhr, note, _status) => # Convert returned HTML to a jQuery object so we can modify it further $html = $(note.html) + $('.js-timeago', $html).timeago() $html.syntaxHighlight() $html.find('.js-task-list-container').taskList('enable') @@ -626,10 +627,10 @@ class @Notes if closebtn.text() isnt closetext closebtn.text(closetext) - if reopenbtn.is(':not(.btn-comment-and-reopen)') + if reopenbtn.is('.btn-comment-and-reopen') reopenbtn.removeClass('btn-comment-and-reopen') - if closebtn.is(':not(.btn-comment-and-close)') + if closebtn.is('.btn-comment-and-close') closebtn.removeClass('btn-comment-and-close') if discardbtn.is(':visible') diff --git a/app/assets/javascripts/project_new.js.coffee b/app/assets/javascripts/project_new.js.coffee index fecdb9fc2e7..63dee4ed5d7 100644 --- a/app/assets/javascripts/project_new.js.coffee +++ b/app/assets/javascripts/project_new.js.coffee @@ -3,3 +3,16 @@ class @ProjectNew $('.project-edit-container').on 'ajax:before', => $('.project-edit-container').hide() $('.save-project-loader').show() + @toggleSettings() + @toggleSettingsOnclick() + + + toggleSettings: -> + checked = $("#project_builds_enabled").prop("checked") + if checked + $('.builds-feature').show() + else + $('.builds-feature').hide() + + toggleSettingsOnclick: -> + $("#project_builds_enabled").on 'click', @toggleSettings diff --git a/app/assets/javascripts/todos.js.coffee b/app/assets/javascripts/todos.js.coffee new file mode 100644 index 00000000000..b6b4bd90e6a --- /dev/null +++ b/app/assets/javascripts/todos.js.coffee @@ -0,0 +1,56 @@ +class @Todos + constructor: (@name) -> + @clearListeners() + @initBtnListeners() + + clearListeners: -> + $('.done-todo').off('click') + $('.js-todos-mark-all').off('click') + + initBtnListeners: -> + $('.done-todo').on('click', @doneClicked) + $('.js-todos-mark-all').on('click', @allDoneClicked) + + doneClicked: (e) => + e.preventDefault() + e.stopImmediatePropagation() + + $this = $(e.currentTarget) + $this.disable() + + $.ajax + type: 'POST' + url: $this.attr('href') + dataType: 'json' + data: '_method': 'delete' + success: (data) => + @clearDone $this.closest('li') + @updateBadges data + + allDoneClicked: (e) => + e.preventDefault() + e.stopImmediatePropagation() + + $this = $(e.currentTarget) + $this.disable() + + $.ajax + type: 'POST' + url: $this.attr('href') + dataType: 'json' + data: '_method': 'delete' + success: (data) => + $this.remove() + $('.js-todos-list').remove() + @updateBadges data + + clearDone: ($row) -> + $ul = $row.closest('ul') + $row.remove() + + if not $ul.find('li').length + $ul.parents('.panel').remove() + + updateBadges: (data) -> + $('.todos-pending .badge, .todos-pending-count').text data.count + $('.todos-done .badge').text data.done_count diff --git a/app/assets/javascripts/users_select.js.coffee b/app/assets/javascripts/users_select.js.coffee index 987c6f4b8d2..3d6452d2f46 100644 --- a/app/assets/javascripts/users_select.js.coffee +++ b/app/assets/javascripts/users_select.js.coffee @@ -4,14 +4,16 @@ class @UsersSelect @userPath = "/autocomplete/users/:id.json" $('.js-user-search').each (i, dropdown) => - @projectId = $(dropdown).data('project-id') - @showCurrentUser = $(dropdown).data('current-user') - showNullUser = $(dropdown).data('null-user') - showAnyUser = $(dropdown).data('any-user') - firstUser = $(dropdown).data('first-user') - selectedId = $(dropdown).data('selected') - - $(dropdown).glDropdown( + $dropdown = $(dropdown) + @projectId = $dropdown.data('project-id') + @showCurrentUser = $dropdown.data('current-user') + showNullUser = $dropdown.data('null-user') + showAnyUser = $dropdown.data('any-user') + firstUser = $dropdown.data('first-user') + selectedId = $dropdown.data('selected') + defaultLabel = $dropdown.data('default-label') + + $dropdown.glDropdown( data: (term, callback) => @users term, (users) => if term.length is 0 @@ -52,10 +54,21 @@ class @UsersSelect search: fields: ['name', 'username'] selectable: true - fieldName: $(dropdown).data('field-name') + fieldName: $dropdown.data('field-name') + toggleLabel: (selected) -> + if selected && 'id' of selected + selected.name + else + defaultLabel clicked: -> - if $(dropdown).hasClass "js-filter-submit" - $(dropdown).parents('form').submit() + page = $('body').data 'page' + isIssueIndex = page is 'projects:issues:index' + isMRIndex = page is page is 'projects:merge_requests:index' + + if $dropdown.hasClass('js-filter-submit') and (isIssueIndex or isMRIndex) + Issues.filterResults $dropdown.closest('form') + else if $dropdown.hasClass 'js-filter-submit' + $dropdown.closest('form').submit() renderRow: (user) -> username = if user.username then "@#{user.username}" else "" avatar = if user.avatar_url then user.avatar_url else false diff --git a/app/assets/stylesheets/framework/blocks.scss b/app/assets/stylesheets/framework/blocks.scss index 31084872367..2fac6abbac9 100644 --- a/app/assets/stylesheets/framework/blocks.scss +++ b/app/assets/stylesheets/framework/blocks.scss @@ -23,15 +23,11 @@ margin-bottom: -$gl-padding; background-color: $background-color; padding: $gl-padding; - margin-bottom: 0px; + margin-bottom: 0; border-top: 1px solid $border-color; border-bottom: 1px solid $border-color; color: $gl-gray; - a { - color: $md-link-color; - } - &.oneline-block { line-height: 42px; } diff --git a/app/assets/stylesheets/framework/buttons.scss b/app/assets/stylesheets/framework/buttons.scss index 8d475137b03..657c5f033c7 100644 --- a/app/assets/stylesheets/framework/buttons.scss +++ b/app/assets/stylesheets/framework/buttons.scss @@ -37,23 +37,23 @@ } @mixin btn-green { - @include btn-color($green-light, $border-green-light, $green-normal, $border-green-normal, $green-dark, $border-green-dark, #FFFFFF); + @include btn-color($green-light, $border-green-light, $green-normal, $border-green-normal, $green-dark, $border-green-dark, #fff); } @mixin btn-blue { - @include btn-color($blue-light, $border-blue-light, $blue-normal, $border-blue-normal, $blue-dark, $border-blue-dark, #FFFFFF); + @include btn-color($blue-light, $border-blue-light, $blue-normal, $border-blue-normal, $blue-dark, $border-blue-dark, #fff); } @mixin btn-blue-medium { - @include btn-color($blue-medium-light, $border-blue-light, $blue-medium, $border-blue-normal, $blue-medium-dark, $border-blue-dark, #FFFFFF); + @include btn-color($blue-medium-light, $border-blue-light, $blue-medium, $border-blue-normal, $blue-medium-dark, $border-blue-dark, #fff); } @mixin btn-orange { - @include btn-color($orange-light, $border-orange-light, $orange-normal, $border-orange-normal, $orange-dark, $border-orange-dark, #FFFFFF); + @include btn-color($orange-light, $border-orange-light, $orange-normal, $border-orange-normal, $orange-dark, $border-orange-dark, #fff); } @mixin btn-red { - @include btn-color($red-light, $border-red-light, $red-normal, $border-red-normal, $red-dark, $border-red-dark, #FFFFFF); + @include btn-color($red-light, $border-red-light, $red-normal, $border-red-normal, $red-dark, $border-red-dark, #fff); } @mixin btn-gray { @@ -127,7 +127,7 @@ margin-right: 7px; float: left; &:last-child { - margin-right: 0px; + margin-right: 0; } &.btn-xs { margin-right: 3px; @@ -139,7 +139,6 @@ .caret { margin-left: 5px; - color: $gray-darkest; } } @@ -170,7 +169,7 @@ margin-right: 7px; float: left; &:last-child { - margin-right: 0px; + margin-right: 0; } } } @@ -209,3 +208,13 @@ background-color: #e4e7ed !important; } } + +.btn-loading { + &:not(.disabled) .fa { + display: none; + } + + .fa { + margin-right: 5px; + } +} diff --git a/app/assets/stylesheets/framework/calendar.scss b/app/assets/stylesheets/framework/calendar.scss index 580012abd77..e3192823a1a 100644 --- a/app/assets/stylesheets/framework/calendar.scss +++ b/app/assets/stylesheets/framework/calendar.scss @@ -33,19 +33,19 @@ } .q2 { - fill: #ACD5F2 !important; + fill: #acd5f2 !important; } .q3 { - fill: #7FA8D1 !important; + fill: #7fa8d1 !important; } .q4 { - fill: #49729B !important; + fill: #49729b !important; } .q5 { - fill: #254E77 !important; + fill: #254e77 !important; } .domain-background { diff --git a/app/assets/stylesheets/framework/callout.scss b/app/assets/stylesheets/framework/callout.scss index 20a9bfb9816..da7bab74a32 100644 --- a/app/assets/stylesheets/framework/callout.scss +++ b/app/assets/stylesheets/framework/callout.scss @@ -39,6 +39,6 @@ } .bs-callout-success { background-color: #dff0d8; - border-color: #5cA64d; + border-color: #5ca64d; color: #3c763d; } diff --git a/app/assets/stylesheets/framework/common.scss b/app/assets/stylesheets/framework/common.scss index ff551f151f1..bc03c2180be 100644 --- a/app/assets/stylesheets/framework/common.scss +++ b/app/assets/stylesheets/framework/common.scss @@ -1,6 +1,6 @@ /** COLORS **/ .cgray { color: $gl-gray; } -.clgray { color: #BBB } +.clgray { color: #bbb } .cred { color: $gl-text-red; } .cgreen { color: $gl-text-green; } .cdark { color: #444 } @@ -8,20 +8,20 @@ /** COMMON CLASSES **/ .prepend-top-0 { margin-top: 0; } .prepend-top-5 { margin-top: 5px; } -.prepend-top-10 { margin-top:10px } +.prepend-top-10 { margin-top: 10px } .prepend-top-default { margin-top: $gl-padding !important; } -.prepend-top-20 { margin-top:20px } -.prepend-left-10 { margin-left:10px } +.prepend-top-20 { margin-top: 20px } +.prepend-left-10 { margin-left: 10px } .prepend-left-default { margin-left: $gl-padding; } -.prepend-left-20 { margin-left:20px } +.prepend-left-20 { margin-left: 20px } .append-right-5 { margin-right: 5px } -.append-right-10 { margin-right:10px } +.append-right-10 { margin-right: 10px } .append-right-default { margin-right: $gl-padding; } -.append-right-20 { margin-right:20px } -.append-bottom-0 { margin-bottom:0 } -.append-bottom-10 { margin-bottom:10px } -.append-bottom-15 { margin-bottom:15px } -.append-bottom-20 { margin-bottom:20px } +.append-right-20 { margin-right: 20px } +.append-bottom-0 { margin-bottom: 0 } +.append-bottom-10 { margin-bottom: 10px } +.append-bottom-15 { margin-bottom: 15px } +.append-bottom-20 { margin-bottom: 20px } .append-bottom-default { margin-bottom: $gl-padding; } .inline { display: inline-block } .center { text-align: center } @@ -51,7 +51,7 @@ pre { } &.well-pre { - border: 1px solid #EEE; + border: 1px solid #eee; background: #f9f9f9; border-radius: 0; color: #555; @@ -103,7 +103,7 @@ span.update-author { } .user-mention { - color: #2FA0BB; + color: #2fa0bb; font-weight: bold; } @@ -134,10 +134,10 @@ p.time { // Fix issue with notes & lists creating a bunch of bottom borders. li.note { - img { max-width:100% } + img { max-width: 100% } .note-title { li { - border-bottom:none !important; + border-bottom: none !important; } } } @@ -187,9 +187,9 @@ li.note { .error-message { padding: 10px; - background: #C67; + background: #c67; margin: 0; - color: #FFF; + color: #fff; a { color: #fff; @@ -200,7 +200,7 @@ li.note { .browser-alert { padding: 10px; text-align: center; - background: #C67; + background: #c67; color: #fff; font-weight: bold; a { @@ -271,7 +271,7 @@ img.emoji { table { td.permission-x { - background: #D9EDF7 !important; + background: #d9edf7 !important; text-align: center; } } @@ -280,7 +280,7 @@ table { float: left; text-align: center; font-size: 32px; - color: #AAA; + color: #aaa; width: 60px; } @@ -347,7 +347,7 @@ table { .profiler-button, .profiler-controls { - border-color: #EEE !important; + border-color: #eee !important; } } diff --git a/app/assets/stylesheets/framework/dropdowns.scss b/app/assets/stylesheets/framework/dropdowns.scss index 5b647fc6176..a48b6c17fa0 100644 --- a/app/assets/stylesheets/framework/dropdowns.scss +++ b/app/assets/stylesheets/framework/dropdowns.scss @@ -4,11 +4,17 @@ height: 0; margin-left: 2px; vertical-align: middle; - border-top: $caret-width-base dashed $dropdown-caret-color; + border-top: $caret-width-base dashed; border-right: $caret-width-base solid transparent; border-left: $caret-width-base solid transparent; } +.btn-group { + .caret { + margin-left: 0; + } +} + .dropdown { position: relative; } @@ -161,9 +167,8 @@ .dropdown-menu-user-full-name { display: block; - margin-bottom: 2px; font-weight: 600; - line-height: 1; + line-height: 16px; text-overflow: ellipsis; overflow: hidden; white-space: nowrap; @@ -171,7 +176,7 @@ .dropdown-menu-user-username { display: block; - line-height: 1; + line-height: 16px; text-overflow: ellipsis; overflow: hidden; white-space: nowrap; @@ -261,7 +266,7 @@ position: absolute; top: 10px; right: 10px; - color: #C7C7C7; + color: #c7c7c7; font-size: 12px; pointer-events: none; } diff --git a/app/assets/stylesheets/framework/files.scss b/app/assets/stylesheets/framework/files.scss index b034a4882c1..646e2610831 100644 --- a/app/assets/stylesheets/framework/files.scss +++ b/app/assets/stylesheets/framework/files.scss @@ -30,7 +30,7 @@ right: 15px; .btn { - padding: 0px 10px; + padding: 0 10px; font-size: 13px; line-height: 28px; } @@ -84,7 +84,7 @@ &.blob-no-preview { background: #eee; - text-shadow: 0 1px 2px #FFF; + text-shadow: 0 1px 2px #fff; padding: 100px 0; } @@ -124,7 +124,7 @@ } td.line-numbers { float: none; - border-left: 1px solid #DDD; + border-left: 1px solid #ddd; } td.lines { padding: 0; diff --git a/app/assets/stylesheets/framework/filters.scss b/app/assets/stylesheets/framework/filters.scss index c431e2b0df3..40a508c1ebc 100644 --- a/app/assets/stylesheets/framework/filters.scss +++ b/app/assets/stylesheets/framework/filters.scss @@ -3,22 +3,11 @@ vertical-align: top; } -@media (min-width: 800px) { +@media (min-width: $screen-sm-min) { .issues-filters, .issues_bulk_update { - select, .select2-container { - width: 120px !important; - display: inline-block; - } - } -} - -@media (min-width: 1200px) { - .issues-filters, - .issues_bulk_update { - select, .select2-container { - width: 150px !important; - display: inline-block; + .dropdown-menu-toggle { + width: 132px; } } } diff --git a/app/assets/stylesheets/framework/forms.scss b/app/assets/stylesheets/framework/forms.scss index 6c08005812e..4cb4129b71b 100644 --- a/app/assets/stylesheets/framework/forms.scss +++ b/app/assets/stylesheets/framework/forms.scss @@ -41,8 +41,8 @@ input[type='search'].search-input { } input[type='text'].danger { - background: #F2DEDE!important; - border-color: #D66; + background: #f2dede!important; + border-color: #d66; text-shadow: 0 1px 1px #fff } diff --git a/app/assets/stylesheets/framework/gitlab-theme.scss b/app/assets/stylesheets/framework/gitlab-theme.scss index 12cef6f8ea1..2a4cf4fc335 100644 --- a/app/assets/stylesheets/framework/gitlab-theme.scss +++ b/app/assets/stylesheets/framework/gitlab-theme.scss @@ -23,13 +23,13 @@ &:hover { background-color: $color-darker; a { - color: #FFF; + color: #fff; } } } .collapse-nav a { - color: #FFF; + color: #fff; background: $color; } @@ -42,7 +42,7 @@ &:hover { background-color: $color-dark; - color: #FFF; + color: #fff; text-decoration: none; } } @@ -71,7 +71,7 @@ } &.active a { - color: #FFF; + color: #fff; background: $color-dark; &.no-highlight { @@ -79,42 +79,42 @@ } i { - color: #FFF + color: #fff } } } } } -$theme-blue: #2980B9; +$theme-blue: #2980b9; $theme-charcoal: #333c47; -$theme-graphite: #888888; +$theme-graphite: #888; $theme-gray: #373737; $theme-green: #019875; -$theme-violet: #554488; +$theme-violet: #548; body { &.ui_blue { - @include gitlab-theme(#BECDE9, $theme-blue, #1970A9, #096099); + @include gitlab-theme(#becde9, $theme-blue, #1970a9, #096099); } &.ui_charcoal { - @include gitlab-theme(#c5d0de, $theme-charcoal, #2b333d, #24272D); + @include gitlab-theme(#c5d0de, $theme-charcoal, #2b333d, #24272d); } &.ui_graphite { - @include gitlab-theme(#CCCCCC, $theme-graphite, #777777, #666666); + @include gitlab-theme(#ccc, $theme-graphite, #777, #666); } &.ui_gray { - @include gitlab-theme(#979797, $theme-gray, #272727, #222222); + @include gitlab-theme(#979797, $theme-gray, #272727, #222); } &.ui_green { - @include gitlab-theme(#AADDCC, $theme-green, #018865, #017855); + @include gitlab-theme(#adc, $theme-green, #018865, #017855); } &.ui_violet { - @include gitlab-theme(#9988CC, $theme-violet, #443366, #332255); + @include gitlab-theme(#98c, $theme-violet, #436, #325); } }
\ No newline at end of file diff --git a/app/assets/stylesheets/framework/header.scss b/app/assets/stylesheets/framework/header.scss index 4c4033e3ae7..71a7ecab8ef 100644 --- a/app/assets/stylesheets/framework/header.scss +++ b/app/assets/stylesheets/framework/header.scss @@ -7,8 +7,8 @@ header { &.navbar-empty { height: 58px; - background: #FFF; - border-bottom: 1px solid #EEE; + background: #fff; + border-bottom: 1px solid #eee; .center-logo { margin: 11px 0; @@ -28,7 +28,7 @@ header { min-height: $header-height; background-color: #fff; border: none; - border-bottom: 1px solid #EEE; + border-bottom: 1px solid #eee; .container-fluid { width: 100% !important; @@ -47,7 +47,7 @@ header { text-align: center; &:hover, &:focus, &:active { - background-color: #FFF; + background-color: #fff; } } @@ -59,7 +59,7 @@ header { right: 2px; &:hover { - background-color: #EEE; + background-color: #eee; } &.active { color: #7f8fa4; @@ -162,7 +162,7 @@ header { font-size: 18px; .navbar-nav { - margin: 0px; + margin: 0; float: none !important; .visible-xs, .visable-sm { diff --git a/app/assets/stylesheets/framework/highlight.scss b/app/assets/stylesheets/framework/highlight.scss index 12e2f00fe89..7cf4d4fba42 100644 --- a/app/assets/stylesheets/framework/highlight.scss +++ b/app/assets/stylesheets/framework/highlight.scss @@ -1,8 +1,8 @@ .file-content.code { border: none; box-shadow: none; - margin: 0px; - padding: 0px; + margin: 0; + padding: 0; table-layout: fixed; pre { diff --git a/app/assets/stylesheets/framework/issue_box.scss b/app/assets/stylesheets/framework/issue_box.scss index 77a00586b26..7f7b7c806e7 100644 --- a/app/assets/stylesheets/framework/issue_box.scss +++ b/app/assets/stylesheets/framework/issue_box.scss @@ -20,7 +20,7 @@ display: block; float: left; margin-right: 10px; - color: #FFF; + color: #fff; font-size: $gl-font-size; line-height: 25px; diff --git a/app/assets/stylesheets/framework/jquery.scss b/app/assets/stylesheets/framework/jquery.scss index 0cdcd923b3c..525ed81b059 100644 --- a/app/assets/stylesheets/framework/jquery.scss +++ b/app/assets/stylesheets/framework/jquery.scss @@ -3,13 +3,13 @@ font-size: $font-size-base; &.ui-datepicker-inline { - border: 1px solid #DDD; + border: 1px solid #ddd; padding: 10px; width: 270px; .ui-datepicker-header { - background: #FFF; - border-color: #DDD; + background: #fff; + border-color: #ddd; } .ui-datepicker-calendar td a { @@ -19,7 +19,7 @@ } &.ui-autocomplete { - border-color: #DDD; + border-color: #ddd; padding: 0; margin-top: 2px; z-index: 1001; @@ -30,20 +30,20 @@ } .ui-state-default { - border: 1px solid #FFF; - background: #FFF; + border: 1px solid #fff; + background: #fff; color: #777; } .ui-state-highlight { - border: 1px solid #EEE; - background: #EEE; + border: 1px solid #eee; + background: #eee; } .ui-state-active { border: 1px solid $gl-primary; background: $gl-primary; - color: #FFF; + color: #fff; } .ui-state-hover, diff --git a/app/assets/stylesheets/framework/lists.scss b/app/assets/stylesheets/framework/lists.scss index bfec0911b3c..b17c8bcbb1e 100644 --- a/app/assets/stylesheets/framework/lists.scss +++ b/app/assets/stylesheets/framework/lists.scss @@ -111,14 +111,17 @@ ul.content-list { > li { border-color: $table-border-color; - color: $list-text-color; font-size: $list-font-size; + color: $list-text-color; .title { - color: $list-title-color; font-weight: 600; } + a { + color: $gl-dark-link-color; + } + .description { p { @include str-truncated; @@ -141,6 +144,10 @@ ul.content-list { } } +.panel > .content-list > li { + padding: $gl-padding-top $gl-padding; +} + ul.controls { padding-top: 1px; float: right; diff --git a/app/assets/stylesheets/framework/markdown_area.scss b/app/assets/stylesheets/framework/markdown_area.scss index 1d8611b04dc..8328aac4e7a 100644 --- a/app/assets/stylesheets/framework/markdown_area.scss +++ b/app/assets/stylesheets/framework/markdown_area.scss @@ -71,7 +71,7 @@ } .md-preview-holder { - background: #FFF; + background: #fff; border: 1px solid #ddd; min-height: 169px; padding: 5px; @@ -80,7 +80,7 @@ .markdown-area { @include border-radius(0); - background: #FFF; + background: #fff; border: 1px solid #ddd; min-height: 140px; max-height: 500px; diff --git a/app/assets/stylesheets/framework/mixins.scss b/app/assets/stylesheets/framework/mixins.scss index 1d5000fe388..377bfa174bd 100644 --- a/app/assets/stylesheets/framework/mixins.scss +++ b/app/assets/stylesheets/framework/mixins.scss @@ -67,17 +67,17 @@ * Base mixin for lists in GitLab */ @mixin basic-list { - margin: 5px 0px; - padding: 0px; + margin: 5px 0; + padding: 0; list-style: none; > li { @include clearfix; padding: 10px 0; - border-bottom: 1px solid #EEE; + border-bottom: 1px solid #eee; display: block; - margin: 0px; + margin: 0; &:last-child { border-bottom: none; diff --git a/app/assets/stylesheets/framework/mobile.scss b/app/assets/stylesheets/framework/mobile.scss index d088228fe4c..bf874b9b822 100644 --- a/app/assets/stylesheets/framework/mobile.scss +++ b/app/assets/stylesheets/framework/mobile.scss @@ -128,12 +128,12 @@ .show-aside { display: none; position: fixed; - right: 0px; + right: 0; top: 30%; padding: 5px 15px; - background: #EEE; + background: #eee; font-size: 20px; color: #777; z-index: 100; - @include box-shadow(0 1px 2px #DDD); + @include box-shadow(0 1px 2px #ddd); } diff --git a/app/assets/stylesheets/framework/nav.scss b/app/assets/stylesheets/framework/nav.scss index b2fbc95e043..5f4ce87b085 100644 --- a/app/assets/stylesheets/framework/nav.scss +++ b/app/assets/stylesheets/framework/nav.scss @@ -26,7 +26,7 @@ } &.active a { - color: #000000; + color: #000; border-bottom: 2px solid #4688f1; } @@ -41,7 +41,7 @@ .top-area { @include clearfix; - border-bottom: 1px solid #EEE; + border-bottom: 1px solid #eee; .nav-text { padding-top: 16px; @@ -59,7 +59,7 @@ .nav-links { display: inline-block; width: 50%; - margin-bottom: 0px; + margin-bottom: 0; border-bottom: none; /* Small devices (phones, tablets, 768px and lower) */ @@ -74,7 +74,7 @@ float: right; text-align: right; padding: 11px 0; - margin-bottom: 0px; + margin-bottom: 0; > .dropdown { margin-right: $gl-padding-top; diff --git a/app/assets/stylesheets/framework/selects.scss b/app/assets/stylesheets/framework/selects.scss index 7bf04e4ad74..b3371229d5a 100644 --- a/app/assets/stylesheets/framework/selects.scss +++ b/app/assets/stylesheets/framework/selects.scss @@ -151,7 +151,7 @@ padding: 2px 25px 2px 5px; background: #fff image-url('select2.png'); background-repeat: no-repeat; - background-position: right 0px bottom 6px; + background-position: right 0 bottom 6px; border: 1px solid $input-border; @include border-radius($border-radius-default); @include transition(border-color ease-in-out .15s, box-shadow ease-in-out .15s); @@ -229,7 +229,7 @@ .namespace-result { .namespace-kind { - color: #AAA; + color: #aaa; font-weight: normal; } .namespace-path { diff --git a/app/assets/stylesheets/framework/sidebar.scss b/app/assets/stylesheets/framework/sidebar.scss index 26df9acd2ae..be05db58c40 100644 --- a/app/assets/stylesheets/framework/sidebar.scss +++ b/app/assets/stylesheets/framework/sidebar.scss @@ -16,7 +16,7 @@ .gitlab-text-container-link { z-index: 1; position: absolute; - left: 0px; + left: 0; } #logo { @@ -47,7 +47,7 @@ width: 100%; .container-fluid { - background: #FFF; + background: #fff; padding: 0 $gl-padding; &.container-blank { @@ -103,7 +103,7 @@ } &:hover { - background-color: #EEE; + background-color: #eee; } } @@ -143,7 +143,7 @@ overflow: hidden; &.navbar-collapse { - padding: 0px !important; + padding: 0 !important; } li { @@ -182,7 +182,7 @@ .count { float: right; background: #eee; - padding: 0px 8px; + padding: 0 8px; @include border-radius(6px); } @@ -194,8 +194,8 @@ } .sidebar-subnav { - margin-left: 0px; - padding-left: 0px; + margin-left: 0; + padding-left: 0; li { list-style: none; diff --git a/app/assets/stylesheets/framework/tw_bootstrap.scss b/app/assets/stylesheets/framework/tw_bootstrap.scss index ddf76704a53..dd42db1840f 100644 --- a/app/assets/stylesheets/framework/tw_bootstrap.scss +++ b/app/assets/stylesheets/framework/tw_bootstrap.scss @@ -95,7 +95,7 @@ } &.label-inverse { - background-color: #333333; + background-color: #333; } } @@ -138,7 +138,7 @@ } .btn-clipboard { - min-width: 0px; + min-width: 0; } } diff --git a/app/assets/stylesheets/framework/tw_bootstrap_variables.scss b/app/assets/stylesheets/framework/tw_bootstrap_variables.scss index b1b8295411b..f63ac033234 100644 --- a/app/assets/stylesheets/framework/tw_bootstrap_variables.scss +++ b/app/assets/stylesheets/framework/tw_bootstrap_variables.scss @@ -57,7 +57,7 @@ $component-active-bg: $brand-info; $input-color: $text-color; $input-border: #e7e9ed; -$input-border-focus: #7F8FA4; +$input-border-focus: #7f8fa4; $legend-color: $text-color; @@ -125,8 +125,8 @@ $panel-inner-border: $border-color; // //## -$well-bg: #F9F9F9; -$well-border: #EEE; +$well-bg: #f9f9f9; +$well-border: #eee; //== Code // diff --git a/app/assets/stylesheets/framework/typography.scss b/app/assets/stylesheets/framework/typography.scss index 9381cb3281c..949295a1d0c 100644 --- a/app/assets/stylesheets/framework/typography.scss +++ b/app/assets/stylesheets/framework/typography.scss @@ -27,13 +27,13 @@ line-height: 10px; color: #555; vertical-align: middle; - background-color: #FCFCFC; + background-color: #fcfcfc; border-width: 1px; border-style: solid; - border-color: #CCC #CCC #BBB; + border-color: #ccc #ccc #bbb; border-image: none; border-radius: 3px; - box-shadow: 0px -1px 0px #BBB inset; + box-shadow: 0 -1px 0 #bbb inset; } h1 { @@ -187,7 +187,7 @@ body { } .page-title-empty { - margin-top: 0px; + margin-top: 0; line-height: 1.3; font-size: 1.25em; font-weight: 600; diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss index d491d01a3cf..be626678bd7 100644 --- a/app/assets/stylesheets/framework/variables.scss +++ b/app/assets/stylesheets/framework/variables.scss @@ -1,53 +1,83 @@ -$row-hover: #f4f8fe; -$gl-text-color: #54565B; -$gl-text-green: #4A2; -$gl-text-red: #D12F19; -$gl-text-orange: #D90; -$gl-header-color: #323232; -$gl-link-color: #333c48; -$md-text-color: #444; -$md-link-color: #3084bb; -$progress-color: #c0392b; -$gl-font-size: 15px; -$list-font-size: 15px; +/* + * Layout + */ $sidebar_collapsed_width: 62px; $sidebar_width: 230px; $gutter_collapsed_width: 62px; $gutter_width: 290px; $gutter_inner_width: 258px; -$avatar_radius: 50%; + +/* + * UI elements + */ +$border-color: #efeff1; +$table-border-color: #eef0f2; +$background-color: #faf9f9; + +/* + * Text + */ +$gl-font-size: 15px; +$gl-title-color: #333; +$gl-text-color: #555; +$gl-text-green: #4a2; +$gl-text-red: #d12f19; +$gl-text-orange: #d90; +$gl-link-color: #3084bb; +$gl-dark-link-color: #333; +$gl-placeholder-color: #8f8f8f; +$gl-gray: $gl-text-color; +$gl-header-color: $gl-title-color; + +/* + * Lists + */ +$list-font-size: $gl-font-size; +$list-title-color: $gl-title-color; +$list-text-color: $gl-text-color; + +/* + * Markdown + */ +$md-text-color: $gl-text-color; +$md-link-color: $gl-link-color; + +/* + * Code + */ $code_font_size: 13px; $code_line_height: 1.5; -$border-color: #efeff1; -$table-border-color: #eef0f2; -$background-color: #faf9f9; -$header-height: 58px; -$fixed-layout-width: 1280px; -$gl-gray: #5a5a5a; + +/* + * Padding + */ $gl-padding: 16px; $gl-btn-padding: 10px; $gl-vert-padding: 6px; -$gl-padding-top:10px; +$gl-padding-top: 10px; + +/* + * Misc + */ +$row-hover: #f4f8fe; +$progress-color: #c0392b; +$avatar_radius: 50%; +$header-height: 58px; +$fixed-layout-width: 1280px; $gl-avatar-size: 40px; -$secondary-text: #7f8fa4; -$error-exclamation-point: #E62958; +$error-exclamation-point: #e62958; $border-radius-default: 3px; -$list-title-color: #333333; -$list-text-color: #555555; - -$btn-transparent-color: #8F8F8F; - -$ssh-key-icon-color: #8F8F8F; +$btn-transparent-color: #8f8f8f; +$ssh-key-icon-color: #8f8f8f; $ssh-key-icon-size: 18px; - -$provider-btn-group-border: #E5E5E5; -$provider-btn-not-active-color: #4688F1; +$provider-btn-group-border: #e5e5e5; +$provider-btn-not-active-color: #4688f1; /* * Color schema */ -$white-light: #FFFFFF; +$white-light: #fff; $white-normal: #ededed; $white-dark: #ededed; @@ -57,55 +87,55 @@ $gray-dark: #ededed; $gray-darkest: #c9c9c9; $green-light: #38ae67; -$green-normal: #2FAA60; -$green-dark: #2CA05B; +$green-normal: #2faa60; +$green-dark: #2ca05b; -$blue-light: #2EA8E5; -$blue-normal: #2D9FD8; -$blue-dark: #2897CE; +$blue-light: #2ea8e5; +$blue-normal: #2d9fd8; +$blue-dark: #2897ce; -$blue-medium-light: #3498CB; -$blue-medium: #2F8EBF; -$blue-medium-dark: #2D86B4; +$blue-medium-light: #3498cb; +$blue-medium: #2f8ebf; +$blue-medium-dark: #2d86b4; $orange-light: rgba(252, 109, 38, 0.80); -$orange-normal: #E75E40; -$orange-dark: #CE5237; +$orange-normal: #e75e40; +$orange-dark: #ce5237; -$red-light: #F06559; -$red-normal: #E52C5A; -$red-dark: #D22852; +$red-light: #f06559; +$red-normal: #e52c5a; +$red-dark: #d22852; -$border-white-light: #F1F2F4; -$border-white-normal: #D6DAE2; -$border-white-dark: #C6CACF; +$border-white-light: #f1f2f4; +$border-white-normal: #d6dae2; +$border-white-dark: #c6cacf; $border-gray-light: rgba(0, 0, 0, 0.06); $border-gray-normal: rgba(0, 0, 0, 0.10);; -$border-gray-dark: #C6CACF; +$border-gray-dark: #c6cacf; -$border-green-light: #2FAA60; -$border-green-normal: #2CA05B; +$border-green-light: #2faa60; +$border-green-normal: #2ca05b; $border-green-dark: #279654; -$border-blue-light: #2D9FD8; -$border-blue-normal: #2897CE; -$border-blue-dark: #258DC1; +$border-blue-light: #2d9fd8; +$border-blue-normal: #2897ce; +$border-blue-dark: #258dc1; $border-orange-light: #fc6d26; -$border-orange-normal: #CE5237; -$border-orange-dark: #C14E35; +$border-orange-normal: #ce5237; +$border-orange-dark: #c14e35; -$border-red-light: #F24F41; -$border-red-normal: #D22852; -$border-red-dark: #CA264F; +$border-red-light: #f24f41; +$border-red-normal: #d22852; +$border-red-dark: #ca264f; -$help-well-bg: #FAFAFA; -$help-well-border: #E5E5E5; +$help-well-bg: #fafafa; +$help-well-border: #e5e5e5; -$warning-message-bg: #FBF2D9; -$warning-message-color: #9E8E60; -$warning-message-border: #F0E2BB; +$warning-message-bg: #fbf2d9; +$warning-message-color: #9e8e60; +$warning-message-border: #f0e2bb; /* header */ $light-grey-header: #faf9f9; @@ -143,23 +173,22 @@ $dropdown-border-color: rgba(#000, .1); $dropdown-shadow-color: rgba(#000, .1); $dropdown-divider-color: rgba(#000, .1); $dropdown-header-color: #959494; -$dropdown-caret-color: #54565B; -$dropdown-title-btn-color: #BFBFBF; -$dropdown-input-color: #C7C7C7; +$dropdown-title-btn-color: #bfbfbf; +$dropdown-input-color: #c7c7c7; $dropdown-input-focus-border: rgb(58, 171, 240); $dropdown-input-focus-shadow: rgba(#000, .2); $dropdown-loading-bg: rgba(#fff, .6); $dropdown-toggle-bg: #fff; $dropdown-toggle-color: #626262; -$dropdown-toggle-border-color: #EAEAEA; +$dropdown-toggle-border-color: #eaeaea; $dropdown-toggle-hover-border-color: darken($dropdown-toggle-border-color, 15%); -$dropdown-toggle-icon-color: #C4C4C4; +$dropdown-toggle-icon-color: #c4c4c4; $dropdown-toggle-hover-icon-color: $dropdown-toggle-hover-border-color; /* * Award emoji */ -$award-emoji-menu-bg: #FFF; -$award-emoji-menu-border: #F1F2F4; -$award-emoji-new-btn-icon-color: #DCDCDC; +$award-emoji-menu-bg: #fff; +$award-emoji-menu-border: #f1f2f4; +$award-emoji-new-btn-icon-color: #dcdcdc; diff --git a/app/assets/stylesheets/framework/zen.scss b/app/assets/stylesheets/framework/zen.scss index c3f27333fad..02e24ec7c4d 100644 --- a/app/assets/stylesheets/framework/zen.scss +++ b/app/assets/stylesheets/framework/zen.scss @@ -2,7 +2,7 @@ a.js-zen-enter { color: $gl-gray; position: absolute; - top: 0px; + top: 0; right: 4px; line-height: 56px; } diff --git a/app/assets/stylesheets/highlight/dark.scss b/app/assets/stylesheets/highlight/dark.scss index b794da2ce98..47673944896 100644 --- a/app/assets/stylesheets/highlight/dark.scss +++ b/app/assets/stylesheets/highlight/dark.scss @@ -43,12 +43,12 @@ // Search result highlight span.highlight_word { background-color: #ffe792 !important; - color: #000000 !important; + color: #000 !important; } .hll { background-color: #373b41 } .c { color: #969896 } /* Comment */ - .err { color: #cc6666 } /* Error */ + .err { color: #c66 } /* Error */ .k { color: #b294bb } /* Keyword */ .l { color: #de935f } /* Literal */ .n { color: #c5c8c6 } /* Name */ @@ -58,7 +58,7 @@ .cp { color: #969896 } /* Comment.Preproc */ .c1 { color: #969896 } /* Comment.Single */ .cs { color: #969896 } /* Comment.Special */ - .gd { color: #cc6666 } /* Generic.Deleted */ + .gd { color: #c66 } /* Generic.Deleted */ .ge { font-style: italic } /* Generic.Emph */ .gh { color: #c5c8c6; font-weight: bold } /* Generic.Heading */ .gi { color: #b5bd68 } /* Generic.Inserted */ @@ -77,17 +77,17 @@ .na { color: #81a2be } /* Name.Attribute */ .nb { color: #c5c8c6 } /* Name.Builtin */ .nc { color: #f0c674 } /* Name.Class */ - .no { color: #cc6666 } /* Name.Constant */ + .no { color: #c66 } /* Name.Constant */ .nd { color: #8abeb7 } /* Name.Decorator */ .ni { color: #c5c8c6 } /* Name.Entity */ - .ne { color: #cc6666 } /* Name.Exception */ + .ne { color: #c66 } /* Name.Exception */ .nf { color: #81a2be } /* Name.Function */ .nl { color: #c5c8c6 } /* Name.Label */ .nn { color: #f0c674 } /* Name.Namespace */ .nx { color: #81a2be } /* Name.Other */ .py { color: #c5c8c6 } /* Name.Property */ .nt { color: #8abeb7 } /* Name.Tag */ - .nv { color: #cc6666 } /* Name.Variable */ + .nv { color: #c66 } /* Name.Variable */ .ow { color: #8abeb7 } /* Operator.Word */ .w { color: #c5c8c6 } /* Text.Whitespace */ .mf { color: #de935f } /* Literal.Number.Float */ @@ -106,8 +106,8 @@ .s1 { color: #b5bd68 } /* Literal.String.Single */ .ss { color: #b5bd68 } /* Literal.String.Symbol */ .bp { color: #c5c8c6 } /* Name.Builtin.Pseudo */ - .vc { color: #cc6666 } /* Name.Variable.Class */ - .vg { color: #cc6666 } /* Name.Variable.Global */ - .vi { color: #cc6666 } /* Name.Variable.Instance */ + .vc { color: #c66 } /* Name.Variable.Class */ + .vg { color: #c66 } /* Name.Variable.Global */ + .vi { color: #c66 } /* Name.Variable.Instance */ .il { color: #de935f } /* Literal.Number.Integer.Long */ } diff --git a/app/assets/stylesheets/highlight/monokai.scss b/app/assets/stylesheets/highlight/monokai.scss index 9098e07adcd..806401c21ae 100644 --- a/app/assets/stylesheets/highlight/monokai.scss +++ b/app/assets/stylesheets/highlight/monokai.scss @@ -43,7 +43,7 @@ // Search result highlight span.highlight_word { background-color: #ffe792 !important; - color: #000000 !important; + color: #000 !important; } .hll { background-color: #49483e } diff --git a/app/assets/stylesheets/highlight/solarized_dark.scss b/app/assets/stylesheets/highlight/solarized_dark.scss index 8b1a2824f76..6a809d4dfd2 100644 --- a/app/assets/stylesheets/highlight/solarized_dark.scss +++ b/app/assets/stylesheets/highlight/solarized_dark.scss @@ -96,7 +96,7 @@ .m { color: #2aa198 } /* Literal.Number */ .s { color: #2aa198 } /* Literal.String */ .na { color: #93a1a1 } /* Name.Attribute */ - .nb { color: #B58900 } /* Name.Builtin */ + .nb { color: #b58900 } /* Name.Builtin */ .nc { color: #268bd2 } /* Name.Class */ .no { color: #cb4b16 } /* Name.Constant */ .nd { color: #268bd2 } /* Name.Decorator */ diff --git a/app/assets/stylesheets/highlight/solarized_light.scss b/app/assets/stylesheets/highlight/solarized_light.scss index 7ad89dd2c7c..b90c95c62d1 100644 --- a/app/assets/stylesheets/highlight/solarized_light.scss +++ b/app/assets/stylesheets/highlight/solarized_light.scss @@ -96,7 +96,7 @@ .m { color: #2aa198 } /* Literal.Number */ .s { color: #2aa198 } /* Literal.String */ .na { color: #586e75 } /* Name.Attribute */ - .nb { color: #B58900 } /* Name.Builtin */ + .nb { color: #b58900 } /* Name.Builtin */ .nc { color: #268bd2 } /* Name.Class */ .no { color: #cb4b16 } /* Name.Constant */ .nd { color: #268bd2 } /* Name.Decorator */ diff --git a/app/assets/stylesheets/highlight/white.scss b/app/assets/stylesheets/highlight/white.scss index 8a091028a6c..8c1b0cd84ec 100644 --- a/app/assets/stylesheets/highlight/white.scss +++ b/app/assets/stylesheets/highlight/white.scss @@ -23,7 +23,7 @@ .line_holder { .diff-line-num { &.old { - background: #ffdddd; + background: #fdd; border-color: #f1c0c0; } @@ -68,66 +68,66 @@ } .hll { background-color: #f8f8f8 } - .c { color: #999988; font-style: italic; } + .c { color: #998; font-style: italic; } .err { color: #a61717; background-color: #e3d2d2; } .k { font-weight: bold; } .o { font-weight: bold; } - .cm { color: #999988; font-style: italic; } - .cp { color: #999999; font-weight: bold; } - .c1 { color: #999988; font-style: italic; } - .cs { color: #999999; font-weight: bold; font-style: italic; } - .gd { color: #000000; background-color: #ffdddd; } - .gd .x { color: #000000; background-color: #ffaaaa; } + .cm { color: #998; font-style: italic; } + .cp { color: #999; font-weight: bold; } + .c1 { color: #998; font-style: italic; } + .cs { color: #999; font-weight: bold; font-style: italic; } + .gd { color: #000; background-color: #fdd; } + .gd .x { color: #000; background-color: #faa; } .ge { font-style: italic; } - .gr { color: #aa0000; } - .gh { color: #999999; } - .gi { color: #000000; background-color: #ddffdd; } - .gi .x { color: #000000; background-color: #aaffaa; } - .go { color: #888888; } - .gp { color: #555555; } + .gr { color: #a00; } + .gh { color: #999; } + .gi { color: #000; background-color: #dfd; } + .gi .x { color: #000; background-color: #afa; } + .go { color: #888; } + .gp { color: #555; } .gs { font-weight: bold; } .gu { color: #800080; font-weight: bold; } - .gt { color: #aa0000; } + .gt { color: #a00; } .kc { font-weight: bold; } .kd { font-weight: bold; } .kn { font-weight: bold; } .kp { font-weight: bold; } .kr { font-weight: bold; } - .kt { color: #445588; font-weight: bold; } - .m { color: #009999; } - .s { color: #dd1144; } - .n { color: #333333; } + .kt { color: #458; font-weight: bold; } + .m { color: #099; } + .s { color: #d14; } + .n { color: #333; } .na { color: teal; } .nb { color: #0086b3; } - .nc { color: #445588; font-weight: bold; } + .nc { color: #458; font-weight: bold; } .no { color: teal; } .ni { color: purple; } - .ne { color: #990000; font-weight: bold; } - .nf { color: #990000; font-weight: bold; } - .nn { color: #555555; } + .ne { color: #900; font-weight: bold; } + .nf { color: #900; font-weight: bold; } + .nn { color: #555; } .nt { color: navy; } .nv { color: teal; } .ow { font-weight: bold; } - .w { color: #bbbbbb; } - .mf { color: #009999; } - .mh { color: #009999; } - .mi { color: #009999; } - .mo { color: #009999; } - .sb { color: #dd1144; } - .sc { color: #dd1144; } - .sd { color: #dd1144; } - .s2 { color: #dd1144; } - .se { color: #dd1144; } - .sh { color: #dd1144; } - .si { color: #dd1144; } - .sx { color: #dd1144; } + .w { color: #bbb; } + .mf { color: #099; } + .mh { color: #099; } + .mi { color: #099; } + .mo { color: #099; } + .sb { color: #d14; } + .sc { color: #d14; } + .sd { color: #d14; } + .s2 { color: #d14; } + .se { color: #d14; } + .sh { color: #d14; } + .si { color: #d14; } + .sx { color: #d14; } .sr { color: #009926; } - .s1 { color: #dd1144; } + .s1 { color: #d14; } .ss { color: #990073; } - .bp { color: #999999; } + .bp { color: #999; } .vc { color: teal; } .vg { color: teal; } .vi { color: teal; } - .il { color: #009999; } - .gc { color: #999; background-color: #EAF2F5; } + .il { color: #099; } + .gc { color: #999; background-color: #eaf2f5; } } diff --git a/app/assets/stylesheets/pages/appearances.scss b/app/assets/stylesheets/pages/appearances.scss index e2070f17c3b..878f44116ba 100644 --- a/app/assets/stylesheets/pages/appearances.scss +++ b/app/assets/stylesheets/pages/appearances.scss @@ -4,7 +4,7 @@ } .appearance-light-logo-preview { - background-color: $background-color; + background-color: $background-color; max-width: 72px; padding: 10px; margin-bottom: 10px; diff --git a/app/assets/stylesheets/pages/builds.scss b/app/assets/stylesheets/pages/builds.scss index 75f298019e3..201f3e5ca46 100644 --- a/app/assets/stylesheets/pages/builds.scss +++ b/app/assets/stylesheets/pages/builds.scss @@ -1,6 +1,6 @@ .build-page { pre.trace { - background: #111111; + background: #111; color: #fff; font-family: $monospace_font; white-space: pre; diff --git a/app/assets/stylesheets/pages/commit.scss b/app/assets/stylesheets/pages/commit.scss index c0cc30d33a6..971656feb42 100644 --- a/app/assets/stylesheets/pages/commit.scss +++ b/app/assets/stylesheets/pages/commit.scss @@ -55,7 +55,7 @@ padding: 10px 0; li { - padding: 3px 0px; + padding: 3px 0; line-height: 20px; } } diff --git a/app/assets/stylesheets/pages/commits.scss b/app/assets/stylesheets/pages/commits.scss index 818fd03e2ae..33b3c7558ed 100644 --- a/app/assets/stylesheets/pages/commits.scss +++ b/app/assets/stylesheets/pages/commits.scss @@ -9,7 +9,7 @@ .lists-separator { margin: 10px 0; - border-color: #DDD; + border-color: #ddd; } .commits-row { @@ -55,7 +55,7 @@ li.commit { } .commit-row-message { - color: $gl-link-color; + color: $gl-dark-link-color; &:hover { text-decoration: underline; @@ -76,7 +76,7 @@ li.commit { .commit-row-description { font-size: 14px; - border-left: 1px solid #EEE; + border-left: 1px solid #eee; padding: 10px 15px; margin: 5px 0 10px 5px; background: #f9f9f9; @@ -152,7 +152,7 @@ li.commit { .count { padding-top: 6px; - padding-bottom: 0px; + padding-bottom: 0; font-size: 12px; color: #333; display: block; diff --git a/app/assets/stylesheets/pages/dashboard.scss b/app/assets/stylesheets/pages/dashboard.scss index 88639399148..cf7567513ec 100644 --- a/app/assets/stylesheets/pages/dashboard.scss +++ b/app/assets/stylesheets/pages/dashboard.scss @@ -11,15 +11,15 @@ } .dashboard-search-filter { - padding:5px; + padding: 5px; .search-text-input { - float:left; + float: left; @extend .col-md-2; } .btn { margin-left: 5px; - float:left; + float: left; } } diff --git a/app/assets/stylesheets/pages/diff.scss b/app/assets/stylesheets/pages/diff.scss index a7925e79549..d5862a11aca 100644 --- a/app/assets/stylesheets/pages/diff.scss +++ b/app/assets/stylesheets/pages/diff.scss @@ -1,7 +1,7 @@ // Common .diff-file { border: 1px solid $border-color; - border-top: none; + margin-bottom: $gl-padding; .diff-header { position: relative; @@ -29,7 +29,7 @@ .diff-content { overflow: auto; overflow-y: hidden; - background: #FFF; + background: #fff; color: #333; .unfold { @@ -57,8 +57,8 @@ font-family: $monospace_font; border: none; border-collapse: separate; - margin: 0px; - padding: 0px; + margin: 0; + padding: 0; .line_holder td { line-height: $code_line_height; font-size: $code_font_size; @@ -76,10 +76,10 @@ } .old_line, .new_line { - margin: 0px; - padding: 0px; + margin: 0; + padding: 0; border: none; - padding: 0px 5px; + padding: 0 5px; border-right: 1px solid; text-align: right; min-width: 35px; @@ -97,8 +97,8 @@ } .line_content { display: block; - margin: 0px; - padding: 0px 0.5em; + margin: 0; + padding: 0 0.5em; border: none; &.parallel { display: table-cell; @@ -118,7 +118,7 @@ background-color: #fff; line-height: 0; img { - border: 1px solid #FFF; + border: 1px solid #fff; background: image-url('trans_bg.gif'); max-width: 100%; } @@ -183,7 +183,7 @@ height: 14px; width: 15px; position: absolute; - top: 0px; + top: 0; background: image-url('swipemode_sprites.gif') 0 3px no-repeat; } .bottom-handle { @@ -191,7 +191,7 @@ height: 14px; width: 15px; position: absolute; - bottom: 0px; + bottom: 0; background: image-url('swipemode_sprites.gif') 0 -11px no-repeat; } } @@ -206,8 +206,8 @@ .frame.added, .frame.deleted { position: absolute; display: block; - top: 0px; - left: 0px; + top: 0; + left: 0; } .controls { display: block; @@ -215,7 +215,7 @@ width: 300px; z-index: 100; position: absolute; - bottom: 0px; + bottom: 0; left: 50%; margin-left: -150px; @@ -231,11 +231,11 @@ .dragger { display: block; position: absolute; - left: 0px; - top: 0px; + left: 0; + top: 0; height: 14px; width: 14px; - background: image-url('onion_skin_sprites.gif') 0px -34px repeat-x; + background: image-url('onion_skin_sprites.gif') 0 -34px repeat-x; cursor: pointer; } @@ -243,17 +243,17 @@ display: block; position: absolute; top: 2px; - right: 0px; + right: 0; height: 10px; width: 10px; - background: image-url('onion_skin_sprites.gif') -2px 0px no-repeat; + background: image-url('onion_skin_sprites.gif') -2px 0 no-repeat; } .opaque { display: block; position: absolute; top: 2px; - left: 0px; + left: 0; height: 10px; width: 10px; background: image-url('onion_skin_sprites.gif') -2px -10px no-repeat; @@ -265,7 +265,7 @@ .view-modes { padding: 10px; text-align: center; - background: #EEE; + background: #eee; ul, li { list-style: none; @@ -361,3 +361,11 @@ border-color: $border; } } + +.files { + margin-top: -1px; + + .diff-file:last-child { + margin-bottom: 0; + } +} diff --git a/app/assets/stylesheets/pages/editor.scss b/app/assets/stylesheets/pages/editor.scss index 39d916cd336..43be5e38ba8 100644 --- a/app/assets/stylesheets/pages/editor.scss +++ b/app/assets/stylesheets/pages/editor.scss @@ -14,9 +14,9 @@ } .cancel-btn { - color: #B94A48; + color: #b94a48; &:hover { - color: #B94A48; + color: #b94a48; } } diff --git a/app/assets/stylesheets/pages/emojis.scss b/app/assets/stylesheets/pages/emojis.scss index 6c721b514f8..b731abc7450 100644 --- a/app/assets/stylesheets/pages/emojis.scss +++ b/app/assets/stylesheets/pages/emojis.scss @@ -1,60 +1,60 @@ -.emoji-0023-20E3 { background-position: 0px 0px; } -.emoji-002A-20E3 { background-position: -20px 0px; } -.emoji-0030-20E3 { background-position: 0px -20px; } +.emoji-0023-20E3 { background-position: 0 0; } +.emoji-002A-20E3 { background-position: -20px 0; } +.emoji-0030-20E3 { background-position: 0 -20px; } .emoji-0031-20E3 { background-position: -20px -20px; } -.emoji-0032-20E3 { background-position: -40px 0px; } +.emoji-0032-20E3 { background-position: -40px 0; } .emoji-0033-20E3 { background-position: -40px -20px; } -.emoji-0034-20E3 { background-position: 0px -40px; } +.emoji-0034-20E3 { background-position: 0 -40px; } .emoji-0035-20E3 { background-position: -20px -40px; } .emoji-0036-20E3 { background-position: -40px -40px; } -.emoji-0037-20E3 { background-position: -60px 0px; } +.emoji-0037-20E3 { background-position: -60px 0; } .emoji-0038-20E3 { background-position: -60px -20px; } .emoji-0039-20E3 { background-position: -60px -40px; } -.emoji-00A9 { background-position: 0px -60px; } +.emoji-00A9 { background-position: 0 -60px; } .emoji-00AE { background-position: -20px -60px; } .emoji-1F004 { background-position: -40px -60px; } .emoji-1F0CF { background-position: -60px -60px; } -.emoji-1F170 { background-position: -80px 0px; } +.emoji-1F170 { background-position: -80px 0; } .emoji-1F171 { background-position: -80px -20px; } .emoji-1F17E { background-position: -80px -40px; } .emoji-1F17F { background-position: -80px -60px; } -.emoji-1F18E { background-position: 0px -80px; } +.emoji-1F18E { background-position: 0 -80px; } .emoji-1F191 { background-position: -20px -80px; } .emoji-1F192 { background-position: -40px -80px; } .emoji-1F193 { background-position: -60px -80px; } .emoji-1F194 { background-position: -80px -80px; } -.emoji-1F195 { background-position: -100px 0px; } +.emoji-1F195 { background-position: -100px 0; } .emoji-1F196 { background-position: -100px -20px; } .emoji-1F197 { background-position: -100px -40px; } .emoji-1F198 { background-position: -100px -60px; } .emoji-1F199 { background-position: -100px -80px; } -.emoji-1F19A { background-position: 0px -100px; } +.emoji-1F19A { background-position: 0 -100px; } .emoji-1F1E6-1F1E8 { background-position: -20px -100px; } .emoji-1F1E6-1F1E9 { background-position: -40px -100px; } .emoji-1F1E6-1F1EA { background-position: -60px -100px; } .emoji-1F1E6-1F1EB { background-position: -80px -100px; } .emoji-1F1E6-1F1EC { background-position: -100px -100px; } -.emoji-1F1E6-1F1EE { background-position: -120px 0px; } +.emoji-1F1E6-1F1EE { background-position: -120px 0; } .emoji-1F1E6-1F1F1 { background-position: -120px -20px; } .emoji-1F1E6-1F1F2 { background-position: -120px -40px; } .emoji-1F1E6-1F1F4 { background-position: -120px -60px; } .emoji-1F1E6-1F1F6 { background-position: -120px -80px; } .emoji-1F1E6-1F1F7 { background-position: -120px -100px; } -.emoji-1F1E6-1F1F8 { background-position: 0px -120px; } +.emoji-1F1E6-1F1F8 { background-position: 0 -120px; } .emoji-1F1E6-1F1F9 { background-position: -20px -120px; } .emoji-1F1E6-1F1FA { background-position: -40px -120px; } .emoji-1F1E6-1F1FC { background-position: -60px -120px; } .emoji-1F1E6-1F1FD { background-position: -80px -120px; } .emoji-1F1E6-1F1FF { background-position: -100px -120px; } .emoji-1F1E7-1F1E6 { background-position: -120px -120px; } -.emoji-1F1E7-1F1E7 { background-position: -140px 0px; } +.emoji-1F1E7-1F1E7 { background-position: -140px 0; } .emoji-1F1E7-1F1E9 { background-position: -140px -20px; } .emoji-1F1E7-1F1EA { background-position: -140px -40px; } .emoji-1F1E7-1F1EB { background-position: -140px -60px; } .emoji-1F1E7-1F1EC { background-position: -140px -80px; } .emoji-1F1E7-1F1ED { background-position: -140px -100px; } .emoji-1F1E7-1F1EE { background-position: -140px -120px; } -.emoji-1F1E7-1F1EF { background-position: 0px -140px; } +.emoji-1F1E7-1F1EF { background-position: 0 -140px; } .emoji-1F1E7-1F1F1 { background-position: -20px -140px; } .emoji-1F1E7-1F1F2 { background-position: -40px -140px; } .emoji-1F1E7-1F1F3 { background-position: -60px -140px; } @@ -62,7 +62,7 @@ .emoji-1F1E7-1F1F6 { background-position: -100px -140px; } .emoji-1F1E7-1F1F7 { background-position: -120px -140px; } .emoji-1F1E7-1F1F8 { background-position: -140px -140px; } -.emoji-1F1E7-1F1F9 { background-position: -160px 0px; } +.emoji-1F1E7-1F1F9 { background-position: -160px 0; } .emoji-1F1E7-1F1FB { background-position: -160px -20px; } .emoji-1F1E7-1F1FC { background-position: -160px -40px; } .emoji-1F1E7-1F1FE { background-position: -160px -60px; } @@ -70,7 +70,7 @@ .emoji-1F1E8-1F1E6 { background-position: -160px -100px; } .emoji-1F1E8-1F1E8 { background-position: -160px -120px; } .emoji-1F1E8-1F1E9 { background-position: -160px -140px; } -.emoji-1F1E8-1F1EB { background-position: 0px -160px; } +.emoji-1F1E8-1F1EB { background-position: 0 -160px; } .emoji-1F1E8-1F1EC { background-position: -20px -160px; } .emoji-1F1E8-1F1ED { background-position: -40px -160px; } .emoji-1F1E8-1F1EE { background-position: -60px -160px; } @@ -79,7 +79,7 @@ .emoji-1F1E8-1F1F2 { background-position: -120px -160px; } .emoji-1F1E8-1F1F3 { background-position: -140px -160px; } .emoji-1F1E8-1F1F4 { background-position: -160px -160px; } -.emoji-1F1E8-1F1F5 { background-position: -180px 0px; } +.emoji-1F1E8-1F1F5 { background-position: -180px 0; } .emoji-1F1E8-1F1F7 { background-position: -180px -20px; } .emoji-1F1E8-1F1FA { background-position: -180px -40px; } .emoji-1F1E8-1F1FB { background-position: -180px -60px; } @@ -88,7 +88,7 @@ .emoji-1F1E8-1F1FE { background-position: -180px -120px; } .emoji-1F1E8-1F1FF { background-position: -180px -140px; } .emoji-1F1E9-1F1EA { background-position: -180px -160px; } -.emoji-1F1E9-1F1EC { background-position: 0px -180px; } +.emoji-1F1E9-1F1EC { background-position: 0 -180px; } .emoji-1F1E9-1F1EF { background-position: -20px -180px; } .emoji-1F1E9-1F1F0 { background-position: -40px -180px; } .emoji-1F1E9-1F1F2 { background-position: -60px -180px; } @@ -98,7 +98,7 @@ .emoji-1F1EA-1F1E8 { background-position: -140px -180px; } .emoji-1F1EA-1F1EA { background-position: -160px -180px; } .emoji-1F1EA-1F1EC { background-position: -180px -180px; } -.emoji-1F1EA-1F1ED { background-position: -200px 0px; } +.emoji-1F1EA-1F1ED { background-position: -200px 0; } .emoji-1F1EA-1F1F7 { background-position: -200px -20px; } .emoji-1F1EA-1F1F8 { background-position: -200px -40px; } .emoji-1F1EA-1F1F9 { background-position: -200px -60px; } @@ -108,7 +108,7 @@ .emoji-1F1EB-1F1F0 { background-position: -200px -140px; } .emoji-1F1EB-1F1F2 { background-position: -200px -160px; } .emoji-1F1EB-1F1F4 { background-position: -200px -180px; } -.emoji-1F1EB-1F1F7 { background-position: 0px -200px; } +.emoji-1F1EB-1F1F7 { background-position: 0 -200px; } .emoji-1F1EC-1F1E6 { background-position: -20px -200px; } .emoji-1F1EC-1F1E7 { background-position: -40px -200px; } .emoji-1F1EC-1F1E9 { background-position: -60px -200px; } @@ -119,7 +119,7 @@ .emoji-1F1EC-1F1EE { background-position: -160px -200px; } .emoji-1F1EC-1F1F1 { background-position: -180px -200px; } .emoji-1F1EC-1F1F2 { background-position: -200px -200px; } -.emoji-1F1EC-1F1F3 { background-position: -220px 0px; } +.emoji-1F1EC-1F1F3 { background-position: -220px 0; } .emoji-1F1EC-1F1F5 { background-position: -220px -20px; } .emoji-1F1EC-1F1F6 { background-position: -220px -40px; } .emoji-1F1EC-1F1F7 { background-position: -220px -60px; } @@ -130,7 +130,7 @@ .emoji-1F1EC-1F1FE { background-position: -220px -160px; } .emoji-1F1ED-1F1F0 { background-position: -220px -180px; } .emoji-1F1ED-1F1F2 { background-position: -220px -200px; } -.emoji-1F1ED-1F1F3 { background-position: 0px -220px; } +.emoji-1F1ED-1F1F3 { background-position: 0 -220px; } .emoji-1F1ED-1F1F7 { background-position: -20px -220px; } .emoji-1F1ED-1F1F9 { background-position: -40px -220px; } .emoji-1F1ED-1F1FA { background-position: -60px -220px; } @@ -142,7 +142,7 @@ .emoji-1F1EE-1F1F3 { background-position: -180px -220px; } .emoji-1F1EE-1F1F4 { background-position: -200px -220px; } .emoji-1F1EE-1F1F6 { background-position: -220px -220px; } -.emoji-1F1EE-1F1F7 { background-position: -240px 0px; } +.emoji-1F1EE-1F1F7 { background-position: -240px 0; } .emoji-1F1EE-1F1F8 { background-position: -240px -20px; } .emoji-1F1EE-1F1F9 { background-position: -240px -40px; } .emoji-1F1EF-1F1EA { background-position: -240px -60px; } @@ -154,7 +154,7 @@ .emoji-1F1F0-1F1ED { background-position: -240px -180px; } .emoji-1F1F0-1F1EE { background-position: -240px -200px; } .emoji-1F1F0-1F1F2 { background-position: -240px -220px; } -.emoji-1F1F0-1F1F3 { background-position: 0px -240px; } +.emoji-1F1F0-1F1F3 { background-position: 0 -240px; } .emoji-1F1F0-1F1F5 { background-position: -20px -240px; } .emoji-1F1F0-1F1F7 { background-position: -40px -240px; } .emoji-1F1F0-1F1FC { background-position: -60px -240px; } @@ -167,7 +167,7 @@ .emoji-1F1F1-1F1F0 { background-position: -200px -240px; } .emoji-1F1F1-1F1F7 { background-position: -220px -240px; } .emoji-1F1F1-1F1F8 { background-position: -240px -240px; } -.emoji-1F1F1-1F1F9 { background-position: -260px 0px; } +.emoji-1F1F1-1F1F9 { background-position: -260px 0; } .emoji-1F1F1-1F1FA { background-position: -260px -20px; } .emoji-1F1F1-1F1FB { background-position: -260px -40px; } .emoji-1F1F1-1F1FE { background-position: -260px -60px; } @@ -180,7 +180,7 @@ .emoji-1F1F2-1F1ED { background-position: -260px -200px; } .emoji-1F1F2-1F1F0 { background-position: -260px -220px; } .emoji-1F1F2-1F1F1 { background-position: -260px -240px; } -.emoji-1F1F2-1F1F2 { background-position: 0px -260px; } +.emoji-1F1F2-1F1F2 { background-position: 0 -260px; } .emoji-1F1F2-1F1F3 { background-position: -20px -260px; } .emoji-1F1F2-1F1F4 { background-position: -40px -260px; } .emoji-1F1F2-1F1F5 { background-position: -60px -260px; } @@ -194,7 +194,7 @@ .emoji-1F1F2-1F1FD { background-position: -220px -260px; } .emoji-1F1F2-1F1FE { background-position: -240px -260px; } .emoji-1F1F2-1F1FF { background-position: -260px -260px; } -.emoji-1F1F3-1F1E6 { background-position: -280px 0px; } +.emoji-1F1F3-1F1E6 { background-position: -280px 0; } .emoji-1F1F3-1F1E8 { background-position: -280px -20px; } .emoji-1F1F3-1F1EA { background-position: -280px -40px; } .emoji-1F1F3-1F1EB { background-position: -280px -60px; } @@ -208,7 +208,7 @@ .emoji-1F1F3-1F1FF { background-position: -280px -220px; } .emoji-1F1F4-1F1F2 { background-position: -280px -240px; } .emoji-1F1F5-1F1E6 { background-position: -280px -260px; } -.emoji-1F1F5-1F1EA { background-position: 0px -280px; } +.emoji-1F1F5-1F1EA { background-position: 0 -280px; } .emoji-1F1F5-1F1EB { background-position: -20px -280px; } .emoji-1F1F5-1F1EC { background-position: -40px -280px; } .emoji-1F1F5-1F1ED { background-position: -60px -280px; } @@ -223,7 +223,7 @@ .emoji-1F1F5-1F1FE { background-position: -240px -280px; } .emoji-1F1F6-1F1E6 { background-position: -260px -280px; } .emoji-1F1F7-1F1EA { background-position: -280px -280px; } -.emoji-1F1F7-1F1F4 { background-position: -300px 0px; } +.emoji-1F1F7-1F1F4 { background-position: -300px 0; } .emoji-1F1F7-1F1F8 { background-position: -300px -20px; } .emoji-1F1F7-1F1FA { background-position: -300px -40px; } .emoji-1F1F7-1F1FC { background-position: -300px -60px; } @@ -238,7 +238,7 @@ .emoji-1F1F8-1F1EF { background-position: -300px -240px; } .emoji-1F1F8-1F1F0 { background-position: -300px -260px; } .emoji-1F1F8-1F1F1 { background-position: -300px -280px; } -.emoji-1F1F8-1F1F2 { background-position: 0px -300px; } +.emoji-1F1F8-1F1F2 { background-position: 0 -300px; } .emoji-1F1F8-1F1F3 { background-position: -20px -300px; } .emoji-1F1F8-1F1F4 { background-position: -40px -300px; } .emoji-1F1F8-1F1F7 { background-position: -60px -300px; } @@ -254,7 +254,7 @@ .emoji-1F1F9-1F1EB { background-position: -260px -300px; } .emoji-1F1F9-1F1EC { background-position: -280px -300px; } .emoji-1F1F9-1F1ED { background-position: -300px -300px; } -.emoji-1F1F9-1F1EF { background-position: -320px 0px; } +.emoji-1F1F9-1F1EF { background-position: -320px 0; } .emoji-1F1F9-1F1F0 { background-position: -320px -20px; } .emoji-1F1F9-1F1F1 { background-position: -320px -40px; } .emoji-1F1F9-1F1F2 { background-position: -320px -60px; } @@ -270,7 +270,7 @@ .emoji-1F1FA-1F1F2 { background-position: -320px -260px; } .emoji-1F1FA-1F1F8 { background-position: -320px -280px; } .emoji-1F1FA-1F1FE { background-position: -320px -300px; } -.emoji-1F1FA-1F1FF { background-position: 0px -320px; } +.emoji-1F1FA-1F1FF { background-position: 0 -320px; } .emoji-1F1FB-1F1E6 { background-position: -20px -320px; } .emoji-1F1FB-1F1E8 { background-position: -40px -320px; } .emoji-1F1FB-1F1EA { background-position: -60px -320px; } @@ -287,7 +287,7 @@ .emoji-1F1FF-1F1F2 { background-position: -280px -320px; } .emoji-1F1FF-1F1FC { background-position: -300px -320px; } .emoji-1F201 { background-position: -320px -320px; } -.emoji-1F202 { background-position: -340px 0px; } +.emoji-1F202 { background-position: -340px 0; } .emoji-1F21A { background-position: -340px -20px; } .emoji-1F22F { background-position: -340px -40px; } .emoji-1F232 { background-position: -340px -60px; } @@ -304,7 +304,7 @@ .emoji-1F300 { background-position: -340px -280px; } .emoji-1F301 { background-position: -340px -300px; } .emoji-1F302 { background-position: -340px -320px; } -.emoji-1F303 { background-position: 0px -340px; } +.emoji-1F303 { background-position: 0 -340px; } .emoji-1F304 { background-position: -20px -340px; } .emoji-1F305 { background-position: -40px -340px; } .emoji-1F306 { background-position: -60px -340px; } @@ -322,7 +322,7 @@ .emoji-1F312 { background-position: -300px -340px; } .emoji-1F313 { background-position: -320px -340px; } .emoji-1F314 { background-position: -340px -340px; } -.emoji-1F315 { background-position: -360px 0px; } +.emoji-1F315 { background-position: -360px 0; } .emoji-1F316 { background-position: -360px -20px; } .emoji-1F317 { background-position: -360px -40px; } .emoji-1F318 { background-position: -360px -60px; } @@ -340,7 +340,7 @@ .emoji-1F326 { background-position: -360px -300px; } .emoji-1F327 { background-position: -360px -320px; } .emoji-1F328 { background-position: -360px -340px; } -.emoji-1F329 { background-position: 0px -360px; } +.emoji-1F329 { background-position: 0 -360px; } .emoji-1F32A { background-position: -20px -360px; } .emoji-1F32B { background-position: -40px -360px; } .emoji-1F32C { background-position: -60px -360px; } @@ -359,7 +359,7 @@ .emoji-1F339 { background-position: -320px -360px; } .emoji-1F33A { background-position: -340px -360px; } .emoji-1F33B { background-position: -360px -360px; } -.emoji-1F33C { background-position: -380px 0px; } +.emoji-1F33C { background-position: -380px 0; } .emoji-1F33D { background-position: -380px -20px; } .emoji-1F33E { background-position: -380px -40px; } .emoji-1F33F { background-position: -380px -60px; } @@ -378,7 +378,7 @@ .emoji-1F34C { background-position: -380px -320px; } .emoji-1F34D { background-position: -380px -340px; } .emoji-1F34E { background-position: -380px -360px; } -.emoji-1F34F { background-position: 0px -380px; } +.emoji-1F34F { background-position: 0 -380px; } .emoji-1F350 { background-position: -20px -380px; } .emoji-1F351 { background-position: -40px -380px; } .emoji-1F352 { background-position: -60px -380px; } @@ -398,7 +398,7 @@ .emoji-1F360 { background-position: -340px -380px; } .emoji-1F361 { background-position: -360px -380px; } .emoji-1F362 { background-position: -380px -380px; } -.emoji-1F363 { background-position: -400px 0px; } +.emoji-1F363 { background-position: -400px 0; } .emoji-1F364 { background-position: -400px -20px; } .emoji-1F365 { background-position: -400px -40px; } .emoji-1F366 { background-position: -400px -60px; } @@ -418,7 +418,7 @@ .emoji-1F374 { background-position: -400px -340px; } .emoji-1F375 { background-position: -400px -360px; } .emoji-1F376 { background-position: -400px -380px; } -.emoji-1F377 { background-position: 0px -400px; } +.emoji-1F377 { background-position: 0 -400px; } .emoji-1F378 { background-position: -20px -400px; } .emoji-1F379 { background-position: -40px -400px; } .emoji-1F37A { background-position: -60px -400px; } @@ -439,7 +439,7 @@ .emoji-1F385-1F3FE { background-position: -360px -400px; } .emoji-1F385-1F3FF { background-position: -380px -400px; } .emoji-1F386 { background-position: -400px -400px; } -.emoji-1F387 { background-position: -420px 0px; } +.emoji-1F387 { background-position: -420px 0; } .emoji-1F388 { background-position: -420px -20px; } .emoji-1F389 { background-position: -420px -40px; } .emoji-1F38A { background-position: -420px -60px; } @@ -460,7 +460,7 @@ .emoji-1F399 { background-position: -420px -360px; } .emoji-1F39A { background-position: -420px -380px; } .emoji-1F39B { background-position: -420px -400px; } -.emoji-1F39C { background-position: 0px -420px; } +.emoji-1F39C { background-position: 0 -420px; } .emoji-1F39D { background-position: -20px -420px; } .emoji-1F39E { background-position: -40px -420px; } .emoji-1F39F { background-position: -60px -420px; } @@ -482,7 +482,7 @@ .emoji-1F3AF { background-position: -380px -420px; } .emoji-1F3B0 { background-position: -400px -420px; } .emoji-1F3B1 { background-position: -420px -420px; } -.emoji-1F3B2 { background-position: -440px 0px; } +.emoji-1F3B2 { background-position: -440px 0; } .emoji-1F3B3 { background-position: -440px -20px; } .emoji-1F3B4 { background-position: -440px -40px; } .emoji-1F3B5 { background-position: -440px -60px; } @@ -504,7 +504,7 @@ .emoji-1F3C3-1F3FC { background-position: -440px -380px; } .emoji-1F3C3-1F3FD { background-position: -440px -400px; } .emoji-1F3C3-1F3FE { background-position: -440px -420px; } -.emoji-1F3C3-1F3FF { background-position: 0px -440px; } +.emoji-1F3C3-1F3FF { background-position: 0 -440px; } .emoji-1F3C4 { background-position: -20px -440px; } .emoji-1F3C4-1F3FB { background-position: -40px -440px; } .emoji-1F3C4-1F3FC { background-position: -60px -440px; } @@ -527,7 +527,7 @@ .emoji-1F3CA-1F3FD { background-position: -400px -440px; } .emoji-1F3CA-1F3FE { background-position: -420px -440px; } .emoji-1F3CA-1F3FF { background-position: -440px -440px; } -.emoji-1F3CB { background-position: -460px 0px; } +.emoji-1F3CB { background-position: -460px 0; } .emoji-1F3CB-1F3FB { background-position: -460px -20px; } .emoji-1F3CB-1F3FC { background-position: -460px -40px; } .emoji-1F3CB-1F3FD { background-position: -460px -60px; } @@ -550,7 +550,7 @@ .emoji-1F3DA { background-position: -460px -400px; } .emoji-1F3DB { background-position: -460px -420px; } .emoji-1F3DC { background-position: -460px -440px; } -.emoji-1F3DD { background-position: 0px -460px; } +.emoji-1F3DD { background-position: 0 -460px; } .emoji-1F3DE { background-position: -20px -460px; } .emoji-1F3DF { background-position: -40px -460px; } .emoji-1F3E0 { background-position: -60px -460px; } @@ -574,7 +574,7 @@ .emoji-1F3F2 { background-position: -420px -460px; } .emoji-1F3F3 { background-position: -440px -460px; } .emoji-1F3F4 { background-position: -460px -460px; } -.emoji-1F3F5 { background-position: -480px 0px; } +.emoji-1F3F5 { background-position: -480px 0; } .emoji-1F3F6 { background-position: -480px -20px; } .emoji-1F3F7 { background-position: -480px -40px; } .emoji-1F3F8 { background-position: -480px -60px; } @@ -598,7 +598,7 @@ .emoji-1F40A { background-position: -480px -420px; } .emoji-1F40B { background-position: -480px -440px; } .emoji-1F40C { background-position: -480px -460px; } -.emoji-1F40D { background-position: 0px -480px; } +.emoji-1F40D { background-position: 0 -480px; } .emoji-1F40E { background-position: -20px -480px; } .emoji-1F40F { background-position: -40px -480px; } .emoji-1F410 { background-position: -60px -480px; } @@ -623,7 +623,7 @@ .emoji-1F423 { background-position: -440px -480px; } .emoji-1F424 { background-position: -460px -480px; } .emoji-1F425 { background-position: -480px -480px; } -.emoji-1F426 { background-position: -500px 0px; } +.emoji-1F426 { background-position: -500px 0; } .emoji-1F427 { background-position: -500px -20px; } .emoji-1F428 { background-position: -500px -40px; } .emoji-1F429 { background-position: -500px -60px; } @@ -648,7 +648,7 @@ .emoji-1F43C { background-position: -500px -440px; } .emoji-1F43D { background-position: -500px -460px; } .emoji-1F43E { background-position: -500px -480px; } -.emoji-1F43F { background-position: 0px -500px; } +.emoji-1F43F { background-position: 0 -500px; } .emoji-1F440 { background-position: -20px -500px; } .emoji-1F441 { background-position: -40px -500px; } .emoji-1F441-1F5E8 { background-position: -60px -500px; } @@ -674,7 +674,7 @@ .emoji-1F446-1F3FF { background-position: -460px -500px; } .emoji-1F447 { background-position: -480px -500px; } .emoji-1F447-1F3FB { background-position: -500px -500px; } -.emoji-1F447-1F3FC { background-position: -520px 0px; } +.emoji-1F447-1F3FC { background-position: -520px 0; } .emoji-1F447-1F3FD { background-position: -520px -20px; } .emoji-1F447-1F3FE { background-position: -520px -40px; } .emoji-1F447-1F3FF { background-position: -520px -60px; } @@ -700,7 +700,7 @@ .emoji-1F44B-1F3FB { background-position: -520px -460px; } .emoji-1F44B-1F3FC { background-position: -520px -480px; } .emoji-1F44B-1F3FD { background-position: -520px -500px; } -.emoji-1F44B-1F3FE { background-position: 0px -520px; } +.emoji-1F44B-1F3FE { background-position: 0 -520px; } .emoji-1F44B-1F3FF { background-position: -20px -520px; } .emoji-1F44C { background-position: -40px -520px; } .emoji-1F44C-1F3FB { background-position: -60px -520px; } @@ -727,7 +727,7 @@ .emoji-1F44F-1F3FE { background-position: -480px -520px; } .emoji-1F44F-1F3FF { background-position: -500px -520px; } .emoji-1F450 { background-position: -520px -520px; } -.emoji-1F450-1F3FB { background-position: -540px 0px; } +.emoji-1F450-1F3FB { background-position: -540px 0; } .emoji-1F450-1F3FC { background-position: -540px -20px; } .emoji-1F450-1F3FD { background-position: -540px -40px; } .emoji-1F450-1F3FE { background-position: -540px -60px; } @@ -754,7 +754,7 @@ .emoji-1F464 { background-position: -540px -480px; } .emoji-1F465 { background-position: -540px -500px; } .emoji-1F466 { background-position: -540px -520px; } -.emoji-1F466-1F3FB { background-position: 0px -540px; } +.emoji-1F466-1F3FB { background-position: 0 -540px; } .emoji-1F466-1F3FC { background-position: -20px -540px; } .emoji-1F466-1F3FD { background-position: -40px -540px; } .emoji-1F466-1F3FE { background-position: -60px -540px; } @@ -782,7 +782,7 @@ .emoji-1F468-1F469-1F467-1F467 { background-position: -500px -540px; } .emoji-1F468-2764-1F468 { background-position: -520px -540px; } .emoji-1F468-2764-1F48B-1F468 { background-position: -540px -540px; } -.emoji-1F469 { background-position: -560px 0px; } +.emoji-1F469 { background-position: -560px 0; } .emoji-1F469-1F3FB { background-position: -560px -20px; } .emoji-1F469-1F3FC { background-position: -560px -40px; } .emoji-1F469-1F3FD { background-position: -560px -60px; } @@ -810,7 +810,7 @@ .emoji-1F470-1F3FB { background-position: -560px -500px; } .emoji-1F470-1F3FC { background-position: -560px -520px; } .emoji-1F470-1F3FD { background-position: -560px -540px; } -.emoji-1F470-1F3FE { background-position: 0px -560px; } +.emoji-1F470-1F3FE { background-position: 0 -560px; } .emoji-1F470-1F3FF { background-position: -20px -560px; } .emoji-1F471 { background-position: -40px -560px; } .emoji-1F471-1F3FB { background-position: -60px -560px; } @@ -839,7 +839,7 @@ .emoji-1F475 { background-position: -520px -560px; } .emoji-1F475-1F3FB { background-position: -540px -560px; } .emoji-1F475-1F3FC { background-position: -560px -560px; } -.emoji-1F475-1F3FD { background-position: -580px 0px; } +.emoji-1F475-1F3FD { background-position: -580px 0; } .emoji-1F475-1F3FE { background-position: -580px -20px; } .emoji-1F475-1F3FF { background-position: -580px -40px; } .emoji-1F476 { background-position: -580px -60px; } @@ -868,7 +868,7 @@ .emoji-1F47C-1F3FC { background-position: -580px -520px; } .emoji-1F47C-1F3FD { background-position: -580px -540px; } .emoji-1F47C-1F3FE { background-position: -580px -560px; } -.emoji-1F47C-1F3FF { background-position: 0px -580px; } +.emoji-1F47C-1F3FF { background-position: 0 -580px; } .emoji-1F47D { background-position: -20px -580px; } .emoji-1F47E { background-position: -40px -580px; } .emoji-1F47F { background-position: -60px -580px; } @@ -898,7 +898,7 @@ .emoji-1F485-1F3FD { background-position: -540px -580px; } .emoji-1F485-1F3FE { background-position: -560px -580px; } .emoji-1F485-1F3FF { background-position: -580px -580px; } -.emoji-1F486 { background-position: -600px 0px; } +.emoji-1F486 { background-position: -600px 0; } .emoji-1F486-1F3FB { background-position: -600px -20px; } .emoji-1F486-1F3FC { background-position: -600px -40px; } .emoji-1F486-1F3FD { background-position: -600px -60px; } @@ -928,7 +928,7 @@ .emoji-1F497 { background-position: -600px -540px; } .emoji-1F498 { background-position: -600px -560px; } .emoji-1F499 { background-position: -600px -580px; } -.emoji-1F49A { background-position: 0px -600px; } +.emoji-1F49A { background-position: 0 -600px; } .emoji-1F49B { background-position: -20px -600px; } .emoji-1F49C { background-position: -40px -600px; } .emoji-1F49D { background-position: -60px -600px; } @@ -959,7 +959,7 @@ .emoji-1F4B1 { background-position: -560px -600px; } .emoji-1F4B2 { background-position: -580px -600px; } .emoji-1F4B3 { background-position: -600px -600px; } -.emoji-1F4B4 { background-position: -620px 0px; } +.emoji-1F4B4 { background-position: -620px 0; } .emoji-1F4B5 { background-position: -620px -20px; } .emoji-1F4B6 { background-position: -620px -40px; } .emoji-1F4B7 { background-position: -620px -60px; } @@ -990,7 +990,7 @@ .emoji-1F4D0 { background-position: -620px -560px; } .emoji-1F4D1 { background-position: -620px -580px; } .emoji-1F4D2 { background-position: -620px -600px; } -.emoji-1F4D3 { background-position: 0px -620px; } +.emoji-1F4D3 { background-position: 0 -620px; } .emoji-1F4D4 { background-position: -20px -620px; } .emoji-1F4D5 { background-position: -40px -620px; } .emoji-1F4D6 { background-position: -60px -620px; } @@ -1022,7 +1022,7 @@ .emoji-1F4F0 { background-position: -580px -620px; } .emoji-1F4F1 { background-position: -600px -620px; } .emoji-1F4F2 { background-position: -620px -620px; } -.emoji-1F4F3 { background-position: -640px 0px; } +.emoji-1F4F3 { background-position: -640px 0; } .emoji-1F4F4 { background-position: -640px -20px; } .emoji-1F4F5 { background-position: -640px -40px; } .emoji-1F4F6 { background-position: -640px -60px; } @@ -1054,7 +1054,7 @@ .emoji-1F510 { background-position: -640px -580px; } .emoji-1F511 { background-position: -640px -600px; } .emoji-1F512 { background-position: -640px -620px; } -.emoji-1F513 { background-position: 0px -640px; } +.emoji-1F513 { background-position: 0 -640px; } .emoji-1F514 { background-position: -20px -640px; } .emoji-1F515 { background-position: -40px -640px; } .emoji-1F516 { background-position: -60px -640px; } @@ -1087,7 +1087,7 @@ .emoji-1F531 { background-position: -600px -640px; } .emoji-1F532 { background-position: -620px -640px; } .emoji-1F533 { background-position: -640px -640px; } -.emoji-1F534 { background-position: -660px 0px; } +.emoji-1F534 { background-position: -660px 0; } .emoji-1F535 { background-position: -660px -20px; } .emoji-1F536 { background-position: -660px -40px; } .emoji-1F537 { background-position: -660px -60px; } @@ -1120,7 +1120,7 @@ .emoji-1F55B { background-position: -660px -600px; } .emoji-1F55C { background-position: -660px -620px; } .emoji-1F55D { background-position: -660px -640px; } -.emoji-1F55E { background-position: 0px -660px; } +.emoji-1F55E { background-position: 0 -660px; } .emoji-1F55F { background-position: -20px -660px; } .emoji-1F560 { background-position: -40px -660px; } .emoji-1F561 { background-position: -60px -660px; } @@ -1154,7 +1154,7 @@ .emoji-1F578 { background-position: -620px -660px; } .emoji-1F579 { background-position: -640px -660px; } .emoji-1F57B { background-position: -660px -660px; } -.emoji-1F57E { background-position: -680px 0px; } +.emoji-1F57E { background-position: -680px 0; } .emoji-1F57F { background-position: -680px -20px; } .emoji-1F581 { background-position: -680px -40px; } .emoji-1F582 { background-position: -680px -60px; } @@ -1188,7 +1188,7 @@ .emoji-1F595-1F3FF { background-position: -680px -620px; } .emoji-1F596 { background-position: -680px -640px; } .emoji-1F596-1F3FB { background-position: -680px -660px; } -.emoji-1F596-1F3FC { background-position: 0px -680px; } +.emoji-1F596-1F3FC { background-position: 0 -680px; } .emoji-1F596-1F3FD { background-position: -20px -680px; } .emoji-1F596-1F3FE { background-position: -40px -680px; } .emoji-1F596-1F3FF { background-position: -60px -680px; } @@ -1223,7 +1223,7 @@ .emoji-1F5C4 { background-position: -640px -680px; } .emoji-1F5C6 { background-position: -660px -680px; } .emoji-1F5C7 { background-position: -680px -680px; } -.emoji-1F5C9 { background-position: -700px 0px; } +.emoji-1F5C9 { background-position: -700px 0; } .emoji-1F5CA { background-position: -700px -20px; } .emoji-1F5CE { background-position: -700px -40px; } .emoji-1F5CF { background-position: -700px -60px; } @@ -1258,7 +1258,7 @@ .emoji-1F5F8 { background-position: -700px -640px; } .emoji-1F5F9 { background-position: -700px -660px; } .emoji-1F5FA { background-position: -700px -680px; } -.emoji-1F5FB { background-position: 0px -700px; } +.emoji-1F5FB { background-position: 0 -700px; } .emoji-1F5FC { background-position: -20px -700px; } .emoji-1F5FD { background-position: -40px -700px; } .emoji-1F5FE { background-position: -60px -700px; } @@ -1294,7 +1294,7 @@ .emoji-1F61C { background-position: -660px -700px; } .emoji-1F61D { background-position: -680px -700px; } .emoji-1F61E { background-position: -700px -700px; } -.emoji-1F61F { background-position: -720px 0px; } +.emoji-1F61F { background-position: -720px 0; } .emoji-1F620 { background-position: -720px -20px; } .emoji-1F621 { background-position: -720px -40px; } .emoji-1F622 { background-position: -720px -60px; } @@ -1330,7 +1330,7 @@ .emoji-1F640 { background-position: -720px -660px; } .emoji-1F641 { background-position: -720px -680px; } .emoji-1F642 { background-position: -720px -700px; } -.emoji-1F643 { background-position: 0px -720px; } +.emoji-1F643 { background-position: 0 -720px; } .emoji-1F644 { background-position: -20px -720px; } .emoji-1F645 { background-position: -40px -720px; } .emoji-1F645-1F3FB { background-position: -60px -720px; } @@ -1367,7 +1367,7 @@ .emoji-1F64C-1F3FF { background-position: -680px -720px; } .emoji-1F64D { background-position: -700px -720px; } .emoji-1F64D-1F3FB { background-position: -720px -720px; } -.emoji-1F64D-1F3FC { background-position: -740px 0px; } +.emoji-1F64D-1F3FC { background-position: -740px 0; } .emoji-1F64D-1F3FD { background-position: -740px -20px; } .emoji-1F64D-1F3FE { background-position: -740px -40px; } .emoji-1F64D-1F3FF { background-position: -740px -60px; } @@ -1404,7 +1404,7 @@ .emoji-1F692 { background-position: -740px -680px; } .emoji-1F693 { background-position: -740px -700px; } .emoji-1F694 { background-position: -740px -720px; } -.emoji-1F695 { background-position: 0px -740px; } +.emoji-1F695 { background-position: 0 -740px; } .emoji-1F696 { background-position: -20px -740px; } .emoji-1F697 { background-position: -40px -740px; } .emoji-1F698 { background-position: -60px -740px; } @@ -1442,7 +1442,7 @@ .emoji-1F6B3 { background-position: -700px -740px; } .emoji-1F6B4 { background-position: -720px -740px; } .emoji-1F6B4-1F3FB { background-position: -740px -740px; } -.emoji-1F6B4-1F3FC { background-position: -760px 0px; } +.emoji-1F6B4-1F3FC { background-position: -760px 0; } .emoji-1F6B4-1F3FD { background-position: -760px -20px; } .emoji-1F6B4-1F3FE { background-position: -760px -40px; } .emoji-1F6B4-1F3FF { background-position: -760px -60px; } @@ -1480,7 +1480,7 @@ .emoji-1F6C5 { background-position: -760px -700px; } .emoji-1F6C6 { background-position: -760px -720px; } .emoji-1F6C7 { background-position: -760px -740px; } -.emoji-1F6C8 { background-position: 0px -760px; } +.emoji-1F6C8 { background-position: 0 -760px; } .emoji-1F6C9 { background-position: -20px -760px; } .emoji-1F6CA { background-position: -40px -760px; } .emoji-1F6CB { background-position: -60px -760px; } @@ -1519,7 +1519,7 @@ .emoji-1F918-1F3FC { background-position: -720px -760px; } .emoji-1F918-1F3FD { background-position: -740px -760px; } .emoji-1F918-1F3FE { background-position: -760px -760px; } -.emoji-1F918-1F3FF { background-position: -780px 0px; } +.emoji-1F918-1F3FF { background-position: -780px 0; } .emoji-1F980 { background-position: -780px -20px; } .emoji-1F981 { background-position: -780px -40px; } .emoji-1F982 { background-position: -780px -60px; } @@ -1558,7 +1558,7 @@ .emoji-24C2 { background-position: -780px -720px; } .emoji-25AA { background-position: -780px -740px; } .emoji-25AB { background-position: -780px -760px; } -.emoji-25B6 { background-position: 0px -780px; } +.emoji-25B6 { background-position: 0 -780px; } .emoji-25C0 { background-position: -20px -780px; } .emoji-25FB { background-position: -40px -780px; } .emoji-25FC { background-position: -60px -780px; } @@ -1598,7 +1598,7 @@ .emoji-264D { background-position: -740px -780px; } .emoji-264E { background-position: -760px -780px; } .emoji-264F { background-position: -780px -780px; } -.emoji-2650 { background-position: -800px 0px; } +.emoji-2650 { background-position: -800px 0; } .emoji-2651 { background-position: -800px -20px; } .emoji-2652 { background-position: -800px -40px; } .emoji-2653 { background-position: -800px -60px; } @@ -1638,7 +1638,7 @@ .emoji-26F0 { background-position: -800px -740px; } .emoji-26F1 { background-position: -800px -760px; } .emoji-26F2 { background-position: -800px -780px; } -.emoji-26F3 { background-position: 0px -800px; } +.emoji-26F3 { background-position: 0 -800px; } .emoji-26F4 { background-position: -20px -800px; } .emoji-26F5 { background-position: -40px -800px; } .emoji-26F7 { background-position: -60px -800px; } @@ -1679,7 +1679,7 @@ .emoji-270D-1F3FD { background-position: -760px -800px; } .emoji-270D-1F3FE { background-position: -780px -800px; } .emoji-270D-1F3FF { background-position: -800px -800px; } -.emoji-270F { background-position: -820px 0px; } +.emoji-270F { background-position: -820px 0; } .emoji-2712 { background-position: -820px -20px; } .emoji-2714 { background-position: -820px -40px; } .emoji-2716 { background-position: -820px -60px; } diff --git a/app/assets/stylesheets/pages/events.scss b/app/assets/stylesheets/pages/events.scss index 35df9a61c86..84eefd01cfe 100644 --- a/app/assets/stylesheets/pages/events.scss +++ b/app/assets/stylesheets/pages/events.scss @@ -6,7 +6,7 @@ font-size: $gl-font-size; padding: $gl-padding-top 0 $gl-padding-top ($gl-avatar-size + $gl-padding-top); border-bottom: 1px solid $table-border-color; - color: #7f8fa4; + color: $list-text-color; &.event-inline { .avatar { @@ -21,7 +21,7 @@ } a { - color: #4c4e54; + color: $gl-dark-link-color; } .avatar { @@ -31,10 +31,7 @@ .event-title { @include str-truncated(calc(100% - 174px)); font-weight: 600; - - .author_name { - color: #333; - } + color: $list-text-color; } .event-body { @@ -63,7 +60,7 @@ .note-image-attach { margin-top: 4px; - margin-left: 0px; + margin-left: 0; max-width: 200px; float: none; } @@ -83,10 +80,10 @@ .event_icon { position: relative; float: right; - border: 1px solid #EEE; + border: 1px solid #eee; padding: 5px; @include border-radius(5px); - background: #F9F9F9; + background: #f9f9f9; margin-left: 10px; top: -6px; img { @@ -94,7 +91,7 @@ } } - &:last-child { border:none } + &:last-child { border: none } .event_commits { li { @@ -138,7 +135,7 @@ @include str-truncated(100%); padding: 5px 0; font-size: 13px; - float:left; + float: left; margin-right: -150px; padding-right: 150px; line-height: 20px; @@ -160,7 +157,7 @@ .event-body { margin: 0; - border-left: 2px solid #DDD; + border-left: 2px solid #ddd; padding-left: 10px; } diff --git a/app/assets/stylesheets/pages/graph.scss b/app/assets/stylesheets/pages/graph.scss index c3b10d144e1..4e5c4ed84b6 100644 --- a/app/assets/stylesheets/pages/graph.scss +++ b/app/assets/stylesheets/pages/graph.scss @@ -6,11 +6,11 @@ font-size: 14px; padding: 5px; border-bottom: 1px solid $border-color; - background: #EEE; + background: #eee; } .network-graph { - background: #FFF; + background: #fff; height: 500px; overflow-y: scroll; overflow-x: hidden; diff --git a/app/assets/stylesheets/pages/import.scss b/app/assets/stylesheets/pages/import.scss index 3df4bb84bd2..6a99cd9cb94 100644 --- a/app/assets/stylesheets/pages/import.scss +++ b/app/assets/stylesheets/pages/import.scss @@ -1,6 +1,6 @@ i.icon-gitorious { display: inline-block; - background-position: 0px 0px; + background-position: 0 0; background-size: contain; background-repeat: no-repeat; } diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss index 1310e6ad7c7..2760af8a48a 100644 --- a/app/assets/stylesheets/pages/issuable.scss +++ b/app/assets/stylesheets/pages/issuable.scss @@ -1,34 +1,3 @@ -@media (max-width: $screen-sm-max) { - .issuable-affix { - margin-top: 20px; - } -} - -@media (max-width: $screen-md-max) { - .issuable-affix { - position: static; - } -} - -@media (min-width: $screen-md-max) { - .issuable-affix { - &.affix-top { - position: static; - } - - &.affix { - position: fixed; - top: 70px; - margin-right: 35px; - - &.no-affix { - position: relative; - top: 0; - } - } - } -} - .issuable-details { section { .issuable-discussion { @@ -54,20 +23,25 @@ padding: 6px 10px; } } + + &.has-labels { + margin-bottom: -5px; + } } .issuable-sidebar { .block { @include clearfix; - padding: $gl-padding 0; + padding: $gl-padding 0; border-bottom: 1px solid $border-gray-light; // This prevents the mess when resizing the sidebar // of elements repositioning themselves.. width: $gutter_inner_width; // -- - &:first-child { - padding-top: 5px; + &.issuable-sidebar-header { + padding-top: 0; + padding-bottom: 10px; } &:last-child { @@ -75,7 +49,6 @@ } span { - margin-top: 7px; display: inline-block; } @@ -84,7 +57,7 @@ } .issuable-count { - + margin-top: 7px; } .gutter-toggle { @@ -99,19 +72,19 @@ .title { color: $gl-text-color; - margin-bottom: 8px; + margin-bottom: 10px; + line-height: 1; .avatar { margin-left: 0; } - label { - font-weight: normal; - margin-right: 4px; - } - .edit-link { color: $gl-gray; + + &:hover { + color: $md-link-color; + } } } @@ -144,11 +117,6 @@ .btn-clipboard { color: $gl-gray; } - - .participants .avatar { - margin-top: 6px; - margin-right: 2px; - } } .right-sidebar { @@ -163,8 +131,12 @@ &.right-sidebar-expanded { width: $gutter_width; - hr { - display: none; + .value { + line-height: 1; + } + + .bold { + font-weight: 600; } .sidebar-collapsed-icon { @@ -172,8 +144,23 @@ } .gutter-toggle { + margin-top: 7px; border-left: 1px solid $border-gray-light; } + + .assignee .avatar { + float: left; + margin-right: 10px; + margin-bottom: 0; + margin-left: 0; + } + + .username { + display: block; + margin-top: 4px; + font-size: 13px; + font-weight: normal; + } } .subscribe-button { @@ -193,14 +180,6 @@ width: $sidebar_collapsed_width; padding-top: 0; - hr { - margin: 0; - color: $gray-normal; - border-color: $gray-normal; - width: 62px; - margin-left: -20px - } - .block { width: $sidebar_collapsed_width - 1px; margin-left: -19px; @@ -209,12 +188,18 @@ overflow: hidden; } + .participants { + border-bottom: 1px solid $border-gray-light; + } + .hide-collapsed { display: none; } .gutter-toggle { - margin-left: -36px; + width: 100%; + margin-left: 0; + padding-left: 25px; } .sidebar-collapsed-icon { @@ -222,13 +207,17 @@ width: 100%; text-align: center; padding-bottom: 10px; - color: #999999; + color: #999; span { display: block; margin-top: 0; } + .author { + display: none; + } + .btn-clipboard { border: none; @@ -237,10 +226,15 @@ } i { - color: #999999; + color: #999; } } } + + .sidebar-collapsed-user { + padding-bottom: 0; + margin-bottom: 10px; + } } .btn { @@ -251,6 +245,13 @@ border: 1px solid $border-gray-dark; } } + + a:not(.btn) { + &:hover { + color: $md-link-color; + text-decoration: none; + } + } } .btn-default.gutter-toggle { @@ -262,3 +263,37 @@ color: $gray-darkest; } } + +.edited-text { + color: $gray-darkest; + + .author_link { + color: $gray-darkest; + } +} + +.participants-list { + margin: -5px -5px; +} + +.participants-author { + display: inline-block; + padding: 5px 5px; + + .author_link { + display: block; + } + + .avatar.avatar-inline { + margin: 0; + } +} + +.participants-more { + margin-top: 5px; + margin-left: 5px; + + a { + color: #8c8c8c; + } +} diff --git a/app/assets/stylesheets/pages/issues.scss b/app/assets/stylesheets/pages/issues.scss index 1b686c58eaf..6a1d28590c2 100644 --- a/app/assets/stylesheets/pages/issues.scss +++ b/app/assets/stylesheets/pages/issues.scss @@ -3,7 +3,7 @@ padding: 10px $gl-padding; position: relative; - .issue-title { + .title { margin-bottom: 2px; } @@ -49,7 +49,7 @@ form.edit-issue { margin: 0; } -.merge-requests-title { +.merge-requests-title, .related-branches-title { font-size: 16px; font-weight: 600; } @@ -68,18 +68,18 @@ form.edit-issue { .merge-request, .issue { &.today { - background: #EFE; - border-color: #CEC; + background: #efe; + border-color: #cec; } &.closed { - background: #F9F9F9; - border-color: #E5E5E5; + background: #f9f9f9; + border-color: #e5e5e5; } &.merged { - background: #F9F9F9; - border-color: #E5E5E5; + background: #f9f9f9; + border-color: #e5e5e5; } } @@ -130,14 +130,14 @@ form.edit-issue { } .issue-closed-by-widget { - color: $secondary-text; + color: $gl-text-color; margin-left: 52px; } .editor-details { display: block; - + @media (min-width: $screen-sm-min) { display: inline-block; } -}
\ No newline at end of file +} diff --git a/app/assets/stylesheets/pages/login.scss b/app/assets/stylesheets/pages/login.scss index 61bec02f6c5..bc41f7d306f 100644 --- a/app/assets/stylesheets/pages/login.scss +++ b/app/assets/stylesheets/pages/login.scss @@ -28,7 +28,7 @@ img { max-width: 100%; - margin-bottom: 30px; + margin-bottom: 30px; } a { @@ -39,7 +39,7 @@ .login-box{ background: #fafafa; border-radius: 10px; - box-shadow: 0 0px 2px #CCC; + box-shadow: 0 0 2px #ccc; padding: 15px; .login-heading h3 { @@ -74,7 +74,7 @@ &.top { @include border-radius(5px 5px 0 0); - margin-bottom: 0px; + margin-bottom: 0; } &.bottom { @@ -85,12 +85,12 @@ &.middle { border-top: 0; - margin-bottom:0px; + margin-bottom: 0; @include border-radius(0); } &:active, &:focus { - background-color: #FFF; + background-color: #fff; } } diff --git a/app/assets/stylesheets/pages/merge_requests.scss b/app/assets/stylesheets/pages/merge_requests.scss index 2772623f4bd..cee5c47cfb2 100644 --- a/app/assets/stylesheets/pages/merge_requests.scss +++ b/app/assets/stylesheets/pages/merge_requests.scss @@ -113,7 +113,7 @@ } .mr-widget-footer { - border-top: 1px solid #EEE; + border-top: 1px solid #eee; } .ci-coverage { @@ -222,7 +222,7 @@ margin-bottom: 20px; span { - color: #B2B2B2; + color: #b2b2b2; a { color: $md-link-color; diff --git a/app/assets/stylesheets/pages/note_form.scss b/app/assets/stylesheets/pages/note_form.scss index 158c2a47862..61783ec46aa 100644 --- a/app/assets/stylesheets/pages/note_form.scss +++ b/app/assets/stylesheets/pages/note_form.scss @@ -156,7 +156,7 @@ .comment-hints { color: #999; - background: #FFF; + background: #fff; padding: 7px; margin-top: -7px; border: 1px solid $border-color; diff --git a/app/assets/stylesheets/pages/notes.scss b/app/assets/stylesheets/pages/notes.scss index d5f9852ebed..d408853cc80 100644 --- a/app/assets/stylesheets/pages/notes.scss +++ b/app/assets/stylesheets/pages/notes.scss @@ -3,16 +3,16 @@ */ @-webkit-keyframes targe3-note { - from { background:#fffff0; } - 50% { background:#ffffd3; } - to { background:#fffff0; } + from { background: #fffff0; } + 50% { background: #ffffd3; } + to { background: #fffff0; } } ul.notes { display: block; list-style: none; - margin: 0px; - padding: 0px; + margin: 0; + padding: 0; .timeline-icon { float: left; @@ -30,7 +30,7 @@ ul.notes { font-size: 14px; padding-top: 10px; padding-bottom: 10px; - background: #FDFDFD; + background: #fdfdfd; .timeline-icon { .avatar { @@ -93,12 +93,12 @@ ul.notes { .discussion { overflow: hidden; display: block; - position:relative; + position: relative; } .note { display: block; - position:relative; + position: relative; .note-body { overflow: auto; @@ -108,6 +108,13 @@ ul.notes { word-wrap: break-word; @include md-typography; + // On diffs code should wrap nicely and not overflow + pre { + code { + white-space: pre-wrap; + } + } + // Reset ul style types since we're nested inside a ul already & > ul { list-style-type: disc; @@ -129,7 +136,7 @@ ul.notes { hr { // Darken 'whitesmoke' a bit to make it more visible in note bodies - border-color: darken(#F5F5F5, 8%); + border-color: darken(#f5f5f5, 8%); margin: 10px 0; } } @@ -166,7 +173,7 @@ ul.notes { vertical-align: middle; text-align: center; padding: 10px 0; - background: #FFF; + background: #fff; color: $text-color; } &.notes_line2 { @@ -232,7 +239,7 @@ ul.notes { .add-diff-note { margin-top: -4px; @include border-radius(40px); - background: #FFF; + background: #fff; padding: 4px; font-size: 16px; color: $gl-link-color; @@ -249,7 +256,7 @@ ul.notes { &:hover { background: $gl-info; - color: #FFF; + color: #fff; @include show-add-diff-note; } } diff --git a/app/assets/stylesheets/pages/profile.scss b/app/assets/stylesheets/pages/profile.scss index ecfe0e37c85..260179074cf 100644 --- a/app/assets/stylesheets/pages/profile.scss +++ b/app/assets/stylesheets/pages/profile.scss @@ -30,7 +30,7 @@ .btn { line-height: 40px; height: 42px; - padding: 0px 12px; + padding: 0 12px; img { width: 32px; diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss index 1a7b0c1e278..250de079ff5 100644 --- a/app/assets/stylesheets/pages/projects.scss +++ b/app/assets/stylesheets/pages/projects.scss @@ -33,6 +33,13 @@ .project-settings-dropdown { margin-left: 10px; display: inline-block; + + .dropdown-menu { + left: auto; + width: auto; + right: 0px; + max-width: 240px; + } } } @@ -63,7 +70,7 @@ .project-repo-buttons { margin-top: 20px; - margin-bottom: 0px; + margin-bottom: 0; .count-buttons { display: block; @@ -118,7 +125,7 @@ left: 1px; margin-top: -9px; border-width: 10px 7px 10px 0; - border-right-color: #FFF; + border-right-color: #fff; } } .count { @@ -140,10 +147,10 @@ cursor: pointer; background-image: none; white-space: nowrap; - margin: 0 11px 0px 4px; + margin: 0 11px 0 4px; &:hover { - background: #FFF; + background: #fff; } } } @@ -188,7 +195,7 @@ } .project_member_row form { - margin: 0px; + margin: 0; } .transfer-project .select2-container { @@ -264,11 +271,11 @@ table.table.protected-branches-list tr.no-border { padding-bottom: 4px; ul.nav { - display:inline-block; + display: inline-block; } .nav li { - display:inline; + display: inline; } .nav > li > a { @@ -281,11 +288,11 @@ table.table.protected-branches-list tr.no-border { } li { - display:inline; + display: inline; } a { - float:left; + float: left; font-size: 17px; } @@ -440,7 +447,7 @@ pre.light-well { .form-control { @extend .monospace; - background: #FFF; + background: #fff; font-size: 14px; margin-left: -1px; cursor: auto; @@ -450,16 +457,16 @@ pre.light-well { .cannot-be-merged, .cannot-be-merged:hover { - color: #E62958; + color: #e62958; margin-top: 2px; } .private-forks-notice .private-fork-icon { i:nth-child(1) { - color: #2AA056; + color: #2aa056; } i:nth-child(2) { - color: #FFFFFF; + color: #fff; } } diff --git a/app/assets/stylesheets/pages/runners.scss b/app/assets/stylesheets/pages/runners.scss index a9111a7388f..eec22c5dc96 100644 --- a/app/assets/stylesheets/pages/runners.scss +++ b/app/assets/stylesheets/pages/runners.scss @@ -1,7 +1,7 @@ .runner-state { padding: 6px 12px; margin-right: 10px; - color: #FFF; + color: #fff; &.runner-state-shared { background: #32b186; diff --git a/app/assets/stylesheets/pages/search.scss b/app/assets/stylesheets/pages/search.scss index 84234b15c65..b6e45024644 100644 --- a/app/assets/stylesheets/pages/search.scss +++ b/app/assets/stylesheets/pages/search.scss @@ -16,7 +16,7 @@ margin-bottom: 20px; input { - border-color: #BBB; + border-color: #bbb; font-weight: bold; } } diff --git a/app/assets/stylesheets/pages/sherlock.scss b/app/assets/stylesheets/pages/sherlock.scss index 92d84d9640f..bed6470dbd3 100644 --- a/app/assets/stylesheets/pages/sherlock.scss +++ b/app/assets/stylesheets/pages/sherlock.scss @@ -13,13 +13,13 @@ table .sherlock-code { } .sherlock-line-samples-table { - margin-bottom: 0px !important; + margin-bottom: 0 !important; thead tr th, tbody tr td { font-size: 13px !important; text-align: right; - padding: 0px 10px !important; + padding: 0 10px !important; } } diff --git a/app/assets/stylesheets/pages/status.scss b/app/assets/stylesheets/pages/status.scss index 4b6ef035673..6f777d11641 100644 --- a/app/assets/stylesheets/pages/status.scss +++ b/app/assets/stylesheets/pages/status.scss @@ -1,7 +1,7 @@ .ci-status { padding: 2px 7px; margin-right: 5px; - border: 1px solid #EEE; + border: 1px solid #eee; white-space: nowrap; @include border-radius(4px); diff --git a/app/assets/stylesheets/pages/todos.scss b/app/assets/stylesheets/pages/todos.scss index 479c3c16d46..f983e9829e6 100644 --- a/app/assets/stylesheets/pages/todos.scss +++ b/app/assets/stylesheets/pages/todos.scss @@ -14,25 +14,8 @@ } .todo-item { - font-size: $gl-font-size; - padding-left: $gl-avatar-size + $gl-padding-top; - color: $secondary-text; - - a { - color: #4c4e54; - } - - .avatar { - margin-left: -($gl-avatar-size + $gl-padding-top); - } - .todo-title { @include str-truncated(calc(100% - 174px)); - font-weight: 600; - - .author-name { - color: #333; - } } .todo-body { @@ -61,7 +44,7 @@ .note-image-attach { margin-top: 4px; - margin-left: 0px; + margin-left: 0; max-width: 200px; float: none; } @@ -89,7 +72,7 @@ .todo-body { margin: 0; - border-left: 2px solid #DDD; + border-left: 2px solid #ddd; padding-left: 10px; } } diff --git a/app/assets/stylesheets/pages/tree.scss b/app/assets/stylesheets/pages/tree.scss index ef63b010600..25b5e95583e 100644 --- a/app/assets/stylesheets/pages/tree.scss +++ b/app/assets/stylesheets/pages/tree.scss @@ -41,12 +41,12 @@ vertical-align: middle; i, a { - color: $gl-link-color; + color: $gl-dark-link-color; } img { position: relative; - top:-1px; + top: -1px; } } diff --git a/app/assets/stylesheets/pages/ui_dev_kit.scss b/app/assets/stylesheets/pages/ui_dev_kit.scss index 05fa9312efb..587bd6a1e8a 100644 --- a/app/assets/stylesheets/pages/ui_dev_kit.scss +++ b/app/assets/stylesheets/pages/ui_dev_kit.scss @@ -7,7 +7,7 @@ .example { &:before { content: "Example"; - color: #BBB; + color: #bbb; } padding: 15px; diff --git a/app/assets/stylesheets/pages/xterm.scss b/app/assets/stylesheets/pages/xterm.scss index 9a50096c0d0..8886c1dff56 100644 --- a/app/assets/stylesheets/pages/xterm.scss +++ b/app/assets/stylesheets/pages/xterm.scss @@ -2,23 +2,23 @@ // color codes are based on http://en.wikipedia.org/wiki/File:Xterm_256color_chart.svg // see also: https://gist.github.com/jasonm23/2868981 - $black: #000000; + $black: #000; $red: #cd0000; $green: #00cd00; $yellow: #cdcd00; - $blue: #0000ee; // according to wikipedia, this is the xterm standard + $blue: #00e; // according to wikipedia, this is the xterm standard //$blue: #1e90ff; // this is used by all the terminals I tried (when configured with the xterm color profile) $magenta: #cd00cd; $cyan: #00cdcd; $white: #e5e5e5; $l-black: #7f7f7f; - $l-red: #ff0000; - $l-green: #00ff00; - $l-yellow: #ffff00; + $l-red: #f00; + $l-green: #0f0; + $l-yellow: #ff0; $l-blue: #5c5cff; - $l-magenta: #ff00ff; - $l-cyan: #00ffff; - $l-white: #ffffff; + $l-magenta: #f0f; + $l-cyan: #0ff; + $l-white: #fff; .term-bold { font-weight: bold; @@ -136,7 +136,7 @@ .xterm-fg-0 { - color: #000000; + color: #000; } .xterm-fg-1 { color: #800000; @@ -163,28 +163,28 @@ color: #808080; } .xterm-fg-9 { - color: #ff0000; + color: #f00; } .xterm-fg-10 { - color: #00ff00; + color: #0f0; } .xterm-fg-11 { - color: #ffff00; + color: #ff0; } .xterm-fg-12 { - color: #0000ff; + color: #00f; } .xterm-fg-13 { - color: #ff00ff; + color: #f0f; } .xterm-fg-14 { - color: #00ffff; + color: #0ff; } .xterm-fg-15 { - color: #ffffff; + color: #fff; } .xterm-fg-16 { - color: #000000; + color: #000; } .xterm-fg-17 { color: #00005f; @@ -199,7 +199,7 @@ color: #0000d7; } .xterm-fg-21 { - color: #0000ff; + color: #00f; } .xterm-fg-22 { color: #005f00; @@ -274,7 +274,7 @@ color: #00d7ff; } .xterm-fg-46 { - color: #00ff00; + color: #0f0; } .xterm-fg-47 { color: #00ff5f; @@ -289,7 +289,7 @@ color: #00ffd7; } .xterm-fg-51 { - color: #00ffff; + color: #0ff; } .xterm-fg-52 { color: #5f0000; @@ -724,7 +724,7 @@ color: #d7ffff; } .xterm-fg-196 { - color: #ff0000; + color: #f00; } .xterm-fg-197 { color: #ff005f; @@ -739,7 +739,7 @@ color: #ff00d7; } .xterm-fg-201 { - color: #ff00ff; + color: #f0f; } .xterm-fg-202 { color: #ff5f00; @@ -814,7 +814,7 @@ color: #ffd7ff; } .xterm-fg-226 { - color: #ffff00; + color: #ff0; } .xterm-fg-227 { color: #ffff5f; @@ -829,7 +829,7 @@ color: #ffffd7; } .xterm-fg-231 { - color: #ffffff; + color: #fff; } .xterm-fg-232 { color: #080808; @@ -850,7 +850,7 @@ color: #3a3a3a; } .xterm-fg-238 { - color: #444444; + color: #444; } .xterm-fg-239 { color: #4e4e4e; @@ -901,6 +901,6 @@ color: #e4e4e4; } .xterm-fg-255 { - color: #eeeeee; + color: #eee; } } diff --git a/app/controllers/admin/abuse_reports_controller.rb b/app/controllers/admin/abuse_reports_controller.rb index 2463cfa87be..e9b0972bdd8 100644 --- a/app/controllers/admin/abuse_reports_controller.rb +++ b/app/controllers/admin/abuse_reports_controller.rb @@ -6,7 +6,7 @@ class Admin::AbuseReportsController < Admin::ApplicationController def destroy abuse_report = AbuseReport.find(params[:id]) - abuse_report.remove_user if params[:remove_user] + abuse_report.remove_user(deleted_by: current_user) if params[:remove_user] abuse_report.destroy render nothing: true diff --git a/app/controllers/admin/users_controller.rb b/app/controllers/admin/users_controller.rb index be192964a93..9abf08d0e19 100644 --- a/app/controllers/admin/users_controller.rb +++ b/app/controllers/admin/users_controller.rb @@ -119,10 +119,10 @@ class Admin::UsersController < Admin::ApplicationController end def destroy - DeleteUserService.new(current_user).execute(user) + DeleteUserWorker.perform_async(current_user.id, user.id) respond_to do |format| - format.html { redirect_to admin_users_path } + format.html { redirect_to admin_users_path, notice: "The user is being deleted." } format.json { head :ok } end end diff --git a/app/controllers/dashboard/todos_controller.rb b/app/controllers/dashboard/todos_controller.rb index 43cf8fa71af..be488483b09 100644 --- a/app/controllers/dashboard/todos_controller.rb +++ b/app/controllers/dashboard/todos_controller.rb @@ -1,25 +1,34 @@ class Dashboard::TodosController < Dashboard::ApplicationController - before_action :find_todos, only: [:index, :destroy_all] + before_action :find_todos, only: [:index, :destroy, :destroy_all] def index @todos = @todos.page(params[:page]).per(PER_PAGE) end def destroy - todo.done! + todo.done + + todo_notice = 'Todo was successfully marked as done.' respond_to do |format| - format.html { redirect_to dashboard_todos_path, notice: 'Todo was successfully marked as done.' } + format.html { redirect_to dashboard_todos_path, notice: todo_notice } format.js { render nothing: true } + format.json do + render json: { count: @todos.size, done_count: current_user.todos.done.count } + end end end def destroy_all - @todos.each(&:done!) + @todos.each(&:done) respond_to do |format| format.html { redirect_to dashboard_todos_path, notice: 'All todos were marked as done.' } format.js { render nothing: true } + format.json do + find_todos + render json: { count: @todos.size, done_count: current_user.todos.done.count } + end end end diff --git a/app/controllers/dashboard_controller.rb b/app/controllers/dashboard_controller.rb index 139e40db180..b538c7d1608 100644 --- a/app/controllers/dashboard_controller.rb +++ b/app/controllers/dashboard_controller.rb @@ -3,7 +3,7 @@ class DashboardController < Dashboard::ApplicationController include MergeRequestsAction before_action :event_filter, only: :activity - before_action :projects, only: [:issues, :merge_requests] + before_action :projects, only: [:issues, :merge_requests, :labels, :milestones] respond_to :html @@ -20,6 +20,29 @@ class DashboardController < Dashboard::ApplicationController end end + def labels + labels = Label.where(project_id: @projects).select(:title, :color).uniq(:title) + + respond_to do |format| + format.json do + render json: labels + end + end + end + + def milestones + milestones = Milestone.where(project_id: @projects).active + epoch = DateTime.parse('1970-01-01') + grouped_milestones = GlobalMilestone.build_collection(milestones) + grouped_milestones = grouped_milestones.sort_by { |x| x.due_date.nil? ? epoch : x.due_date } + + respond_to do |format| + format.json do + render json: grouped_milestones + end + end + end + protected def load_events diff --git a/app/controllers/oauth/authorizations_controller.rb b/app/controllers/oauth/authorizations_controller.rb index 24025d8c723..c721dca58d9 100644 --- a/app/controllers/oauth/authorizations_controller.rb +++ b/app/controllers/oauth/authorizations_controller.rb @@ -7,6 +7,7 @@ class Oauth::AuthorizationsController < Doorkeeper::AuthorizationsController if pre_auth.authorizable? if skip_authorization? || matching_token? auth = authorization.authorize + session.delete(:user_return_to) redirect_to auth.redirect_uri else render "doorkeeper/authorizations/new" diff --git a/app/controllers/projects/badges_controller.rb b/app/controllers/projects/badges_controller.rb index dc9c96df003..6ff47c4033a 100644 --- a/app/controllers/projects/badges_controller.rb +++ b/app/controllers/projects/badges_controller.rb @@ -1,5 +1,5 @@ class Projects::BadgesController < Projects::ApplicationController - before_action :set_no_cache + before_action :no_cache_headers def build respond_to do |format| @@ -10,15 +10,4 @@ class Projects::BadgesController < Projects::ApplicationController end end end - - private - - def set_no_cache - expires_now - - # Add some deprecated headers for older agents - # - response.headers['Pragma'] = 'no-cache' - response.headers['Expires'] = 'Fri, 01 Jan 1990 00:00:00 GMT' - end end diff --git a/app/controllers/projects/branches_controller.rb b/app/controllers/projects/branches_controller.rb index 4db3b3bf23d..43ea717cbd2 100644 --- a/app/controllers/projects/branches_controller.rb +++ b/app/controllers/projects/branches_controller.rb @@ -9,7 +9,7 @@ class Projects::BranchesController < Projects::ApplicationController @sort = params[:sort] || 'name' @branches = @repository.branches_sorted_by(@sort) @branches = Kaminari.paginate_array(@branches).page(params[:page]).per(PER_PAGE) - + @max_commits = @branches.reduce(0) do |memo, branch| diverging_commit_counts = repository.diverging_commit_counts(branch) [memo, diverging_commit_counts[:behind], diverging_commit_counts[:ahead]].max @@ -23,11 +23,15 @@ class Projects::BranchesController < Projects::ApplicationController def create branch_name = sanitize(strip_tags(params[:branch_name])) branch_name = Addressable::URI.unescape(branch_name) - ref = sanitize(strip_tags(params[:ref])) - ref = Addressable::URI.unescape(ref) + result = CreateBranchService.new(project, current_user). execute(branch_name, ref) + if params[:issue_iid] + issue = @project.issues.find_by(iid: params[:issue_iid]) + SystemNoteService.new_issue_branch(issue, @project, current_user, branch_name) if issue + end + if result[:status] == :success @branch = result[:branch] redirect_to namespace_project_tree_path(@project.namespace, @project, @@ -49,4 +53,15 @@ class Projects::BranchesController < Projects::ApplicationController format.js { render status: status[:return_code] } end end + + private + + def ref + if params[:ref] + ref_escaped = sanitize(strip_tags(params[:ref])) + Addressable::URI.unescape(ref_escaped) + else + @project.default_branch + end + end end diff --git a/app/controllers/projects/issues_controller.rb b/app/controllers/projects/issues_controller.rb index b0a03ee45cc..6603f28a082 100644 --- a/app/controllers/projects/issues_controller.rb +++ b/app/controllers/projects/issues_controller.rb @@ -5,7 +5,7 @@ class Projects::IssuesController < Projects::ApplicationController before_action :issue, only: [:edit, :update, :show] # Allow read any issue - before_action :authorize_read_issue! + before_action :authorize_read_issue!, only: [:show] # Allow write(create) issue before_action :authorize_create_issue!, only: [:new, :create] @@ -65,6 +65,7 @@ class Projects::IssuesController < Projects::ApplicationController @notes = @issue.notes.nonawards.with_associations.fresh @noteable = @issue @merge_requests = @issue.referenced_merge_requests(current_user) + @related_branches = @issue.related_branches - @merge_requests.map(&:source_branch) respond_with(@issue) end @@ -127,6 +128,10 @@ class Projects::IssuesController < Projects::ApplicationController end alias_method :subscribable_resource, :issue + def authorize_read_issue! + return render_404 unless can?(current_user, :read_issue, @issue) + end + def authorize_update_issue! return render_404 unless can?(current_user, :update_issue, @issue) end @@ -157,7 +162,7 @@ class Projects::IssuesController < Projects::ApplicationController def issue_params params.require(:issue).permit( - :title, :assignee_id, :position, :description, + :title, :assignee_id, :position, :description, :confidential, :milestone_id, :state_event, :task_num, label_ids: [] ) end diff --git a/app/controllers/projects/labels_controller.rb b/app/controllers/projects/labels_controller.rb index 40d8098690a..5f471d405f5 100644 --- a/app/controllers/projects/labels_controller.rb +++ b/app/controllers/projects/labels_controller.rb @@ -12,6 +12,13 @@ class Projects::LabelsController < Projects::ApplicationController def index @labels = @project.labels.page(params[:page]).per(PER_PAGE) + + respond_to do |format| + format.html + format.json do + render json: @project.labels + end + end end def new diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb index 61b82c9db46..7248ede1699 100644 --- a/app/controllers/projects/merge_requests_controller.rb +++ b/app/controllers/projects/merge_requests_controller.rb @@ -5,7 +5,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController before_action :module_enabled before_action :merge_request, only: [ :edit, :update, :show, :diffs, :commits, :builds, :merge, :merge_check, - :ci_status, :cancel_merge_when_build_succeeds + :ci_status, :toggle_subscription, :cancel_merge_when_build_succeeds, :remove_wip ] before_action :closes_issues, only: [:edit, :update, :show, :diffs, :commits, :builds] before_action :validates_merge_request, only: [:show, :diffs, :commits, :builds] @@ -20,7 +20,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController before_action :authorize_create_merge_request!, only: [:new, :create] # Allow modify merge_request - before_action :authorize_update_merge_request!, only: [:close, :edit, :update, :sort] + before_action :authorize_update_merge_request!, only: [:close, :edit, :update, :remove_wip, :sort] def index terms = params['issue_search'] @@ -164,6 +164,13 @@ class Projects::MergeRequestsController < Projects::ApplicationController end end + def remove_wip + MergeRequests::UpdateService.new(project, current_user, title: @merge_request.wipless_title).execute(@merge_request) + + redirect_to namespace_project_merge_request_path(@project.namespace, @project, @merge_request), + notice: "The merge request can now be merged." + end + def merge_check @merge_request.check_if_can_be_merged diff --git a/app/controllers/projects/milestones_controller.rb b/app/controllers/projects/milestones_controller.rb index da46731d945..0998b191c07 100644 --- a/app/controllers/projects/milestones_controller.rb +++ b/app/controllers/projects/milestones_controller.rb @@ -19,7 +19,15 @@ class Projects::MilestonesController < Projects::ApplicationController end @milestones = @milestones.includes(:project) - @milestones = @milestones.page(params[:page]).per(PER_PAGE) + + respond_to do |format| + format.html do + @milestones = @milestones.page(params[:page]).per(PER_PAGE) + end + format.json do + render json: @milestones + end + end end def new diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index 36f37221c58..c9930480770 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -134,7 +134,7 @@ class ProjectsController < ApplicationController def autocomplete_sources note_type = params['type'] note_id = params['type_id'] - autocomplete = ::Projects::AutocompleteService.new(@project) + autocomplete = ::Projects::AutocompleteService.new(@project, current_user) participants = ::Projects::ParticipantsService.new(@project, current_user).execute(note_type, note_id) @suggestions = { diff --git a/app/finders/issues_finder.rb b/app/finders/issues_finder.rb index 20a2b0ce8f0..c2befa5a5b3 100644 --- a/app/finders/issues_finder.rb +++ b/app/finders/issues_finder.rb @@ -19,4 +19,10 @@ class IssuesFinder < IssuableFinder def klass Issue end + + private + + def init_collection + Issue.visible_to_user(current_user) + end end diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index d1b1c61b710..e6ceb213532 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -182,7 +182,7 @@ module ApplicationHelper # Returns an HTML-safe String def time_ago_with_tooltip(time, placement: 'top', html_class: 'time_ago', skip_js: false) element = content_tag :time, time.to_s, - class: "#{html_class} js-timeago js-timeago-pending", + class: "#{html_class} js-timeago #{"js-timeago-pending" unless skip_js}", datetime: time.to_time.getutc.iso8601, title: time.in_time_zone.to_s(:medium), data: { toggle: 'tooltip', placement: placement, container: 'body' } @@ -196,6 +196,22 @@ module ApplicationHelper element end + def edited_time_ago_with_tooltip(object, placement: 'top', html_class: 'time_ago', include_author: false) + return if object.updated_at == object.created_at + + content_tag :small, class: "edited-text" do + output = content_tag(:span, "Edited ") + output << time_ago_with_tooltip(object.updated_at, placement: placement, html_class: html_class) + + if include_author && object.updated_by && object.updated_by != object.author + output << content_tag(:span, " by ") + output << link_to_member(object.project, object.updated_by, avatar: false, author_class: nil) + end + + output + end + end + def render_markup(file_name, file_content) if gitlab_markdown?(file_name) Haml::Helpers.preserve(markdown(file_content)) @@ -285,7 +301,7 @@ module ApplicationHelper if project.nil? nil elsif current_controller?(:issues) - project.issues.send(entity).count + project.issues.visible_to_user(current_user).send(entity).count elsif current_controller?(:merge_requests) project.merge_requests.send(entity).count end diff --git a/app/helpers/dropdowns_helper.rb b/app/helpers/dropdowns_helper.rb index 74f326e0b83..ceff1fbb161 100644 --- a/app/helpers/dropdowns_helper.rb +++ b/app/helpers/dropdowns_helper.rb @@ -24,7 +24,7 @@ module DropdownsHelper capture(&block) if block && !options.has_key?(:footer_content) end - if block && options.has_key?(:footer_content) + if block && options[:footer_content] output << content_tag(:div, class: "dropdown-footer") do capture(&block) end diff --git a/app/helpers/events_helper.rb b/app/helpers/events_helper.rb index 37a888d9c60..a67a6b208e2 100644 --- a/app/helpers/events_helper.rb +++ b/app/helpers/events_helper.rb @@ -194,7 +194,7 @@ module EventsHelper end def event_to_atom(xml, event) - if event.proper? + if event.proper?(current_user) xml.entry do event_link = event_feed_url(event) event_title = event_feed_title(event) diff --git a/app/helpers/issuables_helper.rb b/app/helpers/issuables_helper.rb index 2dfeddf7368..81df2094392 100644 --- a/app/helpers/issuables_helper.rb +++ b/app/helpers/issuables_helper.rb @@ -20,6 +20,23 @@ module IssuablesHelper base_issuable_scope(issuable).where('iid < ?', issuable.iid).first end + def user_dropdown_label(user_id, default_label) + return "Unassigned" if user_id == "0" + + if @project + member = @project.team.find_member(user_id) + user = member.user if member + else + user = User.find_by(id: user_id) + end + + if user + user.name + else + default_label + end + end + private def sidebar_gutter_collapsed? diff --git a/app/helpers/issues_helper.rb b/app/helpers/issues_helper.rb index ae4ebc0854a..e00d3204027 100644 --- a/app/helpers/issues_helper.rb +++ b/app/helpers/issues_helper.rb @@ -98,6 +98,10 @@ module IssuesHelper end.sort.to_sentence(last_word_connector: ', or ') end + def confidential_icon(issue) + icon('eye-slash') if issue.confidential? + end + def emoji_icon(name, unicode = nil, aliases = []) unicode ||= Emoji.emoji_filename(name) rescue "" diff --git a/app/helpers/labels_helper.rb b/app/helpers/labels_helper.rb index 4455dcd0e20..ed37176aa6b 100644 --- a/app/helpers/labels_helper.rb +++ b/app/helpers/labels_helper.rb @@ -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, &block) + def link_to_label(label, project: nil, type: :issue, tooltip: true, &block) project ||= @project || label.project link = send("namespace_project_#{type.to_s.pluralize}_path", project.namespace, @@ -42,7 +42,7 @@ module LabelsHelper if block_given? link_to link, &block else - link_to render_colored_label(label), link + link_to render_colored_label(label, tooltip: tooltip), link end end @@ -50,23 +50,24 @@ module LabelsHelper @project.labels.pluck(:title) end - def render_colored_label(label, label_suffix = '') + def render_colored_label(label, label_suffix = '', tooltip: true) label_color = label.color || Label::DEFAULT_COLOR text_color = text_color_for_bg(label_color) # Intentionally not using content_tag here so that this method can be called # by LabelReferenceFilter - span = %(<span class="label color-label") + - %(style="background-color: #{label_color}; color: #{text_color}">) + + span = %(<span class="label color-label #{"has_tooltip" if tooltip}" ) + + %(style="background-color: #{label_color}; color: #{text_color}" ) + + %(title="#{escape_once(label.description)}" data-container="body">) + %(#{escape_once(label.name)}#{label_suffix}</span>) span.html_safe end - def render_colored_cross_project_label(label) + def render_colored_cross_project_label(label, tooltip: true) label_suffix = label.project.name_with_namespace label_suffix = " <i>in #{escape_once(label_suffix)}</i>" - render_colored_label(label, label_suffix) + render_colored_label(label, label_suffix, tooltip: tooltip) end def suggested_colors @@ -109,19 +110,12 @@ module LabelsHelper end end - def projects_labels_options - labels = - if @project - @project.labels - else - Label.where(project_id: @projects) - end - - grouped_labels = GlobalLabel.build_collection(labels) - grouped_labels.unshift(Label::None) - grouped_labels.unshift(Label::Any) - - options_from_collection_for_select(grouped_labels, 'name', 'title', params[:label_name]) + def labels_filter_path + if @project + namespace_project_labels_path(@project.namespace, @project, :json) + else + labels_dashboard_path(:json) + end end def label_subscription_status(label) diff --git a/app/helpers/milestones_helper.rb b/app/helpers/milestones_helper.rb index e8ac8788d9d..c9d8787bd19 100644 --- a/app/helpers/milestones_helper.rb +++ b/app/helpers/milestones_helper.rb @@ -38,7 +38,7 @@ module MilestonesHelper def milestone_progress_bar(milestone) options = { class: 'progress-bar progress-bar-success', - style: "width: #{milestone.percent_complete}%;" + style: "width: #{milestone.percent_complete(current_user)}%;" } content_tag :div, class: 'progress' do @@ -46,22 +46,12 @@ module MilestonesHelper end end - def projects_milestones_options - milestones = - if @project - @project.milestones - else - Milestone.where(project_id: @projects) - end.active - - epoch = DateTime.parse('1970-01-01') - grouped_milestones = GlobalMilestone.build_collection(milestones) - grouped_milestones = grouped_milestones.sort_by { |x| x.due_date.nil? ? epoch : x.due_date } - grouped_milestones.unshift(Milestone::None) - grouped_milestones.unshift(Milestone::Any) - grouped_milestones.unshift(Milestone::Upcoming) - - options_from_collection_for_select(grouped_milestones, 'name', 'title', params[:milestone_title]) + def milestones_filter_dropdown_path + if @project + namespace_project_milestones_path(@project.namespace, @project, :json) + else + milestones_dashboard_path(:json) + end end def milestone_remaining_days(milestone) diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb index b5acb80b720..5473419ef24 100644 --- a/app/helpers/projects_helper.rb +++ b/app/helpers/projects_helper.rb @@ -26,7 +26,7 @@ module ProjectsHelper image_tag(avatar_icon(author, opts[:size]), width: opts[:size], class: "avatar avatar-inline #{"s#{opts[:size]}" if opts[:size]}", alt:'') if opts[:avatar] end - def link_to_member(project, author, opts = {}) + def link_to_member(project, author, opts = {}, &block) default_opts = { avatar: true, name: true, size: 16, author_class: 'author', title: ":name" } opts = default_opts.merge(opts) @@ -44,6 +44,8 @@ module ProjectsHelper author_html << content_tag(:span, sanitize(author.name), class: opts[:author_class]) if opts[:name] end + author_html << capture(&block) if block + author_html = author_html.html_safe if opts[:name] diff --git a/app/helpers/todos_helper.rb b/app/helpers/todos_helper.rb index 07ddc691d85..edc5686cf08 100644 --- a/app/helpers/todos_helper.rb +++ b/app/helpers/todos_helper.rb @@ -16,14 +16,19 @@ module TodosHelper def todo_target_link(todo) target = todo.target_type.titleize.downcase - link_to "#{target} #{todo.target.to_reference}", todo_target_path(todo), { title: h(todo.target.title) } + link_to "#{target} #{todo.target_reference}", todo_target_path(todo), { title: todo.target.title } end def todo_target_path(todo) anchor = dom_id(todo.note) if todo.note.present? - polymorphic_path([todo.project.namespace.becomes(Namespace), - todo.project, todo.target], anchor: anchor) + if todo.for_commit? + namespace_project_commit_path(todo.project.namespace.becomes(Namespace), todo.project, + todo.target, anchor: anchor) + else + polymorphic_path([todo.project.namespace.becomes(Namespace), + todo.project, todo.target], anchor: anchor) + end end def todos_filter_params diff --git a/app/models/ability.rb b/app/models/ability.rb index 61d5e7dc859..88d7ecf3a16 100644 --- a/app/models/ability.rb +++ b/app/models/ability.rb @@ -49,7 +49,6 @@ class Ability rules = [ :read_project, :read_wiki, - :read_issue, :read_label, :read_milestone, :read_project_snippet, @@ -63,6 +62,9 @@ class Ability # Allow to read builds by anonymous user if guests are allowed rules << :read_build if project.public_builds? + # Allow to read issues by anonymous user if issue is not confidential + rules << :read_issue unless subject.is_a?(Issue) && subject.confidential? + rules - project_disabled_features_rules(project) else [] @@ -325,6 +327,7 @@ class Ability end rules += project_abilities(user, subject.project) + rules = filter_confidential_issues_abilities(user, subject, rules) if subject.is_a?(Issue) rules end end @@ -443,5 +446,17 @@ class Ability :"admin_#{name}" ] end + + def filter_confidential_issues_abilities(user, issue, rules) + return rules if user.admin? || !issue.confidential? + + unless issue.author == user || issue.assignee == user || issue.project.team.member?(user.id) + rules.delete(:admin_issue) + rules.delete(:read_issue) + rules.delete(:update_issue) + end + + rules + end end end diff --git a/app/models/abuse_report.rb b/app/models/abuse_report.rb index cc59aa4e911..b61f5123127 100644 --- a/app/models/abuse_report.rb +++ b/app/models/abuse_report.rb @@ -19,9 +19,9 @@ class AbuseReport < ActiveRecord::Base validates :message, presence: true validates :user_id, uniqueness: { message: 'has already been reported' } - def remove_user + def remove_user(deleted_by:) user.block - user.destroy + DeleteUserWorker.perform_async(deleted_by.id, user.id, delete_solo_owned_groups: true) end def notify diff --git a/app/models/commit_status.rb b/app/models/commit_status.rb index 3b1aa0f5c80..3377a85a55a 100644 --- a/app/models/commit_status.rb +++ b/app/models/commit_status.rb @@ -114,7 +114,7 @@ class CommitStatus < ActiveRecord::Base end def ignored? - failed? && allow_failure? + allow_failure? && (failed? || canceled?) end def duration diff --git a/app/models/concerns/milestoneish.rb b/app/models/concerns/milestoneish.rb index d67df7c1d9c..5b8e3f654ea 100644 --- a/app/models/concerns/milestoneish.rb +++ b/app/models/concerns/milestoneish.rb @@ -1,18 +1,18 @@ module Milestoneish - def closed_items_count - issues.closed.size + merge_requests.closed_and_merged.size + def closed_items_count(user = nil) + issues_visible_to_user(user).closed.size + merge_requests.closed_and_merged.size end - def total_items_count - issues.size + merge_requests.size + def total_items_count(user = nil) + issues_visible_to_user(user).size + merge_requests.size end - def complete? - total_items_count == closed_items_count + def complete?(user = nil) + total_items_count(user) == closed_items_count(user) end - def percent_complete - ((closed_items_count * 100) / total_items_count).abs + def percent_complete(user = nil) + ((closed_items_count(user) * 100) / total_items_count(user)).abs rescue ZeroDivisionError 0 end @@ -22,4 +22,8 @@ module Milestoneish (due_date - Date.today).to_i end + + def issues_visible_to_user(user = nil) + issues.visible_to_user(user) + end end diff --git a/app/models/event.rb b/app/models/event.rb index 9a0bbf50f8b..a5cfeaf388e 100644 --- a/app/models/event.rb +++ b/app/models/event.rb @@ -73,15 +73,17 @@ class Event < ActiveRecord::Base end end - def proper? + def proper?(user = nil) if push? true elsif membership_changed? true elsif created_project? true + elsif issue? + Ability.abilities.allowed?(user, :read_issue, issue) else - ((issue? || merge_request? || note?) && target) || milestone? + ((merge_request? || note?) && target) || milestone? end end diff --git a/app/models/issue.rb b/app/models/issue.rb index 5f58c0508fd..5347d4fa1be 100644 --- a/app/models/issue.rb +++ b/app/models/issue.rb @@ -58,6 +58,13 @@ class Issue < ActiveRecord::Base attributes end + def self.visible_to_user(user) + return where(confidential: false) if user.blank? + return all if user.admin? + + where('issues.confidential = false OR (issues.confidential = true AND (issues.author_id = :user_id OR issues.assignee_id = :user_id OR issues.project_id IN(:project_ids)))', user_id: user.id, project_ids: user.authorized_projects.select(:id)) + end + def self.reference_prefix '#' end @@ -87,11 +94,20 @@ class Issue < ActiveRecord::Base end def referenced_merge_requests(current_user = nil) - Gitlab::ReferenceExtractor.lazily do - [self, *notes].flat_map do |note| - note.all_references(current_user).merge_requests - end - end.sort_by(&:iid) + @referenced_merge_requests ||= {} + @referenced_merge_requests[current_user] ||= begin + Gitlab::ReferenceExtractor.lazily do + [self, *notes].flat_map do |note| + note.all_references(current_user).merge_requests + end + end.sort_by(&:iid).uniq + end + end + + def related_branches + return [] if self.project.empty_repo? + + self.project.repository.branch_names.select { |branch| branch.end_with?("-#{iid}") } end # Reset issue events cache @@ -120,4 +136,15 @@ class Issue < ActiveRecord::Base note.all_references(current_user).merge_requests end.uniq.select { |mr| mr.open? && mr.closes_issue?(self) } end + + def to_branch_name + "#{title.parameterize}-#{iid}" + end + + def can_be_worked_on?(current_user) + !self.closed? && + !self.project.forked? && + self.related_branches.empty? && + self.closed_by_merge_requests(current_user).empty? + end end diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index 188325045e2..a015a9ef394 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -277,8 +277,14 @@ class MergeRequest < ActiveRecord::Base self.target_project.events.where(target_id: self.id, target_type: "MergeRequest", action: Event::CLOSED).last end + WIP_REGEX = /\A\s*(\[WIP\]\s*|WIP:\s*|WIP\s+)+\s*/i.freeze + def work_in_progress? - !!(title =~ /\A\[?WIP(\]|:| )/i) + title =~ WIP_REGEX + end + + def wipless_title + self.title.sub(WIP_REGEX, "") end def mergeable? @@ -516,11 +522,15 @@ class MergeRequest < ActiveRecord::Base end def target_sha - @target_sha ||= target_project.repository.commit(target_branch).sha + @target_sha ||= target_project.repository.commit(target_branch).try(:sha) end def source_sha - last_commit.try(:sha) + last_commit.try(:sha) || source_tip.try(:sha) + end + + def source_tip + source_branch && source_project.repository.commit(source_branch) end def fetch_ref @@ -568,8 +578,11 @@ class MergeRequest < ActiveRecord::Base end def compute_diverged_commits_count + return 0 unless source_sha && target_sha + Gitlab::Git::Commit.between(target_project.repository.raw_repository, source_sha, target_sha).size end + private :compute_diverged_commits_count def diverged_from_target_branch? diverged_commits_count > 0 diff --git a/app/models/milestone.rb b/app/models/milestone.rb index 374590ba0c5..de7183bf6b4 100644 --- a/app/models/milestone.rb +++ b/app/models/milestone.rb @@ -121,8 +121,8 @@ class Milestone < ActiveRecord::Base active? && issues.opened.count.zero? end - def is_empty? - total_items_count.zero? + def is_empty?(user = nil) + total_items_count(user).zero? end def author_id diff --git a/app/models/project.rb b/app/models/project.rb index 3352959a53d..c1374fc9b3c 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -576,10 +576,7 @@ class Project < ActiveRecord::Base end def avatar_in_git - @avatar_file ||= 'logo.png' if repository.blob_at_branch('master', 'logo.png') - @avatar_file ||= 'logo.jpg' if repository.blob_at_branch('master', 'logo.jpg') - @avatar_file ||= 'logo.gif' if repository.blob_at_branch('master', 'logo.gif') - @avatar_file + repository.avatar end def avatar_url diff --git a/app/models/project_wiki.rb b/app/models/project_wiki.rb index c96e6f0b8ea..59b1b86d1fb 100644 --- a/app/models/project_wiki.rb +++ b/app/models/project_wiki.rb @@ -2,7 +2,7 @@ class ProjectWiki include Gitlab::ShellAdapter MARKUPS = { - 'Markdown' => :md, + 'Markdown' => :markdown, 'RDoc' => :rdoc, 'AsciiDoc' => :asciidoc } unless defined?(MARKUPS) @@ -47,7 +47,7 @@ class ProjectWiki def wiki @wiki ||= begin Gollum::Wiki.new(path_to_repo) - rescue Gollum::NoSuchPathError + rescue Rugged::OSError create_repo! end end @@ -90,7 +90,7 @@ class ProjectWiki def create_page(title, content, format = :markdown, message = nil) commit = commit_details(:created, message, title) - wiki.write_page(title, format, content, commit) + wiki.write_page(title, format.to_sym, content, commit) update_project_activity rescue Gollum::DuplicatePageError => e @@ -101,7 +101,7 @@ class ProjectWiki def update_page(page, content, format = :markdown, message = nil) commit = commit_details(:updated, message, page.title) - wiki.update_page(page, page.name, format, content, commit) + wiki.update_page(page, page.name, format.to_sym, content, commit) update_project_activity end diff --git a/app/models/repository.rb b/app/models/repository.rb index 6441cd87e87..25d24493f6e 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -3,6 +3,10 @@ require 'securerandom' class Repository class CommitError < StandardError; end + # Files to use as a project avatar in case no avatar was uploaded via the web + # UI. + AVATAR_FILES = %w{logo.png logo.jpg logo.gif} + include Gitlab::ShellAdapter attr_accessor :path_with_namespace, :project @@ -223,12 +227,6 @@ class Repository send(key) end end - - branches.each do |branch| - unless cache.exist?(:"diverging_commit_counts_#{branch.name}") - send(:diverging_commit_counts, branch) - end - end end def expire_tags_cache @@ -241,12 +239,13 @@ class Repository @branches = nil end - def expire_cache(branch_name = nil) + def expire_cache(branch_name = nil, revision = nil) cache_keys.each do |key| cache.expire(key) end expire_branch_cache(branch_name) + expire_avatar_cache(branch_name, revision) # This ensures this particular cache is flushed after the first commit to a # new repository. @@ -296,18 +295,6 @@ class Repository @tag_count = nil end - def rebuild_cache - cache_keys.each do |key| - cache.expire(key) - send(key) - end - - branches.each do |branch| - cache.expire(:"diverging_commit_counts_#{branch.name}") - diverging_commit_counts(branch) - end - end - def lookup_cache @lookup_cache ||= {} end @@ -316,6 +303,23 @@ class Repository cache.expire(:branch_names) end + def expire_avatar_cache(branch_name = nil, revision = nil) + # Avatars are pulled from the default branch, thus if somebody pushes to a + # different branch there's no need to expire anything. + return if branch_name && branch_name != root_ref + + # We don't want to flush the cache if the commit didn't actually make any + # changes to any of the possible avatar files. + if revision && commit = self.commit(revision) + return unless commit.diffs. + any? { |diff| AVATAR_FILES.include?(diff.new_path) } + end + + cache.expire(:avatar) + + @avatar = nil + end + # Runs code just before a repository is deleted. def before_delete expire_cache if exists? @@ -350,8 +354,8 @@ class Repository end # Runs code after a new commit has been pushed. - def after_push_commit(branch_name) - expire_cache(branch_name) + def after_push_commit(branch_name, revision) + expire_cache(branch_name, revision) end # Runs code after a new branch has been created. @@ -758,12 +762,15 @@ class Repository def parse_search_result(result) ref = nil filename = nil + basename = nil startline = 0 result.each_line.each_with_index do |line, index| if line =~ /^.*:.*:\d+:/ ref, filename, startline = line.split(':') startline = startline.to_i - index + extname = File.extname(filename) + basename = filename.sub(/#{extname}$/, '') break end end @@ -776,6 +783,7 @@ class Repository OpenStruct.new( filename: filename, + basename: basename, ref: ref, startline: startline, data: data @@ -853,6 +861,14 @@ class Repository end end + def avatar + @avatar ||= cache.fetch(:avatar) do + AVATAR_FILES.find do |file| + blob_at_branch('master', file) + end + end + end + private def cache diff --git a/app/models/todo.rb b/app/models/todo.rb index 5f91991f781..d85f7bfdf57 100644 --- a/app/models/todo.rb +++ b/app/models/todo.rb @@ -5,14 +5,15 @@ # id :integer not null, primary key # user_id :integer not null # project_id :integer not null -# target_id :integer not null +# target_id :integer # target_type :string not null # author_id :integer -# note_id :integer # action :integer not null # state :string not null # created_at :datetime # updated_at :datetime +# note_id :integer +# commit_id :string # class Todo < ActiveRecord::Base @@ -27,7 +28,9 @@ class Todo < ActiveRecord::Base delegate :name, :email, to: :author, prefix: true, allow_nil: true - validates :action, :project, :target, :user, presence: true + validates :action, :project, :target_type, :user, presence: true + validates :target_id, presence: true, unless: :for_commit? + validates :commit_id, presence: true, if: :for_commit? default_scope { reorder(id: :desc) } @@ -36,7 +39,7 @@ class Todo < ActiveRecord::Base state_machine :state, initial: :pending do event :done do - transition [:pending, :done] => :done + transition [:pending] => :done end state :pending @@ -50,4 +53,25 @@ class Todo < ActiveRecord::Base target.title end end + + def for_commit? + target_type == "Commit" + end + + # override to return commits, which are not active record + def target + if for_commit? + project.commit(commit_id) rescue nil + else + super + end + end + + def target_reference + if for_commit? + target.short_id + else + target.to_reference + end + end end diff --git a/app/models/wiki_page.rb b/app/models/wiki_page.rb index dbd70dc5a44..526760779a4 100644 --- a/app/models/wiki_page.rb +++ b/app/models/wiki_page.rb @@ -62,7 +62,7 @@ class WikiPage # The raw content of this page. def content @attributes[:content] ||= if @page - @page.raw_data + @page.text_data end end diff --git a/app/services/commits/revert_service.rb b/app/services/commits/revert_service.rb index 9cb918d7a2e..a3c950ede1f 100644 --- a/app/services/commits/revert_service.rb +++ b/app/services/commits/revert_service.rb @@ -9,7 +9,8 @@ module Commits @commit = params[:commit] @create_merge_request = params[:create_merge_request].present? - validate and commit + check_push_permissions unless @create_merge_request + commit rescue Repository::CommitError, Gitlab::Git::Repository::InvalidBlobName, GitHooksService::PreReceiveError, ValidationError, ReversionError => ex error(ex.message) @@ -45,11 +46,11 @@ module Commits end end - def validate + def check_push_permissions allowed = ::Gitlab::GitAccess.new(current_user, project).can_push_to_branch?(@target_branch) unless allowed - raise_error('You are not allowed to push into this branch') + raise ValidationError.new('You are not allowed to push into this branch') end true diff --git a/app/services/delete_user_service.rb b/app/services/delete_user_service.rb index 173e50c9206..ce79287e35a 100644 --- a/app/services/delete_user_service.rb +++ b/app/services/delete_user_service.rb @@ -5,18 +5,22 @@ class DeleteUserService @current_user = current_user end - def execute(user) - if user.solo_owned_groups.present? + def execute(user, options = {}) + if !options[:delete_solo_owned_groups] && user.solo_owned_groups.present? user.errors[:base] << 'You must transfer ownership or delete groups before you can remove user' - user - else - user.personal_projects.each do |project| - # Skip repository removal because we remove directory with namespace - # that contain all this repositories - ::Projects::DestroyService.new(project, current_user, skip_repo: true).pending_delete! - end + return user + end + + user.solo_owned_groups.each do |group| + DestroyGroupService.new(group, current_user).execute + end - user.destroy + user.personal_projects.each do |project| + # Skip repository removal because we remove directory with namespace + # that contain all this repositories + ::Projects::DestroyService.new(project, current_user, skip_repo: true).pending_delete! end + + user.destroy end end diff --git a/app/services/destroy_group_service.rb b/app/services/destroy_group_service.rb index 9189de390a2..3c42ac61be4 100644 --- a/app/services/destroy_group_service.rb +++ b/app/services/destroy_group_service.rb @@ -6,12 +6,12 @@ class DestroyGroupService end def execute - @group.projects.each do |project| + group.projects.each do |project| # Skip repository removal because we remove directory with namespace # that contain all this repositories ::Projects::DestroyService.new(project, current_user, skip_repo: true).pending_delete! end - @group.destroy + group.destroy end end diff --git a/app/services/git_push_service.rb b/app/services/git_push_service.rb index d840ab5e340..14e2a2c0699 100644 --- a/app/services/git_push_service.rb +++ b/app/services/git_push_service.rb @@ -17,7 +17,7 @@ class GitPushService < BaseService # 6. Checks if the project's main language has changed # def execute - @project.repository.after_push_commit(branch_name) + @project.repository.after_push_commit(branch_name, params[:newrev]) if push_remove_branch? @project.repository.after_remove_branch diff --git a/app/services/merge_requests/base_service.rb b/app/services/merge_requests/base_service.rb index 7b306a8a531..ac5b58db862 100644 --- a/app/services/merge_requests/base_service.rb +++ b/app/services/merge_requests/base_service.rb @@ -5,6 +5,19 @@ module MergeRequests SystemNoteService.change_status(merge_request, merge_request.target_project, current_user, merge_request.state, nil) end + def create_title_change_note(issuable, old_title) + removed_wip = old_title =~ MergeRequest::WIP_REGEX && !issuable.work_in_progress? + added_wip = old_title !~ MergeRequest::WIP_REGEX && issuable.work_in_progress? + + if removed_wip + SystemNoteService.remove_merge_request_wip(issuable, issuable.project, current_user) + elsif added_wip + SystemNoteService.add_merge_request_wip(issuable, issuable.project, current_user) + else + super + end + end + def hook_data(merge_request, action) hook_data = merge_request.to_hook_data(current_user) merge_request_url = Gitlab::UrlBuilder.new(:merge_request).build(merge_request.id) diff --git a/app/services/merge_requests/build_service.rb b/app/services/merge_requests/build_service.rb index 954746a39a5..6e9152e444e 100644 --- a/app/services/merge_requests/build_service.rb +++ b/app/services/merge_requests/build_service.rb @@ -47,6 +47,21 @@ module MergeRequests merge_request.title = merge_request.source_branch.titleize.humanize end + # When your branch name starts with an iid followed by a dash this pattern will + # be interpreted as the use wants to close that issue on this project + # Pattern example: 112-fix-mep-mep + # Will lead to appending `Closes #112` to the description + if match = merge_request.source_branch.match(/-(\d+)\z/) + iid = match[1] + closes_issue = "Closes ##{iid}" + + if merge_request.description.present? + merge_request.description << closes_issue.prepend("\n") + else + merge_request.description = closes_issue + end + end + merge_request end diff --git a/app/services/projects/autocomplete_service.rb b/app/services/projects/autocomplete_service.rb index 7408e09ed1e..ba50305dbd5 100644 --- a/app/services/projects/autocomplete_service.rb +++ b/app/services/projects/autocomplete_service.rb @@ -1,11 +1,7 @@ module Projects class AutocompleteService < BaseService - def initialize(project) - @project = project - end - def issues - @project.issues.opened.select([:iid, :title]) + @project.issues.visible_to_user(current_user).opened.select([:iid, :title]) end def merge_requests diff --git a/app/services/projects/housekeeping_service.rb b/app/services/projects/housekeeping_service.rb index bccd67d3dbf..a0973c5d260 100644 --- a/app/services/projects/housekeeping_service.rb +++ b/app/services/projects/housekeeping_service.rb @@ -24,7 +24,7 @@ module Projects def execute raise LeaseTaken if !try_obtain_lease - GitlabShellWorker.perform_async(:gc, @project.path_with_namespace) + GitlabShellOneShotWorker.perform_async(:gc, @project.path_with_namespace) ensure @project.update_column(:pushes_since_gc, 0) end diff --git a/app/services/search/global_service.rb b/app/services/search/global_service.rb index e1e94c5cc38..aa9837038a6 100644 --- a/app/services/search/global_service.rb +++ b/app/services/search/global_service.rb @@ -11,7 +11,7 @@ module Search projects = ProjectsFinder.new.execute(current_user) projects = projects.in_namespace(group.id) if group - Gitlab::SearchResults.new(projects, params[:search]) + Gitlab::SearchResults.new(current_user, projects, params[:search]) end end end diff --git a/app/services/search/project_service.rb b/app/services/search/project_service.rb index c08881dce4b..4b500914cfb 100644 --- a/app/services/search/project_service.rb +++ b/app/services/search/project_service.rb @@ -7,7 +7,8 @@ module Search end def execute - Gitlab::ProjectSearchResults.new(project, + Gitlab::ProjectSearchResults.new(current_user, + project, params[:search], params[:repository_ref]) end diff --git a/app/services/system_note_service.rb b/app/services/system_note_service.rb index 58a861ee08e..c644cd0b951 100644 --- a/app/services/system_note_service.rb +++ b/app/services/system_note_service.rb @@ -144,6 +144,18 @@ class SystemNoteService create_note(noteable: noteable, project: project, author: author, note: body) end + def self.remove_merge_request_wip(noteable, project, author) + body = 'Unmarked this merge request as a Work In Progress' + + create_note(noteable: noteable, project: project, author: author, note: body) + end + + def self.add_merge_request_wip(noteable, project, author) + body = 'Marked this merge request as a **Work In Progress**' + + create_note(noteable: noteable, project: project, author: author, note: body) + end + # Called when the title of a Noteable is changed # # noteable - Noteable object that responds to `title` @@ -207,6 +219,18 @@ class SystemNoteService create_note(noteable: noteable, project: project, author: author, note: body) end + # Called when a branch is created from the 'new branch' button on a issue + # Example note text: + # + # "Started branch `issue-branch-button-201`" + def self.new_issue_branch(issue, project, author, branch) + h = Gitlab::Application.routes.url_helpers + link = h.namespace_project_compare_url(project.namespace, project, from: project.default_branch, to: branch) + + body = "Started branch [`#{branch}`](#{link})" + create_note(noteable: issue, project: project, author: author, note: body) + end + # Called when a Mentionable references a Noteable # # noteable - Noteable object being referenced diff --git a/app/services/todo_service.rb b/app/services/todo_service.rb index 4392e2d17fe..f2662922e90 100644 --- a/app/services/todo_service.rb +++ b/app/services/todo_service.rb @@ -103,24 +103,16 @@ class TodoService # * mark all pending todos related to the target for the current user as done # def mark_pending_todos_as_done(target, user) - pending_todos(user, target.project, target).update_all(state: :done) + attributes = attributes_for_target(target) + pending_todos(user, attributes).update_all(state: :done) end private - def create_todos(project, target, author, users, action, note = nil) + def create_todos(users, attributes) Array(users).each do |user| - next if pending_todos(user, project, target).exists? - - Todo.create( - project: project, - user_id: user.id, - author_id: author.id, - target_id: target.id, - target_type: target.class.name, - action: action, - note: note - ) + next if pending_todos(user, attributes).exists? + Todo.create(attributes.merge(user_id: user.id)) end end @@ -130,8 +122,8 @@ class TodoService end def handle_note(note, author) - # Skip system notes, notes on commit, and notes on project snippet - return if note.system? || ['Commit', 'Snippet'].include?(note.noteable_type) + # Skip system notes, and notes on project snippet + return if note.system? || note.for_project_snippet? project = note.project target = note.noteable @@ -142,13 +134,39 @@ class TodoService def create_assignment_todo(issuable, author) if issuable.assignee && issuable.assignee != author - create_todos(issuable.project, issuable, author, issuable.assignee, Todo::ASSIGNED) + attributes = attributes_for_todo(issuable.project, issuable, author, Todo::ASSIGNED) + create_todos(issuable.assignee, attributes) end end - def create_mention_todos(project, issuable, author, note = nil) - mentioned_users = filter_mentioned_users(project, note || issuable, author) - create_todos(project, issuable, author, mentioned_users, Todo::MENTIONED, note) + def create_mention_todos(project, target, author, note = nil) + mentioned_users = filter_mentioned_users(project, note || target, author) + attributes = attributes_for_todo(project, target, author, Todo::MENTIONED, note) + create_todos(mentioned_users, attributes) + end + + def attributes_for_target(target) + attributes = { + project_id: target.project.id, + target_id: target.id, + target_type: target.class.name, + commit_id: nil + } + + if target.is_a?(Commit) + attributes.merge!(target_id: nil, commit_id: target.id) + end + + attributes + end + + def attributes_for_todo(project, target, author, action, note = nil) + attributes_for_target(target).merge!( + project_id: project.id, + author_id: author.id, + action: action, + note: note + ) end def filter_mentioned_users(project, target, author) @@ -160,11 +178,8 @@ class TodoService mentioned_users.uniq end - def pending_todos(user, project, target) - user.todos.pending.where( - project_id: project.id, - target_id: target.id, - target_type: target.class.name - ) + def pending_todos(user, criteria = {}) + valid_keys = [:project_id, :target_id, :target_type, :commit_id] + user.todos.pending.where(criteria.slice(*valid_keys)) end end diff --git a/app/views/admin/labels/_label.html.haml b/app/views/admin/labels/_label.html.haml index 5736a301910..f417b2e44a4 100644 --- a/app/views/admin/labels/_label.html.haml +++ b/app/views/admin/labels/_label.html.haml @@ -1,6 +1,6 @@ %li{id: dom_id(label)} .label-row - = render_colored_label(label) + = render_colored_label(label, tooltip: false) = markdown(label.description, pipeline: :single_line) .pull-right = link_to 'Edit', edit_admin_label_path(label), class: 'btn btn-sm' diff --git a/app/views/dashboard/todos/_todo.html.haml b/app/views/dashboard/todos/_todo.html.haml index 45cfe3da188..e3a4d64df01 100644 --- a/app/views/dashboard/todos/_todo.html.haml +++ b/app/views/dashboard/todos/_todo.html.haml @@ -2,7 +2,7 @@ .todo-item.todo-block = image_tag avatar_icon(todo.author_email, 40), class: 'avatar s40', alt:'' - .todo-title + .todo-title.title %span.author-name - if todo.author = link_to_author(todo) @@ -16,7 +16,9 @@ - if todo.pending? .todo-actions.pull-right - = link_to 'Done', [:dashboard, todo], method: :delete, class: 'btn' + = link_to [:dashboard, todo], method: :delete, class: 'btn btn-loading done-todo' do + Done + = icon('spinner spin') .todo-body .todo-note diff --git a/app/views/dashboard/todos/index.html.haml b/app/views/dashboard/todos/index.html.haml index 946d7df3933..f9ec3a89158 100644 --- a/app/views/dashboard/todos/index.html.haml +++ b/app/views/dashboard/todos/index.html.haml @@ -3,13 +3,15 @@ .top-area %ul.nav-links - %li{class: ('active' if params[:state].blank? || params[:state] == 'pending')} + - todo_pending_active = ('active' if params[:state].blank? || params[:state] == 'pending') + %li{class: "todos-pending #{todo_pending_active}"} = link_to todos_filter_path(state: 'pending') do %span To do %span{class: 'badge'} = todos_pending_count - %li{class: ('active' if params[:state] == 'done')} + - todo_done_active = ('active' if params[:state] == 'done') + %li{class: "todos-done #{todo_done_active}"} = link_to todos_filter_path(state: 'done') do %span Done @@ -18,7 +20,9 @@ .nav-controls - if @todos.any?(&:pending?) - = link_to 'Mark all as done', destroy_all_dashboard_todos_path(todos_filter_params), class: 'btn', method: :delete + = link_to destroy_all_dashboard_todos_path(todos_filter_params), class: 'btn btn-loading js-todos-mark-all', method: :delete do + Mark all as done + = icon('spinner spin') .todos-filters .gray-content-block.second-block @@ -42,12 +46,12 @@ .prepend-top-default - if @todos.any? - @todos.group_by(&:project).each do |group| - .panel.panel-default.panel-small + .panel.panel-default.panel-small.js-todos-list - project = group[0] .panel-heading = link_to project.name_with_namespace, namespace_project_path(project.namespace, project) - %ul.well-list.todos-list + %ul.content-list.todos-list = render group[1] = paginate @todos, theme: "gitlab" - else diff --git a/app/views/events/_event.html.haml b/app/views/events/_event.html.haml index 36fb2d51629..2d9d9dd6342 100644 --- a/app/views/events/_event.html.haml +++ b/app/views/events/_event.html.haml @@ -1,4 +1,4 @@ -- if event.proper? +- if event.proper?(current_user) .event-item{class: "#{event.body? ? "event-block" : "event-inline" }"} .event-item-timestamp #{time_ago_with_tooltip(event.created_at)} diff --git a/app/views/layouts/header/_default.html.haml b/app/views/layouts/header/_default.html.haml index 714da410f56..bfa5937cf3f 100644 --- a/app/views/layouts/header/_default.html.haml +++ b/app/views/layouts/header/_default.html.haml @@ -45,6 +45,8 @@ %h1.title= title = render 'shared/outdated_browser' + - if @project && !@project.empty_repo? - :javascript - var findFileURL = "#{namespace_project_find_file_path(@project.namespace, @project, @ref || @project.repository.root_ref)}"; + - if ref = @ref || @project.repository.root_ref + :javascript + var findFileURL = "#{namespace_project_find_file_path(@project.namespace, @project, ref)}"; diff --git a/app/views/layouts/nav/_project.html.haml b/app/views/layouts/nav/_project.html.haml index 0ae83ee01eb..86b46e8c75e 100644 --- a/app/views/layouts/nav/_project.html.haml +++ b/app/views/layouts/nav/_project.html.haml @@ -67,7 +67,7 @@ %span Issues - if @project.default_issues_tracker? - %span.count.issue_counter= number_with_delimiter(@project.issues.opened.count) + %span.count.issue_counter= number_with_delimiter(@project.issues.visible_to_user(current_user).opened.count) - if project_nav_tab? :merge_requests = nav_link(controller: :merge_requests) do diff --git a/app/views/projects/_builds_settings.html.haml b/app/views/projects/_builds_settings.html.haml new file mode 100644 index 00000000000..95ab9ecf3e8 --- /dev/null +++ b/app/views/projects/_builds_settings.html.haml @@ -0,0 +1,60 @@ +%fieldset.builds-feature + %legend + Builds: + .form-group + .col-sm-offset-2.col-sm-10 + %p Get recent application code using the following command: + .radio + = f.label :build_allow_git_fetch_false do + = f.radio_button :build_allow_git_fetch, 'false' + %strong git clone + %br + %span.descr Slower but makes sure you have a clean dir before every build + .radio + = f.label :build_allow_git_fetch_true do + = f.radio_button :build_allow_git_fetch, 'true' + %strong git fetch + %br + %span.descr Faster + + .form-group + = f.label :build_timeout_in_minutes, 'Timeout', class: 'control-label' + .col-sm-10 + = f.number_field :build_timeout_in_minutes, class: 'form-control', min: '0' + %p.help-block per build in minutes + .form-group + = f.label :build_coverage_regex, "Test coverage parsing", class: 'control-label' + .col-sm-10 + .input-group + %span.input-group-addon / + = f.text_field :build_coverage_regex, class: 'form-control', placeholder: '\(\d+.\d+\%\) covered' + %span.input-group-addon / + %p.help-block + We will use this regular expression to find test coverage output in build trace. + Leave blank if you want to disable this feature + .bs-callout.bs-callout-info + %p Below are examples of regex for existing tools: + %ul + %li + Simplecov (Ruby) - + %code \(\d+.\d+\%\) covered + %li + pytest-cov (Python) - + %code \d+\%\s*$ + %li + phpunit --coverage-text --colors=never (PHP) - + %code ^\s*Lines:\s*\d+.\d+\% + + .form-group + .col-sm-offset-2.col-sm-10 + .checkbox + = f.label :public_builds do + = f.check_box :public_builds + %strong Public builds + .help-block Allow everyone to access builds for Public and Internal projects + + .form-group + = f.label :runners_token, "Runners token", class: 'control-label' + .col-sm-10 + = f.text_field :runners_token, class: "form-control", placeholder: 'xEeFCaDAB89' + %p.help-block The secure token used to checkout project. diff --git a/app/views/projects/diffs/_diffs.html.haml b/app/views/projects/diffs/_diffs.html.haml index 6086ad3661e..2e1a37aa06d 100644 --- a/app/views/projects/diffs/_diffs.html.haml +++ b/app/views/projects/diffs/_diffs.html.haml @@ -20,4 +20,4 @@ - next unless blob = render 'projects/diffs/file', i: index, project: project, - diff_file: diff_file, diff_commit: diff_commit, blob: blob + diff_file: diff_file, diff_commit: diff_commit, blob: blob, diff_refs: diff_refs diff --git a/app/views/projects/diffs/_file.html.haml b/app/views/projects/diffs/_file.html.haml index 3ac058a3bf8..3898bb202c5 100644 --- a/app/views/projects/diffs/_file.html.haml +++ b/app/views/projects/diffs/_file.html.haml @@ -42,13 +42,17 @@ .diff-content.diff-wrap-lines -# Skipp all non non-supported blobs - return unless blob.respond_to?('text?') - - if blob_text_viewable?(blob) - - if diff_view == 'parallel' - = render "projects/diffs/parallel_view", diff_file: diff_file, project: project, blob: blob, index: i - - else - = render "projects/diffs/text_file", diff_file: diff_file, index: i - - elsif blob.image? - - old_file = project.repository.prev_blob_for_diff(diff_commit, diff_file) - = render "projects/diffs/image", diff_file: diff_file, old_file: old_file, file: blob, index: i + - if diff_file.too_large? + .nothing-here-block + This diff could not be displayed because it is too large. - else - .nothing-here-block No preview for this file type + - if blob_text_viewable?(blob) + - if diff_view == 'parallel' + = render "projects/diffs/parallel_view", diff_file: diff_file, project: project, blob: blob, index: i + - else + = render "projects/diffs/text_file", diff_file: diff_file, index: i + - elsif blob.image? + - old_file = project.repository.prev_blob_for_diff(diff_commit, diff_file) + = render "projects/diffs/image", diff_file: diff_file, old_file: old_file, file: blob, index: i, diff_refs: diff_refs + - else + .nothing-here-block No preview for this file type diff --git a/app/views/projects/diffs/_image.html.haml b/app/views/projects/diffs/_image.html.haml index 752e92e2e6b..8367112a9cb 100644 --- a/app/views/projects/diffs/_image.html.haml +++ b/app/views/projects/diffs/_image.html.haml @@ -1,6 +1,7 @@ - diff = diff_file.diff - file_raw_path = namespace_project_raw_path(@project.namespace, @project, tree_join(@commit.id, diff.new_path)) -- old_file_raw_path = namespace_project_raw_path(@project.namespace, @project, tree_join(@commit.parent_id, diff.old_path)) +- old_commit_id = diff_refs.first.id +- old_file_raw_path = namespace_project_raw_path(@project.namespace, @project, tree_join(old_commit_id, diff.old_path)) - if diff.renamed_file || diff.new_file || diff.deleted_file .image %span.wrap @@ -12,7 +13,7 @@ %div.two-up.view %span.wrap .frame.deleted - %a{href: namespace_project_blob_path(@project.namespace, @project, tree_join(@commit.parent_id, diff.old_path))} + %a{href: namespace_project_blob_path(@project.namespace, @project, tree_join(old_commit_id, diff.old_path))} %img{src: old_file_raw_path} %p.image-info.hide %span.meta-filesize= "#{number_to_human_size old_file.size}" diff --git a/app/views/projects/edit.html.haml b/app/views/projects/edit.html.haml index f2e56081afe..6d872cd0b21 100644 --- a/app/views/projects/edit.html.haml +++ b/app/views/projects/edit.html.haml @@ -84,6 +84,8 @@ %br %span.descr Share code pastes with others out of git repository + = render 'builds_settings', f: f + %fieldset.features %legend Project avatar: @@ -110,69 +112,6 @@ %hr = link_to 'Remove avatar', namespace_project_avatar_path(@project.namespace, @project), data: { confirm: "Project avatar will be removed. Are you sure?"}, method: :delete, class: "btn btn-remove btn-sm remove-avatar" - %fieldset.features - %legend - Continuous Integration - .form-group - .col-sm-offset-2.col-sm-10 - %p Get recent application code using the following command: - .radio - = f.label :build_allow_git_fetch_false do - = f.radio_button :build_allow_git_fetch, 'false' - %strong git clone - %br - %span.descr Slower but makes sure you have a clean dir before every build - .radio - = f.label :build_allow_git_fetch_true do - = f.radio_button :build_allow_git_fetch, 'true' - %strong git fetch - %br - %span.descr Faster - - .form-group - = f.label :build_timeout_in_minutes, 'Timeout', class: 'control-label' - .col-sm-10 - = f.number_field :build_timeout_in_minutes, class: 'form-control', min: '0' - %p.help-block per build in minutes - .form-group - = f.label :build_coverage_regex, "Test coverage parsing", class: 'control-label' - .col-sm-10 - .input-group - %span.input-group-addon / - = f.text_field :build_coverage_regex, class: 'form-control', placeholder: '\(\d+.\d+\%\) covered' - %span.input-group-addon / - %p.help-block - We will use this regular expression to find test coverage output in build trace. - Leave blank if you want to disable this feature - .bs-callout.bs-callout-info - %p Below are examples of regex for existing tools: - %ul - %li - Simplecov (Ruby) - - %code \(\d+.\d+\%\) covered - %li - pytest-cov (Python) - - %code \d+\%\s*$ - %li - phpunit --coverage-text --colors=never (PHP) - - %code ^\s*Lines:\s*\d+.\d+\% - - .form-group - .col-sm-offset-2.col-sm-10 - .checkbox - = f.label :public_builds do - = f.check_box :public_builds - %strong Public builds - .help-block Allow everyone to access builds for Public and Internal projects - - %fieldset.features - %legend - Advanced settings - .form-group - = f.label :runners_token, "CI token", class: 'control-label' - .col-sm-10 - = f.text_field :runners_token, class: "form-control", placeholder: 'xEeFCaDAB89' - %p.help-block The secure token used to checkout project. .form-actions = f.submit 'Save changes', class: "btn btn-save" diff --git a/app/views/projects/issues/_issue.html.haml b/app/views/projects/issues/_issue.html.haml index a44f34c2a68..4aa92d0b39e 100644 --- a/app/views/projects/issues/_issue.html.haml +++ b/app/views/projects/issues/_issue.html.haml @@ -3,10 +3,11 @@ .issue-check = check_box_tag dom_id(issue,"selected"), nil, false, 'data-id' => issue.id, class: "selected_issue" - .issue-title + .issue-title.title %span.issue-title-text - = link_to_gfm issue.title, issue_path(issue), class: "title" - %ul.controls.light + = confidential_icon(issue) + = link_to_gfm issue.title, issue_path(issue) + %ul.controls - if issue.closed? %li CLOSED diff --git a/app/views/projects/issues/_merge_requests.html.haml b/app/views/projects/issues/_merge_requests.html.haml index d9868ad1f0a..d6b38b327ff 100644 --- a/app/views/projects/issues/_merge_requests.html.haml +++ b/app/views/projects/issues/_merge_requests.html.haml @@ -1,4 +1,4 @@ --if @merge_requests.any? +- if @merge_requests.any? %h2.merge-requests-title = pluralize(@merge_requests.count, 'Related Merge Request') %ul.unstyled-list diff --git a/app/views/projects/issues/_new_branch.html.haml b/app/views/projects/issues/_new_branch.html.haml new file mode 100644 index 00000000000..e66e4669d48 --- /dev/null +++ b/app/views/projects/issues/_new_branch.html.haml @@ -0,0 +1,5 @@ +- if current_user && can?(current_user, :push_code, @project) && @issue.can_be_worked_on?(current_user) + .pull-right + = link_to namespace_project_branches_path(@project.namespace, @project, branch_name: @issue.to_branch_name, issue_iid: @issue.iid), method: :post, class: 'btn', title: @issue.to_branch_name do + = icon('code-fork') + New Branch diff --git a/app/views/projects/issues/_related_branches.html.haml b/app/views/projects/issues/_related_branches.html.haml new file mode 100644 index 00000000000..b10cd03515f --- /dev/null +++ b/app/views/projects/issues/_related_branches.html.haml @@ -0,0 +1,15 @@ +- if @related_branches.any? + %h2.related-branches-title + = pluralize(@related_branches.count, 'Related Branch') + %ul.unstyled-list + - @related_branches.each do |branch| + %li + - sha = @project.repository.find_branch(branch).target + - ci_commit = @project.ci_commit(sha) if sha + - if ci_commit + %span.related-branch-ci-status + = render_ci_status(ci_commit) + %span.related-branch-info + %strong + = link_to namespace_project_compare_path(@project.namespace, @project, from: @project.default_branch, to: branch), class: "label-branch" do + = branch diff --git a/app/views/projects/issues/show.html.haml b/app/views/projects/issues/show.html.haml index 0242276cd84..52df3de8a27 100644 --- a/app/views/projects/issues/show.html.haml +++ b/app/views/projects/issues/show.html.haml @@ -22,20 +22,20 @@ = icon('angle-double-left') .issue-meta + = confidential_icon(@issue) %strong.identifier Issue ##{@issue.iid} %span.creator - by + opened .editor-details .editor-details + = time_ago_with_tooltip(@issue.created_at) + by %strong = link_to_member(@project, @issue.author, size: 24, mobile_classes: "hidden-xs") - %span.hidden-xs - = '@' + @issue.author.username %strong = link_to_member(@project, @issue.author, size: 24, mobile_classes: "hidden-sm hidden-md hidden-lg", by_username: true, avatar: false) - = time_ago_with_tooltip(@issue.created_at) .pull-right.issue-btn-group - if can?(current_user, :create_issue, @project) @@ -63,15 +63,14 @@ = markdown(@issue.description, cache_key: [@issue, "description"]) %textarea.hidden.js-task-list-field = @issue.description - - if @issue.updated_at != @issue.created_at - %small - Edited - = time_ago_with_tooltip(@issue.updated_at, placement: 'bottom', html_class: 'issue_edited_ago') + = edited_time_ago_with_tooltip(@issue, placement: 'bottom', html_class: 'issue_edited_ago') .merge-requests = render 'merge_requests' + = render 'related_branches' .content-block.content-block-small + = render 'new_branch' = render 'votes/votes_block', votable: @issue .row diff --git a/app/views/projects/merge_requests/_merge_request.html.haml b/app/views/projects/merge_requests/_merge_request.html.haml index 18cf3f14f0b..13d0cbdde1d 100644 --- a/app/views/projects/merge_requests/_merge_request.html.haml +++ b/app/views/projects/merge_requests/_merge_request.html.haml @@ -1,8 +1,8 @@ %li{ class: mr_css_classes(merge_request) } - .merge-request-title + .merge-request-title.title %span.merge-request-title-text - = link_to_gfm merge_request.title, merge_request_path(merge_request), class: "title" - %ul.controls.light + = link_to_gfm merge_request.title, merge_request_path(merge_request) + %ul.controls - if merge_request.merged? %li MERGED diff --git a/app/views/projects/merge_requests/show/_mr_box.html.haml b/app/views/projects/merge_requests/show/_mr_box.html.haml index 602f787e6cf..a23bd8d18d0 100644 --- a/app/views/projects/merge_requests/show/_mr_box.html.haml +++ b/app/views/projects/merge_requests/show/_mr_box.html.haml @@ -11,7 +11,4 @@ %textarea.hidden.js-task-list-field = @merge_request.description - - if @merge_request.updated_at != @merge_request.created_at - %small - Edited - = time_ago_with_tooltip(@merge_request.updated_at, placement: 'bottom') + = edited_time_ago_with_tooltip(@merge_request, placement: 'bottom') diff --git a/app/views/projects/merge_requests/show/_mr_title.html.haml b/app/views/projects/merge_requests/show/_mr_title.html.haml index a75c0d96c57..eeb605e2dc5 100644 --- a/app/views/projects/merge_requests/show/_mr_title.html.haml +++ b/app/views/projects/merge_requests/show/_mr_title.html.haml @@ -8,18 +8,21 @@ = icon('angle-double-left') .issue-meta %strong.identifier - Merge Request ##{@merge_request.iid} + %span.hidden-sm.hidden-md.hidden-lg + MR + %span.hidden-xs + Merge Request + !#{@merge_request.iid} %span.creator - by + opened .editor-details + = time_ago_with_tooltip(@merge_request.created_at) + by %strong = link_to_member(@project, @merge_request.author, size: 24, mobile_classes: "hidden-xs") - %span.hidden-xs - = '@' + @merge_request.author.username %strong = link_to_member(@project, @merge_request.author, size: 24, mobile_classes: "hidden-sm hidden-md hidden-lg", by_username: true, avatar: false) - = time_ago_with_tooltip(@merge_request.created_at) .issue-btn-group.pull-right - if can?(current_user, :update_merge_request, @merge_request) diff --git a/app/views/projects/merge_requests/widget/open/_wip.html.haml b/app/views/projects/merge_requests/widget/open/_wip.html.haml index 0cf16542cc1..c296422a9cf 100644 --- a/app/views/projects/merge_requests/widget/open/_wip.html.haml +++ b/app/views/projects/merge_requests/widget/open/_wip.html.haml @@ -1,5 +1,11 @@ %h4 This merge request is currently a Work In Progress -%p - When this merge request is ready, remove the "WIP" prefix from the title to allow it to be merged. +- if can?(current_user, :update_merge_request, @merge_request) + %p + When this merge request is ready, + = link_to remove_wip_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), method: :post do + remove the + %code WIP: + prefix from the title + to allow it to be merged. diff --git a/app/views/projects/milestones/show.html.haml b/app/views/projects/milestones/show.html.haml index b4597043a27..be63875ab34 100644 --- a/app/views/projects/milestones/show.html.haml +++ b/app/views/projects/milestones/show.html.haml @@ -42,7 +42,7 @@ = preserve do = markdown @milestone.description -- if @milestone.complete? && @milestone.active? +- if @milestone.complete?(current_user) && @milestone.active? .alert.alert-success.prepend-top-default %span All issues for this milestone are closed. You may close milestone now. diff --git a/app/views/projects/notes/_note.html.haml b/app/views/projects/notes/_note.html.haml index 52972576aff..2cf32e6093d 100644 --- a/app/views/projects/notes/_note.html.haml +++ b/app/views/projects/notes/_note.html.haml @@ -27,20 +27,13 @@ %span.note-last-update %a{name: dom_id(note), href: "##{dom_id(note)}", title: 'Link here'} = time_ago_with_tooltip(note.created_at, placement: 'bottom', html_class: 'note_created_ago') - - if note.updated_at != note.created_at - %span.note-updated-at - · - = icon('edit', title: 'edited') - = time_ago_with_tooltip(note.updated_at, placement: 'bottom', html_class: 'note_edited_ago') - - if note.updated_by && note.updated_by != note.author - by #{link_to_member(note.project, note.updated_by, avatar: false, author_class: nil)} - .note-body{class: note_editable?(note) ? 'js-task-list-container' : ''} .note-text = preserve do = markdown(note.note, pipeline: :note, cache_key: [note, "note"]) - if note_editable?(note) = render 'projects/notes/edit_form', note: note + = edited_time_ago_with_tooltip(note, placement: 'bottom', html_class: 'note_edited_ago', include_author: true) - if note.attachment.url .note-attachment @@ -54,4 +47,3 @@ = link_to delete_attachment_namespace_project_note_path(note.project.namespace, note.project, note), title: 'Delete this attachment', method: :delete, remote: true, data: { confirm: 'Are you sure you want to remove the attachment?' }, class: 'danger js-note-attachment-delete' do = icon('trash-o', class: 'cred') - .clear diff --git a/app/views/projects/repositories/_download_archive.html.haml b/app/views/projects/repositories/_download_archive.html.haml index b9486a9b492..24658319060 100644 --- a/app/views/projects/repositories/_download_archive.html.haml +++ b/app/views/projects/repositories/_download_archive.html.haml @@ -10,7 +10,7 @@ %span.caret %span.sr-only Select Archive Format - %ul.col-xs-10.dropdown-menu{ role: 'menu' } + %ul.col-xs-10.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 diff --git a/app/views/search/results/_issue.html.haml b/app/views/search/results/_issue.html.haml index 45d700781f3..710f5613c81 100644 --- a/app/views/search/results/_issue.html.haml +++ b/app/views/search/results/_issue.html.haml @@ -1,5 +1,6 @@ .search-result-row %h4 + = confidential_icon(issue) = link_to [issue.project.namespace.becomes(Namespace), issue.project, issue] do %span.term.str-truncated= issue.title .pull-right ##{issue.iid} diff --git a/app/views/search/results/_wiki_blob.html.haml b/app/views/search/results/_wiki_blob.html.haml index f5859481d46..235106c4f74 100644 --- a/app/views/search/results/_wiki_blob.html.haml +++ b/app/views/search/results/_wiki_blob.html.haml @@ -2,9 +2,9 @@ .blob-result .file-holder .file-title - = link_to namespace_project_wiki_path(@project.namespace, @project, wiki_blob.filename) do + = link_to namespace_project_wiki_path(@project.namespace, @project, wiki_blob.basename) do %i.fa.fa-file %strong - = wiki_blob.filename + = wiki_blob.basename .file-content.code.term = render 'shared/file_highlight', blob: wiki_blob, first_line_number: wiki_blob.startline diff --git a/app/views/shared/_label_row.html.haml b/app/views/shared/_label_row.html.haml index 8134b15d245..4b47b0291be 100644 --- a/app/views/shared/_label_row.html.haml +++ b/app/views/shared/_label_row.html.haml @@ -1,4 +1,4 @@ %span.label-row - = link_to_label(label) + = link_to_label(label, tooltip: false) %span.prepend-left-10 = markdown(label.description, pipeline: :single_line) diff --git a/app/views/shared/groups/_group.html.haml b/app/views/shared/groups/_group.html.haml index 82eeb2088c8..67a59f420f3 100644 --- a/app/views/shared/groups/_group.html.haml +++ b/app/views/shared/groups/_group.html.haml @@ -10,7 +10,7 @@ %i.fa.fa-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 - %i.fa.fa-sign-out + = icon('sign-out') .stats %span @@ -25,12 +25,13 @@ = visibility_level_icon(group.visibility_level, fw: false) = image_tag group_icon(group), class: "avatar s40 hidden-xs" - = link_to group, class: 'group-name title' do - = group.name + .title + = link_to group, class: 'group-name' do + = group.name - - if group_member - as - %span #{group_member.human_access} + - if group_member + as + %span #{group_member.human_access} - if group.description.present? .description diff --git a/app/views/shared/issuable/_filter.html.haml b/app/views/shared/issuable/_filter.html.haml index 42a3c2c3f02..ac20f7d1f7e 100644 --- a/app/views/shared/issuable/_filter.html.haml +++ b/app/views/shared/issuable/_filter.html.haml @@ -9,75 +9,20 @@ .filter-item.inline - if params[:author_id] = hidden_field_tag(:author_id, params[:author_id]) - = dropdown_tag("Author", options: { toggle_class: "js-user-search js-filter-submit js-author-search", title: "Filter by author", filter: true, dropdown_class: "dropdown-menu-user dropdown-menu-selectable dropdown-menu-author", - placeholder: "Search authors", data: { any_user: "Any Author", first_user: (current_user.username if current_user), current_user: true, project_id: (@project.id if @project), selected: params[:author_id], field_name: "author_id" } }) + = dropdown_tag(user_dropdown_label(params[:author_id], "Author"), options: { toggle_class: "js-user-search js-filter-submit js-author-search", title: "Filter by author", filter: true, dropdown_class: "dropdown-menu-user dropdown-menu-selectable dropdown-menu-author", + placeholder: "Search authors", data: { any_user: "Any Author", first_user: (current_user.username if current_user), current_user: true, project_id: (@project.id if @project), selected: params[:author_id], field_name: "author_id", default_label: "Author" } }) .filter-item.inline - if params[:assignee_id] = hidden_field_tag(:assignee_id, params[:assignee_id]) - = dropdown_tag("Assignee", options: { toggle_class: "js-user-search js-filter-submit js-assignee-search", title: "Filter by assignee", filter: true, dropdown_class: "dropdown-menu-user dropdown-menu-selectable dropdown-menu-assignee", - placeholder: "Search assignee", data: { any_user: "Any Author", first_user: (current_user.username if current_user), null_user: true, current_user: true, project_id: (@project.id if @project), selected: params[:assignee_id], field_name: "assignee_id" } }) + = dropdown_tag(user_dropdown_label(params[:assignee_id], "Assignee"), options: { toggle_class: "js-user-search js-filter-submit js-assignee-search", title: "Filter by assignee", filter: true, dropdown_class: "dropdown-menu-user dropdown-menu-selectable dropdown-menu-assignee", + placeholder: "Search assignee", data: { any_user: "Any Assignee", first_user: (current_user.username if current_user), null_user: true, current_user: true, project_id: (@project.id if @project), selected: params[:assignee_id], field_name: "assignee_id", default_label: "Assignee" } }) .filter-item.inline.milestone-filter - - if params[:milestone_title] - = hidden_field_tag(:milestone_title, params[:milestone_title]) - = dropdown_tag("Milestone", options: { title: "Filter by milestone", toggle_class: 'js-milestone-select js-filter-submit', filter: true, dropdown_class: "dropdown-menu-selectable", - placeholder: "Search milestones", footer_content: true, data: { show_no: true, show_any: true, field_name: "milestone_title", selected: params[:milestone_title], project_id: (@project.id if @project), milestones: (namespace_project_milestones_path(@project.namespace, @project, :js) if @project) } }) do - - if @project - %ul.dropdown-footer-list - - if can? current_user, :admin_milestone, @project - %li - = link_to new_namespace_project_milestone_path(@project.namespace, @project), title: "New Milestone" do - Create new - %li - = link_to namespace_project_milestones_path(@project.namespace, @project) do - - if can? current_user, :admin_milestone, @project - Manage milestones - - else - View milestones + = render "shared/issuable/milestone_dropdown" .filter-item.inline.labels-filter - - if params[:label_name] - = hidden_field_tag(:label_name, params[:label_name]) - .dropdown - %button.dropdown-menu-toggle.js-label-select.js-filter-submit{type: "button", data: {toggle: "dropdown", field_name: "label_name", show_no: "true", show_any: "true", selected: params[:label_name], project_id: (@project.id if @project), labels: (namespace_project_labels_path(@project.namespace, @project, :js) if @project)}} - %span.dropdown-toggle-text - Label - = icon('chevron-down') - .dropdown-menu.dropdown-select.dropdown-menu-paging.dropdown-menu-labels.dropdown-menu-selectable - .dropdown-page-one - = dropdown_title("Filter by label") - = dropdown_filter("Search labels") - = dropdown_content - - if @project - = dropdown_footer do - %ul.dropdown-footer-list - - if can? current_user, :admin_label, @project - %li - %a.dropdown-toggle-page{href: "#"} - Create new - %li - = link_to namespace_project_labels_path(@project.namespace, @project) do - - if can? current_user, :admin_label, @project - Manage labels - - else - View labels - - if can? current_user, :admin_label, @project - .dropdown-page-two - = dropdown_title("Create new label", back: true) - = dropdown_content do - %input#new_label_color{type: "hidden"} - %input#new_label_name.dropdown-input-field{type: "text", placeholder: "Name new label"} - .dropdown-label-color-preview.js-dropdown-label-color-preview - .suggest-colors.suggest-colors-dropdown - - suggested_colors.each do |color| - = link_to '#', style: "background-color: #{color}", data: { color: color } do -   - %button.btn.btn-primary.js-new-label-btn{type: "button"} - Create - = dropdown_loading - .dropdown-loading - = icon('spinner spin') + = render "shared/issuable/label_dropdown" .pull-right = render 'shared/sort_dropdown' diff --git a/app/views/shared/issuable/_form.html.haml b/app/views/shared/issuable/_form.html.haml index d5a4aad05d9..80418e69d9c 100644 --- a/app/views/shared/issuable/_form.html.haml +++ b/app/views/shared/issuable/_form.html.haml @@ -13,12 +13,21 @@ - if issuable.is_a?(MergeRequest) %p.help-block - - if issuable.work_in_progress? - Remove the <code>WIP</code> prefix from the title to allow this - <strong>Work In Progress</strong> merge request to be merged when it's ready. - - else - Start the title with <code>[WIP]</code> or <code>WIP:</code> to prevent a - <strong>Work In Progress</strong> merge request from being merged before it's ready. + .js-wip-explanation + %a.js-toggle-wip{href: ""} + Remove the + %code WIP: + prefix from the title + to allow this + %strong Work In Progress + merge request to be merged when it's ready. + .js-no-wip-explanation + %a.js-toggle-wip{href: ""} + Start the title with + %code WIP: + to prevent a + %strong Work In Progress + merge request from being merged before it's ready. .form-group.detail-page-description = f.label :description, 'Description', class: 'control-label' .col-sm-10 @@ -29,6 +38,15 @@ = render 'projects/notes/hints' .clearfix .error-alert + +- if issuable.is_a?(Issue) && !issuable.project.private? + .form-group + .col-sm-offset-2.col-sm-10 + .checkbox + = f.label :confidential do + = f.check_box :confidential + This issue is confidential and should only be visible to team members + - if can?(current_user, :"admin_#{issuable.to_ability_name}", issuable.project) %hr .form-group diff --git a/app/views/shared/issuable/_label_dropdown.html.haml b/app/views/shared/issuable/_label_dropdown.html.haml new file mode 100644 index 00000000000..87617315181 --- /dev/null +++ b/app/views/shared/issuable/_label_dropdown.html.haml @@ -0,0 +1,39 @@ +- if params[:label_name] + = hidden_field_tag(:label_name, params[:label_name]) +.dropdown + %button.dropdown-menu-toggle.js-label-select.js-filter-submit{type: "button", data: {toggle: "dropdown", field_name: "label_name", show_no: "true", show_any: "true", selected: params[:label_name], project_id: @project.try(:id), labels: labels_filter_path, default_label: "Label"}} + %span.dropdown-toggle-text + = h(params[:label_name].presence || "Label") + = icon('chevron-down') + .dropdown-menu.dropdown-select.dropdown-menu-paging.dropdown-menu-labels.dropdown-menu-selectable + .dropdown-page-one + = dropdown_title("Filter by label") + = dropdown_filter("Search labels") + = dropdown_content + - if @project + = dropdown_footer do + %ul.dropdown-footer-list + - if can? current_user, :admin_label, @project + %li + %a.dropdown-toggle-page{href: "#"} + Create new + %li + = link_to namespace_project_labels_path(@project.namespace, @project) do + - if can? current_user, :admin_label, @project + Manage labels + - else + View labels + - if can? current_user, :admin_label, @project and @project + .dropdown-page-two + = dropdown_title("Create new label", back: true) + = dropdown_content do + %input#new_label_color{type: "hidden"} + %input#new_label_name.dropdown-input-field{type: "text", placeholder: "Name new label"} + .dropdown-label-color-preview.js-dropdown-label-color-preview + .suggest-colors.suggest-colors-dropdown + - suggested_colors.each do |color| + = link_to '#', style: "background-color: #{color}", data: { color: color } do +   + %button.btn.btn-primary.js-new-label-btn{type: "button"} + Create + = dropdown_loading diff --git a/app/views/shared/issuable/_milestone_dropdown.html.haml b/app/views/shared/issuable/_milestone_dropdown.html.haml new file mode 100644 index 00000000000..0434506c8d7 --- /dev/null +++ b/app/views/shared/issuable/_milestone_dropdown.html.haml @@ -0,0 +1,16 @@ +- if params[:milestone_title] + = hidden_field_tag(:milestone_title, params[:milestone_title]) += dropdown_tag(h(params[:milestone_title].presence || "Milestone"), options: { title: "Filter by milestone", toggle_class: 'js-milestone-select js-filter-submit', filter: true, dropdown_class: "dropdown-menu-selectable", + placeholder: "Search milestones", footer_content: @project.present?, data: { show_no: true, show_any: true, field_name: "milestone_title", selected: params[:milestone_title], project_id: @project.try(:id), milestones: milestones_filter_dropdown_path, default_label: "Milestone" } }) do + - if @project + %ul.dropdown-footer-list + - if can? current_user, :admin_milestone, @project + %li + = link_to new_namespace_project_milestone_path(@project.namespace, @project), title: "New Milestone" do + Create new + %li + = link_to namespace_project_milestones_path(@project.namespace, @project) do + - if can? current_user, :admin_milestone, @project + Manage milestones + - else + View milestones diff --git a/app/views/shared/issuable/_participants.html.haml b/app/views/shared/issuable/_participants.html.haml index f1d92ef48b2..3fb409ff727 100644 --- a/app/views/shared/issuable/_participants.html.haml +++ b/app/views/shared/issuable/_participants.html.haml @@ -1,3 +1,6 @@ +- participants_row = 7 +- participants_size = participants.size +- participants_extra = participants_size - participants_row .block.participants .sidebar-collapsed-icon = icon('users') @@ -5,6 +8,13 @@ = participants.count .title.hide-collapsed = pluralize participants.count, "participant" - - participants.each do |participant| - %span.hide-collapsed - = link_to_member(@project, participant, name: false, size: 24) + .hide-collapsed.participants-list + - participants.each do |participant| + .participants-author.js-participants-author + = link_to_member(@project, participant, name: false, size: 24) + - if participants_extra > 0 + %div.participants-more + %a.js-participants-more{href: "#", data: {original_text: "+ #{participants_size - 7} more", less_text: "- show less"}} + + #{participants_extra} more +:javascript + Issue.prototype.PARTICIPANTS_ROW_COUNT = #{participants_row}; diff --git a/app/views/shared/issuable/_sidebar.html.haml b/app/views/shared/issuable/_sidebar.html.haml index 23b1ed1e51b..2b95b19facc 100644 --- a/app/views/shared/issuable/_sidebar.html.haml +++ b/app/views/shared/issuable/_sidebar.html.haml @@ -1,13 +1,12 @@ %aside.right-sidebar{ class: sidebar_gutter_collapsed_class } .issuable-sidebar - .block + .block.issuable-sidebar-header %span.issuable-count.hide-collapsed.pull-left = issuable.iid of = issuables_count(issuable) - %span.pull-right - %a.gutter-toggle.js-sidebar-toggle{href: '#'} - = sidebar_gutter_toggle_icon + %a.gutter-toggle.pull-right.js-sidebar-toggle{href: '#'} + = sidebar_gutter_toggle_icon .issuable-nav.hide-collapsed.pull-right.btn-group{role: 'group', "aria-label" => '...'} - if prev_issuable = prev_issuable_for(issuable) = link_to 'Prev', [@project.namespace.becomes(Namespace), @project, prev_issuable], class: 'btn btn-default prev-btn' @@ -22,20 +21,20 @@ = form_for [@project.namespace.becomes(Namespace), @project, issuable], remote: true, html: {class: 'issuable-context-form inline-update js-issuable-update'} do |f| .block.assignee - .sidebar-collapsed-icon + .sidebar-collapsed-icon.sidebar-collapsed-user{data: {toggle: "tooltip", placement: "left", container: "body"}, title: (issuable.assignee.to_reference if issuable.assignee)} - if issuable.assignee - = link_to_member_avatar(issuable.assignee, size: 24) + = link_to_member(@project, issuable.assignee, size: 24) - else = icon('user') .title.hide-collapsed - %label - Assignee + Assignee - if can?(current_user, :"admin_#{issuable.to_ability_name}", @project) - .pull-right - = link_to 'Edit', '#', class: 'edit-link' - .value.hide-collapsed + = link_to 'Edit', '#', class: 'edit-link pull-right' + .value.bold.hide-collapsed - if issuable.assignee - %strong= link_to_member(@project, issuable.assignee, size: 24) + = link_to_member(@project, issuable.assignee, size: 32) do + %span.username + = issuable.assignee.to_reference - if issuable.instance_of?(MergeRequest) && !issuable.can_be_merged_by?(issuable.assignee) %a.pull-right.cannot-be-merged{href: '#', data: {toggle: 'tooltip'}, title: 'Not allowed to merge'} = icon('exclamation-triangle') @@ -54,18 +53,13 @@ - else No .title.hide-collapsed - %label - Milestone + Milestone - if can?(current_user, :"admin_#{issuable.to_ability_name}", @project) - .pull-right - = link_to 'Edit', '#', class: 'edit-link' - .value.hide-collapsed + = link_to 'Edit', '#', class: 'edit-link pull-right' + .value.bold.hide-collapsed - if issuable.milestone - %span.back-to-milestone - = link_to namespace_project_milestone_path(@project.namespace, @project, issuable.milestone) do - %strong - = icon('clock-o') - = issuable.milestone.title + = link_to namespace_project_milestone_path(@project.namespace, @project, issuable.milestone) do + = issuable.milestone.title - else .light None .selectbox.hide-collapsed @@ -80,11 +74,10 @@ %span = issuable.labels.count .title.hide-collapsed - %label Labels + Labels - if can?(current_user, :"admin_#{issuable.to_ability_name}", @project) - .pull-right - = link_to 'Edit', '#', class: 'edit-link' - .value.issuable-show-labels.hide-collapsed + = link_to 'Edit', '#', class: 'edit-link pull-right' + .value.issuable-show-labels.hide-collapsed{class: ("has-labels" if issuable.labels.any?)} - if issuable.labels.any? - issuable.labels.each do |label| = link_to_label(label, type: issuable.to_ability_name) @@ -95,14 +88,13 @@ { selected: issuable.label_ids }, multiple: true, class: 'select2 js-select2', data: { placeholder: "Select labels" } = render "shared/issuable/participants", participants: issuable.participants(current_user) - %hr - if current_user - subscribed = issuable.subscribed?(current_user) .block.light.subscription{data: {url: toggle_subscription_path(issuable)}} .sidebar-collapsed-icon = icon('rss') .title.hide-collapsed - %label.light Notifications + Notifications - subscribtion_status = subscribed ? 'subscribed' : 'unsubscribed' %button.btn.btn-block.btn-gray.subscribe-button.hide-collapsed{:type => 'button'} %span= subscribed ? 'Unsubscribe' : 'Subscribe' diff --git a/app/views/shared/milestones/_issuable.html.haml b/app/views/shared/milestones/_issuable.html.haml index f7c6fc14adf..85888096722 100644 --- a/app/views/shared/milestones/_issuable.html.haml +++ b/app/views/shared/milestones/_issuable.html.haml @@ -10,6 +10,8 @@ %strong #{project.name} · - elsif show_full_project_name %strong #{project.name_with_namespace} · + - if issuable.is_a?(Issue) + = confidential_icon(issuable) = link_to_gfm issuable.title, [project.namespace.becomes(Namespace), project, issuable], title: issuable.title %div{class: 'issuable-detail'} = link_to [project.namespace.becomes(Namespace), project, issuable] do diff --git a/app/views/shared/milestones/_labels_tab.html.haml b/app/views/shared/milestones/_labels_tab.html.haml index ba27bafd1bc..868b2357003 100644 --- a/app/views/shared/milestones/_labels_tab.html.haml +++ b/app/views/shared/milestones/_labels_tab.html.haml @@ -5,7 +5,7 @@ %li %span.label-row = link_to milestones_label_path(options) do - - render_colored_label(label) + - render_colored_label(label, tooltip: false) %span.prepend-left-10 = markdown(label.description, pipeline: :single_line) diff --git a/app/views/shared/milestones/_milestone.html.haml b/app/views/shared/milestones/_milestone.html.haml index f01138af3f0..6b25745c554 100644 --- a/app/views/shared/milestones/_milestone.html.haml +++ b/app/views/shared/milestones/_milestone.html.haml @@ -6,10 +6,10 @@ .col-sm-6 %strong= link_to_gfm truncate(milestone.title, length: 100), milestone_path .col-sm-6 - .pull-right.light #{milestone.percent_complete}% complete + .pull-right.light #{milestone.percent_complete(current_user)}% complete .row .col-sm-6 - = link_to pluralize(milestone.issues.size, 'Issue'), issues_path + = link_to pluralize(milestone.issues_visible_to_user(current_user).size, 'Issue'), issues_path · = link_to pluralize(milestone.merge_requests.size, 'Merge Request'), merge_requests_path .col-sm-6= milestone_progress_bar(milestone) diff --git a/app/views/shared/milestones/_summary.html.haml b/app/views/shared/milestones/_summary.html.haml index 59d4ae29f79..385c6596606 100644 --- a/app/views/shared/milestones/_summary.html.haml +++ b/app/views/shared/milestones/_summary.html.haml @@ -3,15 +3,15 @@ .context.prepend-top-default .milestone-summary %h4 Progress - %strong= milestone.issues.size + %strong= milestone.issues_visible_to_user(current_user).size issues: %span.milestone-stat - %strong= milestone.issues.opened.size + %strong= milestone.issues_visible_to_user(current_user).opened.size open and - %strong= milestone.issues.closed.size + %strong= milestone.issues_visible_to_user(current_user).closed.size closed %span.milestone-stat - %strong== #{milestone.percent_complete}% + %strong== #{milestone.percent_complete(current_user)}% complete %span.milestone-stat diff --git a/app/views/shared/milestones/_tabs.html.haml b/app/views/shared/milestones/_tabs.html.haml index 57d7ee85a3b..2b6ce2d7e7a 100644 --- a/app/views/shared/milestones/_tabs.html.haml +++ b/app/views/shared/milestones/_tabs.html.haml @@ -2,7 +2,7 @@ %li.active = link_to '#tab-issues', 'data-toggle' => 'tab', 'data-show' => '.tab-issues-buttons' do Issues - %span.badge= milestone.issues.size + %span.badge= milestone.issues_visible_to_user(current_user).size %li = link_to '#tab-merge-requests', 'data-toggle' => 'tab', 'data-show' => '.tab-merge-requests-buttons' do Merge Requests @@ -21,7 +21,7 @@ .tab-content.milestone-content .tab-pane.active#tab-issues - = render 'shared/milestones/issues_tab', issues: milestone.issues, show_project_name: show_project_name, show_full_project_name: show_full_project_name + = render 'shared/milestones/issues_tab', issues: milestone.issues_visible_to_user(current_user), show_project_name: show_project_name, show_full_project_name: show_full_project_name .tab-pane#tab-merge-requests = render 'shared/milestones/merge_requests_tab', merge_requests: milestone.merge_requests, show_project_name: show_project_name, show_full_project_name: show_full_project_name .tab-pane#tab-participants diff --git a/app/views/shared/milestones/_top.html.haml b/app/views/shared/milestones/_top.html.haml index 4cf1d948b5b..cab8743a077 100644 --- a/app/views/shared/milestones/_top.html.haml +++ b/app/views/shared/milestones/_top.html.haml @@ -28,7 +28,7 @@ %h2.title = markdown escape_once(milestone.title), pipeline: :single_line -- if milestone.complete? && milestone.active? +- if milestone.complete?(current_user) && milestone.active? .alert.alert-success.prepend-top-default - close_msg = group ? 'You may close the milestone now.' : 'Navigate to the project to close the milestone.' %span All issues for this milestone are closed. #{close_msg} @@ -47,7 +47,7 @@ - project_name = group ? ms.project.name : ms.project.name_with_namespace = link_to project_name, namespace_project_milestone_path(ms.project.namespace, ms.project, ms) %td - = ms.issues.opened.count + = ms.issues_visible_to_user(current_user).opened.count %td - if ms.closed? Closed diff --git a/app/views/shared/projects/_project.html.haml b/app/views/shared/projects/_project.html.haml index 97cfb76cdb0..872d2bdf46d 100644 --- a/app/views/shared/projects/_project.html.haml +++ b/app/views/shared/projects/_project.html.haml @@ -7,26 +7,11 @@ - show_last_commit_as_description = false unless local_assigns[:show_last_commit_as_description] == true && project.commit - css_class += " no-description" if project.description.blank? && !show_last_commit_as_description - ci_commit = project.ci_commit(project.commit.sha) if ci && !project.empty_repo? && project.commit -- cache_key = [project.namespace, project, controller.controller_name, controller.action_name, current_application_settings, 'v2.2'] +- cache_key = [project.namespace, project, controller.controller_name, controller.action_name, current_application_settings, 'v2.3'] - cache_key.push(ci_commit.status) if ci_commit %li.project-row{ class: css_class } = cache(cache_key) do - = link_to project_path(project), class: dom_class(project) do - - if avatar - .dash-project-avatar - - if use_creator_avatar - = image_tag avatar_icon(project.creator.email, 40), class: "avatar s40", alt:'' - - else - = project_icon(project, alt: '', class: 'avatar project-avatar s40') - %span.project-full-name.title - %span.namespace-name - - if project.namespace && !skip_namespace - = project.namespace.human_name - \/ - %span.project-name.filter-title - = project.name - .controls - if project.main_language %span @@ -45,6 +30,23 @@ %span.visibility-icon.has_tooltip{data: { container: 'body', placement: 'left' }, title: "#{visibility_level_label(project.visibility_level)} - #{project_visibility_level_description(project.visibility_level)}"} = visibility_level_icon(project.visibility_level, fw: false) + + .title + = link_to project_path(project), class: dom_class(project) do + - if avatar + .dash-project-avatar + - if use_creator_avatar + = image_tag avatar_icon(project.creator.email, 40), class: "avatar s40", alt:'' + - else + = project_icon(project, alt: '', class: 'avatar project-avatar s40') + %span.project-full-name + %span.namespace-name + - if project.namespace && !skip_namespace + = project.namespace.human_name + \/ + %span.project-name.filter-title + = project.name + - if show_last_commit_as_description .description = link_to_gfm project.commit.title, namespace_project_commit_path(project.namespace, project, project.commit), diff --git a/app/views/shared/snippets/_snippet.html.haml b/app/views/shared/snippets/_snippet.html.haml index a316a085107..c96dfefe17f 100644 --- a/app/views/shared/snippets/_snippet.html.haml +++ b/app/views/shared/snippets/_snippet.html.haml @@ -1,8 +1,8 @@ %li.snippet-row = image_tag avatar_icon(snippet.author_email), class: "avatar s40 hidden-xs", alt: '' - .snippet-title - = link_to reliable_snippet_path(snippet), class: 'title' do + .title + = link_to reliable_snippet_path(snippet) do = truncate(snippet.title, length: 60) - if snippet.private? %span.label.label-gray diff --git a/app/workers/delete_user_worker.rb b/app/workers/delete_user_worker.rb new file mode 100644 index 00000000000..6ff361e4d80 --- /dev/null +++ b/app/workers/delete_user_worker.rb @@ -0,0 +1,10 @@ +class DeleteUserWorker + include Sidekiq::Worker + + def perform(current_user_id, delete_user_id, options = {}) + delete_user = User.find(delete_user_id) + current_user = User.find(current_user_id) + + DeleteUserService.new(current_user).execute(delete_user, options.symbolize_keys) + end +end diff --git a/app/workers/gitlab_shell_one_shot_worker.rb b/app/workers/gitlab_shell_one_shot_worker.rb new file mode 100644 index 00000000000..4ddbcf574d5 --- /dev/null +++ b/app/workers/gitlab_shell_one_shot_worker.rb @@ -0,0 +1,10 @@ +class GitlabShellOneShotWorker + include Sidekiq::Worker + include Gitlab::ShellAdapter + + sidekiq_options queue: :gitlab_shell, retry: false + + def perform(action, *arg) + gitlab_shell.send(action, *arg) + end +end diff --git a/app/workers/post_receive.rb b/app/workers/post_receive.rb index 14d7813412e..3cc232ef1ae 100644 --- a/app/workers/post_receive.rb +++ b/app/workers/post_receive.rb @@ -1,6 +1,5 @@ class PostReceive include Sidekiq::Worker - include Gitlab::Identifier sidekiq_options queue: :post_receive @@ -11,51 +10,44 @@ class PostReceive log("Check gitlab.yml config for correct gitlab_shell.repos_path variable. \"#{Gitlab.config.gitlab_shell.repos_path}\" does not match \"#{repo_path}\"") end - repo_path.gsub!(/\.git\z/, "") - repo_path.gsub!(/\A\//, "") + post_received = Gitlab::GitPostReceive.new(repo_path, identifier, changes) - project = Project.find_with_namespace(repo_path) - - if project.nil? + if post_received.project.nil? log("Triggered hook for non-existing project with full path \"#{repo_path} \"") return false end - changes = Base64.decode64(changes) unless changes.include?(" ") - changes = utf8_encode_changes(changes) - changes = changes.lines + if post_received.wiki? + # Nothing defined here yet. + elsif post_received.regular_project? + process_project_changes(post_received) + else + log("Triggered hook for unidentifiable repository type with full path \"#{repo_path} \"") + false + end + end - changes.each do |change| + def process_project_changes(post_received) + post_received.changes.each do |change| oldrev, newrev, ref = change.strip.split(' ') - @user ||= identify(identifier, project, newrev) + @user ||= post_received.identify(newrev) unless @user - log("Triggered hook for non-existing user \"#{identifier} \"") + log("Triggered hook for non-existing user \"#{post_received.identifier} \"") return false end if Gitlab::Git.tag_ref?(ref) - GitTagPushService.new.execute(project, @user, oldrev, newrev, ref) + GitTagPushService.new.execute(post_received.project, @user, oldrev, newrev, ref) else - GitPushService.new(project, @user, oldrev: oldrev, newrev: newrev, ref: ref).execute + GitPushService.new(post_received.project, @user, oldrev: oldrev, newrev: newrev, ref: ref).execute end end end - def utf8_encode_changes(changes) - changes = changes.dup - - changes.force_encoding("UTF-8") - return changes if changes.valid_encoding? - - # Convert non-UTF-8 branch/tag names to UTF-8 so they can be dumped as JSON. - detection = CharlockHolmes::EncodingDetector.detect(changes) - return changes unless detection && detection[:encoding] - - CharlockHolmes::Converter.convert(changes, detection[:encoding], 'UTF-8') - end - + private + def log(message) Gitlab::GitLogger.error("POST-RECEIVE: #{message}") end |