Commit ae7b2ef6 authored by Douwe Maan's avatar Douwe Maan

Merge branch 'master' into issue_12658

# Conflicts:
#	app/models/issue.rb
#	app/views/projects/_home_panel.html.haml
#	app/views/shared/projects/_project.html.haml
#	db/schema.rb
#	spec/models/project_spec.rb
parents 8d544645 0305dd98
This diff is collapsed.
Please view this file on the master branch, on stable branches it's out of date. Please view this file on the master branch, on stable branches it's out of date.
v 8.6.0 (unreleased) v 8.6.0 (unreleased)
- Add ability to move issue to another project
- Prevent tokens in the import URL to be showed by the UI
- Fix bug where wrong commit ID was being used in a merge request diff to show old image (Stan Hu) - Fix bug where wrong commit ID was being used in a merge request diff to show old image (Stan Hu)
- Make HTTP(s) label consistent on clone bar (Stan Hu)
- Add confidential issues - Add confidential issues
- Bump gitlab_git to 9.0.3 (Stan Hu) - Bump gitlab_git to 9.0.3 (Stan Hu)
- Fix diff image view modes (2-up, swipe, onion skin) not working (Stan Hu)
- Support Golang subpackage fetching (Stan Hu) - Support Golang subpackage fetching (Stan Hu)
- Bump Capybara gem to 2.6.2 (Stan Hu) - Bump Capybara gem to 2.6.2 (Stan Hu)
- New branch button appears on issues where applicable - New branch button appears on issues where applicable
...@@ -57,6 +61,7 @@ v 8.6.0 (unreleased) ...@@ -57,6 +61,7 @@ v 8.6.0 (unreleased)
- User deletion is now done in the background so the request can not time out - User deletion is now done in the background so the request can not time out
- Canceled builds are now ignored in compound build status if marked as `allowed to fail` - Canceled builds are now ignored in compound build status if marked as `allowed to fail`
- Trigger a todo for mentions on commits page - Trigger a todo for mentions on commits page
- Let project owners and admins soft delete issues and merge requests
v 8.5.8 v 8.5.8
- Bump Git version requirement to 2.7.4 - Bump Git version requirement to 2.7.4
......
...@@ -222,6 +222,8 @@ gem 'net-ssh', '~> 3.0.1' ...@@ -222,6 +222,8 @@ gem 'net-ssh', '~> 3.0.1'
# Sentry integration # Sentry integration
gem 'sentry-raven', '~> 0.15' gem 'sentry-raven', '~> 0.15'
gem 'premailer-rails', '~> 1.9.0'
# Metrics # Metrics
group :metrics do group :metrics do
gem 'allocations', '~> 1.0', require: false, platform: :mri gem 'allocations', '~> 1.0', require: false, platform: :mri
...@@ -285,7 +287,7 @@ group :development, :test do ...@@ -285,7 +287,7 @@ group :development, :test do
gem 'spring-commands-spinach', '~> 1.0.0' gem 'spring-commands-spinach', '~> 1.0.0'
gem 'spring-commands-teaspoon', '~> 0.0.2' gem 'spring-commands-teaspoon', '~> 0.0.2'
gem 'rubocop', '~> 0.35.0', require: false gem 'rubocop', '~> 0.38.0', require: false
gem 'scss_lint', '~> 0.47.0', require: false gem 'scss_lint', '~> 0.47.0', require: false
gem 'coveralls', '~> 0.8.2', require: false gem 'coveralls', '~> 0.8.2', require: false
gem 'simplecov', '~> 0.10.0', require: false gem 'simplecov', '~> 0.10.0', require: false
......
...@@ -61,9 +61,7 @@ GEM ...@@ -61,9 +61,7 @@ GEM
faraday_middleware-multi_json (~> 0.0) faraday_middleware-multi_json (~> 0.0)
oauth2 (~> 1.0) oauth2 (~> 1.0)
asciidoctor (1.5.3) asciidoctor (1.5.3)
ast (2.1.0) ast (2.2.0)
astrolabe (1.3.1)
parser (~> 2.2)
attr_encrypted (1.3.4) attr_encrypted (1.3.4)
encryptor (>= 1.3.0) encryptor (>= 1.3.0)
attr_required (1.0.0) attr_required (1.0.0)
...@@ -150,6 +148,8 @@ GEM ...@@ -150,6 +148,8 @@ GEM
crack (0.4.3) crack (0.4.3)
safe_yaml (~> 1.0.0) safe_yaml (~> 1.0.0)
creole (0.5.0) creole (0.5.0)
css_parser (1.3.7)
addressable
d3_rails (3.5.11) d3_rails (3.5.11)
railties (>= 3.1.0) railties (>= 3.1.0)
daemons (1.2.3) daemons (1.2.3)
...@@ -423,6 +423,7 @@ GEM ...@@ -423,6 +423,7 @@ GEM
haml (~> 4.0.0) haml (~> 4.0.0)
nokogiri (~> 1.6.0) nokogiri (~> 1.6.0)
ruby_parser (~> 3.5) ruby_parser (~> 3.5)
htmlentities (4.3.4)
http-cookie (1.0.2) http-cookie (1.0.2)
domain_name (~> 0.5) domain_name (~> 0.5)
http_parser.rb (0.5.3) http_parser.rb (0.5.3)
...@@ -554,8 +555,8 @@ GEM ...@@ -554,8 +555,8 @@ GEM
orm_adapter (0.5.0) orm_adapter (0.5.0)
paranoia (2.1.4) paranoia (2.1.4)
activerecord (~> 4.0) activerecord (~> 4.0)
parser (2.2.3.0) parser (2.3.0.6)
ast (>= 1.1, < 3.0) ast (~> 2.2)
pg (0.18.4) pg (0.18.4)
poltergeist (1.9.0) poltergeist (1.9.0)
capybara (~> 2.1) capybara (~> 2.1)
...@@ -564,6 +565,12 @@ GEM ...@@ -564,6 +565,12 @@ GEM
websocket-driver (>= 0.2.0) websocket-driver (>= 0.2.0)
posix-spawn (0.3.11) posix-spawn (0.3.11)
powerpack (0.1.1) powerpack (0.1.1)
premailer (1.8.6)
css_parser (>= 1.3.6)
htmlentities (>= 4.0.0)
premailer-rails (1.9.0)
actionmailer (>= 3, < 5)
premailer (~> 1.7, >= 1.7.9)
pry (0.10.3) pry (0.10.3)
coderay (~> 1.1.0) coderay (~> 1.1.0)
method_source (~> 0.8.1) method_source (~> 0.8.1)
...@@ -615,7 +622,7 @@ GEM ...@@ -615,7 +622,7 @@ GEM
activesupport (= 4.2.5.2) activesupport (= 4.2.5.2)
rake (>= 0.8.7) rake (>= 0.8.7)
thor (>= 0.18.1, < 2.0) thor (>= 0.18.1, < 2.0)
rainbow (2.0.0) rainbow (2.1.0)
raindrops (0.15.0) raindrops (0.15.0)
rake (10.5.0) rake (10.5.0)
raphael-rails (2.1.2) raphael-rails (2.1.2)
...@@ -687,13 +694,12 @@ GEM ...@@ -687,13 +694,12 @@ GEM
rspec-retry (0.4.5) rspec-retry (0.4.5)
rspec-core rspec-core
rspec-support (3.3.0) rspec-support (3.3.0)
rubocop (0.35.1) rubocop (0.38.0)
astrolabe (~> 1.3) parser (>= 2.3.0.6, < 3.0)
parser (>= 2.2.3.0, < 3.0)
powerpack (~> 0.1) powerpack (~> 0.1)
rainbow (>= 1.99.1, < 3.0) rainbow (>= 1.99.1, < 3.0)
ruby-progressbar (~> 1.7) ruby-progressbar (~> 1.7)
tins (<= 1.6.0) unicode-display_width (~> 1.0, >= 1.0.1)
ruby-fogbugz (0.2.1) ruby-fogbugz (0.2.1)
crack (~> 0.4) crack (~> 0.4)
ruby-progressbar (1.7.5) ruby-progressbar (1.7.5)
...@@ -843,6 +849,7 @@ GEM ...@@ -843,6 +849,7 @@ GEM
unf (0.1.4) unf (0.1.4)
unf_ext unf_ext
unf_ext (0.0.7.1) unf_ext (0.0.7.1)
unicode-display_width (1.0.2)
unicorn (4.9.0) unicorn (4.9.0)
kgio (~> 2.6) kgio (~> 2.6)
rack rack
...@@ -992,6 +999,7 @@ DEPENDENCIES ...@@ -992,6 +999,7 @@ DEPENDENCIES
paranoia (~> 2.0) paranoia (~> 2.0)
pg (~> 0.18.2) pg (~> 0.18.2)
poltergeist (~> 1.9.0) poltergeist (~> 1.9.0)
premailer-rails (~> 1.9.0)
pry-rails pry-rails
quiet_assets (~> 1.0.2) quiet_assets (~> 1.0.2)
rack-attack (~> 4.3.1) rack-attack (~> 4.3.1)
...@@ -1013,7 +1021,7 @@ DEPENDENCIES ...@@ -1013,7 +1021,7 @@ DEPENDENCIES
rqrcode-rails3 (~> 0.1.7) rqrcode-rails3 (~> 0.1.7)
rspec-rails (~> 3.3.0) rspec-rails (~> 3.3.0)
rspec-retry rspec-retry
rubocop (~> 0.35.0) rubocop (~> 0.38.0)
ruby-fogbugz (~> 0.2.1) ruby-fogbugz (~> 0.2.1)
sanitize (~> 2.0) sanitize (~> 2.0)
sass-rails (~> 5.0.0) sass-rails (~> 5.0.0)
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
#= require jquery #= require jquery
#= require jquery-ui/autocomplete #= require jquery-ui/autocomplete
#= require jquery-ui/datepicker #= require jquery-ui/datepicker
#= require jquery-ui/draggable
#= require jquery-ui/effect-highlight #= require jquery-ui/effect-highlight
#= require jquery-ui/sortable #= require jquery-ui/sortable
#= require jquery_ujs #= require jquery_ujs
...@@ -138,7 +139,7 @@ $ -> ...@@ -138,7 +139,7 @@ $ ->
# Initialize tooltips # Initialize tooltips
$('body').tooltip( $('body').tooltip(
selector: '.has_tooltip, [data-toggle="tooltip"]' selector: '.has-tooltip, [data-toggle="tooltip"]'
placement: (_, el) -> placement: (_, el) ->
$el = $(el) $el = $(el)
$el.data('placement') || 'bottom' $el.data('placement') || 'bottom'
......
...@@ -5,7 +5,6 @@ class @Aside ...@@ -5,7 +5,6 @@ class @Aside
e.preventDefault() e.preventDefault()
btn = $(e.currentTarget) btn = $(e.currentTarget)
icon = btn.find('i') icon = btn.find('i')
console.log('1')
if icon.hasClass('fa-angle-left') if icon.hasClass('fa-angle-left')
btn.parent().find('section').hide() btn.parent().find('section').hide()
......
...@@ -122,7 +122,7 @@ class @AwardsHandler ...@@ -122,7 +122,7 @@ class @AwardsHandler
nodes = [] nodes = []
nodes.push( nodes.push(
"<button class='btn award-control js-emoji-btn has_tooltip active' title='me'>", "<button class='btn award-control js-emoji-btn has-tooltip active' title='me'>",
"<div class='icon emoji-icon #{emojiCssClass}' data-emoji='#{emoji}'></div>", "<div class='icon emoji-icon #{emojiCssClass}' data-emoji='#{emoji}'></div>",
"<span class='award-control-text js-counter'>1</span>", "<span class='award-control-text js-counter'>1</span>",
"</button>" "</button>"
......
...@@ -167,7 +167,11 @@ class GitLabDropdown ...@@ -167,7 +167,11 @@ class GitLabDropdown
hidden: => hidden: =>
if @options.filterable if @options.filterable
@dropdown.find(".dropdown-input-field").blur().val("") @dropdown
.find(".dropdown-input-field")
.blur()
.val("")
.trigger("keyup")
if @dropdown.find(".dropdown-toggle-page").length if @dropdown.find(".dropdown-toggle-page").length
$('.dropdown-menu', @dropdown).removeClass PAGE_TWO_CLASS $('.dropdown-menu', @dropdown).removeClass PAGE_TWO_CLASS
......
class @IssuableForm class @IssuableForm
issueMoveConfirmMsg: 'Are you sure you want to move this issue to another project?'
wipRegex: /^\s*(\[WIP\]\s*|WIP:\s*|WIP\s+)+\s*/i wipRegex: /^\s*(\[WIP\]\s*|WIP:\s*|WIP\s+)+\s*/i
constructor: (@form) -> constructor: (@form) ->
GitLab.GfmAutoComplete.setup() GitLab.GfmAutoComplete.setup()
new UsersSelect() new UsersSelect()
...@@ -7,12 +9,13 @@ class @IssuableForm ...@@ -7,12 +9,13 @@ class @IssuableForm
@titleField = @form.find("input[name*='[title]']") @titleField = @form.find("input[name*='[title]']")
@descriptionField = @form.find("textarea[name*='[description]']") @descriptionField = @form.find("textarea[name*='[description]']")
@issueMoveField = @form.find("#move_to_project_id")
return unless @titleField.length && @descriptionField.length return unless @titleField.length && @descriptionField.length
@initAutosave() @initAutosave()
@form.on "submit", @resetAutosave @form.on "submit", @handleSubmit
@form.on "click", ".btn-cancel", @resetAutosave @form.on "click", ".btn-cancel", @resetAutosave
@initWip() @initWip()
...@@ -30,6 +33,12 @@ class @IssuableForm ...@@ -30,6 +33,12 @@ class @IssuableForm
"description" "description"
] ]
handleSubmit: =>
if (parseInt(@issueMoveField?.val()) ? 0) > 0
return false unless confirm(@issueMoveConfirmMsg)
@resetAutosave()
resetAutosave: => resetAutosave: =>
@titleField.data("autosave").reset() @titleField.data("autosave").reset()
@descriptionField.data("autosave").reset() @descriptionField.data("autosave").reset()
......
...@@ -3,6 +3,8 @@ ...@@ -3,6 +3,8 @@
# Handles persisting and restoring the current tab selection and lazily-loading # Handles persisting and restoring the current tab selection and lazily-loading
# content on the MergeRequests#show page. # content on the MergeRequests#show page.
# #
#= require jquery.cookie
#
# ### Example Markup # ### Example Markup
# #
# <ul class="nav-links merge-request-tabs"> # <ul class="nav-links merge-request-tabs">
...@@ -68,11 +70,15 @@ class @MergeRequestTabs ...@@ -68,11 +70,15 @@ class @MergeRequestTabs
if action == 'commits' if action == 'commits'
@loadCommits($target.attr('href')) @loadCommits($target.attr('href'))
@expandView()
else if action == 'diffs' else if action == 'diffs'
@loadDiff($target.attr('href')) @loadDiff($target.attr('href'))
@shrinkView() @shrinkView()
else if action == 'builds' else if action == 'builds'
@loadBuilds($target.attr('href')) @loadBuilds($target.attr('href'))
@expandView()
else
@expandView()
@setCurrentAction(action) @setCurrentAction(action)
...@@ -189,11 +195,24 @@ class @MergeRequestTabs ...@@ -189,11 +195,24 @@ class @MergeRequestTabs
$('.container-fluid').removeClass('container-limited') $('.container-fluid').removeClass('container-limited')
shrinkView: -> shrinkView: ->
$gutterIcon = $('.js-sidebar-toggle i') $gutterIcon = $('.js-sidebar-toggle i:visible')
# Wait until listeners are set # Wait until listeners are set
setTimeout( -> setTimeout( ->
# Only when sidebar is collapsed # Only when sidebar is expanded
if $gutterIcon.is('.fa-angle-double-right') if $gutterIcon.is('.fa-angle-double-right')
$gutterIcon.closest('a').trigger('click',[true]) $gutterIcon.closest('a').trigger('click', [true])
, 0)
# Expand the issuable sidebar unless the user explicitly collapsed it
expandView: ->
return if $.cookie('collapsed_gutter') == 'true'
$gutterIcon = $('.js-sidebar-toggle i:visible')
# Wait until listeners are set
setTimeout( ->
# Only when sidebar is collapsed
if $gutterIcon.is('.fa-angle-double-left')
$gutterIcon.closest('a').trigger('click', [true])
, 0) , 0)
...@@ -361,14 +361,12 @@ class @Notes ...@@ -361,14 +361,12 @@ class @Notes
showEditForm: (e) -> showEditForm: (e) ->
e.preventDefault() e.preventDefault()
note = $(this).closest(".note") note = $(this).closest(".note")
note.find(".note-body > .note-text").hide() note.addClass "is-editting"
note.find(".note-header").hide()
form = note.find(".note-edit-form") form = note.find(".note-edit-form")
isNewForm = form.is(':not(.gfm-form)') isNewForm = form.is(':not(.gfm-form)')
if isNewForm if isNewForm
form.addClass('gfm-form') form.addClass('gfm-form')
form.addClass('current-note-edit-form') form.addClass('current-note-edit-form')
form.show()
# Show the attachment delete link # Show the attachment delete link
note.find(".js-note-attachment-delete").show() note.find(".js-note-attachment-delete").show()
...@@ -402,11 +400,9 @@ class @Notes ...@@ -402,11 +400,9 @@ class @Notes
cancelEdit: (e) -> cancelEdit: (e) ->
e.preventDefault() e.preventDefault()
note = $(this).closest(".note") note = $(this).closest(".note")
note.find(".note-body > .note-text").show() note.removeClass "is-editting"
note.find(".note-header").show()
note.find(".current-note-edit-form") note.find(".current-note-edit-form")
.removeClass("current-note-edit-form") .removeClass("current-note-edit-form")
.hide()
### ###
Called in response to deleting a note of any kind. Called in response to deleting a note of any kind.
......
...@@ -11,7 +11,6 @@ class @Project ...@@ -11,7 +11,6 @@ class @Project
$(@).toggleClass('active') $(@).toggleClass('active')
url = $("#project_clone").val() url = $("#project_clone").val()
console.log("url",url)
# Update the input field # Update the input field
$('#project_clone').val(url) $('#project_clone').val(url)
......
...@@ -16,7 +16,7 @@ ...@@ -16,7 +16,7 @@
} }
&.group-avatar, &.project-avatar, &.avatar-tile { &.group-avatar, &.project-avatar, &.avatar-tile {
@include border-radius(0px); @include border-radius(0);
} }
&.s16 { width: 16px; height: 16px; margin-right: 6px; } &.s16 { width: 16px; height: 16px; margin-right: 6px; }
......
...@@ -107,7 +107,7 @@ ...@@ -107,7 +107,7 @@
margin: 0; margin: 0;
font-size: 23px; font-size: 23px;
font-weight: normal; font-weight: normal;
margin: 16px 0 5px 0; margin: 16px 0 5px;
color: #4c4e54; color: #4c4e54;
font-size: 23px; font-size: 23px;
line-height: 1.1; line-height: 1.1;
......
...@@ -75,7 +75,7 @@ ...@@ -75,7 +75,7 @@
width: 240px; width: 240px;
margin-top: 2px; margin-top: 2px;
margin-bottom: 0; margin-bottom: 0;
padding: 10px 10px; padding: 10px;
font-size: 14px; font-size: 14px;
font-weight: normal; font-weight: normal;
background-color: $dropdown-bg; background-color: $dropdown-bg;
......
...@@ -16,7 +16,7 @@ body { ...@@ -16,7 +16,7 @@ body {
} }
.container .content { .container .content {
margin: 0 0; margin: 0;
} }
.navless-container { .navless-container {
......
/** /**
* Generic mixins * Generic mixins
*/ */
@mixin box-shadow($shadow) { @mixin box-shadow($shadow) {
-webkit-box-shadow: $shadow; -webkit-box-shadow: $shadow;
-moz-box-shadow: $shadow; -moz-box-shadow: $shadow;
-ms-box-shadow: $shadow; -ms-box-shadow: $shadow;
......
...@@ -41,7 +41,7 @@ ...@@ -41,7 +41,7 @@
} }
.select2-drop { .select2-drop {
@include box-shadow(rgba(76, 86, 103, 0.247059) 0px 0px 1px 0px, rgba(31, 37, 50, 0.317647) 0px 2px 18px 0px); @include box-shadow(rgba(76, 86, 103, 0.247059) 0 0 1px 0, rgba(31, 37, 50, 0.317647) 0 2px 18px 0);
@include border-radius ($border-radius-default); @include border-radius ($border-radius-default);
border: none; border: none;
} }
......
...@@ -39,8 +39,8 @@ ...@@ -39,8 +39,8 @@
h1 { h1 {
font-size: 1.3em; font-size: 1.3em;
font-weight: 600; font-weight: 600;
margin: 24px 0 12px 0; margin: 24px 0 12px;
padding: 0 0 10px 0; padding: 0 0 10px;
border-bottom: 1px solid #e7e9ed; border-bottom: 1px solid #e7e9ed;
color: #313236; color: #313236;
} }
...@@ -48,27 +48,27 @@ ...@@ -48,27 +48,27 @@
h2 { h2 {
font-size: 1.2em; font-size: 1.2em;
font-weight: 600; font-weight: 600;
margin: 24px 0 12px 0; margin: 24px 0 12px;
color: #313236; color: #313236;
} }
h3 { h3 {
margin: 24px 0 12px 0; margin: 24px 0 12px;
font-size: 1.1em; font-size: 1.1em;
} }
h4 { h4 {
margin: 24px 0 12px 0; margin: 24px 0 12px;
font-size: 0.98em; font-size: 0.98em;
} }
h5 { h5 {
margin: 24px 0 12px 0; margin: 24px 0 12px;
font-size: 0.95em; font-size: 0.95em;
} }
h6 { h6 {
margin: 24px 0 12px 0; margin: 24px 0 12px;
font-size: 0.90em; font-size: 0.90em;
} }
...@@ -76,7 +76,7 @@ ...@@ -76,7 +76,7 @@
color: #7f8fa4; color: #7f8fa4;
font-size: inherit; font-size: inherit;
padding: 8px 21px; padding: 8px 21px;
margin: 12px 0 12px; margin: 12px 0;
border-left: 3px solid #e7e9ed; border-left: 3px solid #e7e9ed;
} }
...@@ -88,13 +88,13 @@ ...@@ -88,13 +88,13 @@
p { p {
color: #5c5d5e; color: #5c5d5e;
margin: 6px 0 0 0; margin: 6px 0 0;
} }
table { table {
@extend .table; @extend .table;
@extend .table-bordered; @extend .table-bordered;
margin: 12px 0 12px 0; margin: 12px 0;
color: #5c5d5e; color: #5c5d5e;
th { th {
background: #f8fafc; background: #f8fafc;
...@@ -102,7 +102,7 @@ ...@@ -102,7 +102,7 @@
} }
pre { pre {
margin: 12px 0 12px 0; margin: 12px 0;
font-size: 13px; font-size: 13px;
line-height: 1.6em; line-height: 1.6em;
overflow-x: auto; overflow-x: auto;
...@@ -191,7 +191,7 @@ body { ...@@ -191,7 +191,7 @@ body {
line-height: 1.3; line-height: 1.3;
font-size: 1.25em; font-size: 1.25em;
font-weight: 600; font-weight: 600;
margin: 12px 7px 12px 7px; margin: 12px 7px;
} }
h1, h2, h3, h4, h5, h6 { h1, h2, h3, h4, h5, h6 {
......
img {
max-width: 100%;
height: auto;
}
p.details {
font-style:italic;
color:#777
}
.footer p {
font-size:small;
color:#777
}
pre.commit-message {
white-space: pre-wrap;
}
.file-stats a {
text-decoration: none;
}
.file-stats .new-file {
color: #090;
}
.file-stats .deleted-file {
color: #B00;
}
...@@ -132,7 +132,7 @@ ...@@ -132,7 +132,7 @@
} }
.image-info { .image-info {
font-size: 12px; font-size: 12px;
margin: 5px 0 0 0; margin: 5px 0 0;
color: grey; color: grey;
} }
......
...@@ -183,7 +183,7 @@ ...@@ -183,7 +183,7 @@
.block { .block {
width: $sidebar_collapsed_width - 1px; width: $sidebar_collapsed_width - 1px;
margin-left: -19px; margin-left: -19px;
padding: 15px 0 0 0; padding: 15px 0 0;
border-bottom: none; border-bottom: none;
overflow: hidden; overflow: hidden;
} }
...@@ -273,12 +273,12 @@ ...@@ -273,12 +273,12 @@
} }
.participants-list { .participants-list {
margin: -5px -5px; margin: -5px;
} }
.participants-author { .participants-author {
display: inline-block; display: inline-block;
padding: 5px 5px; padding: 5px;
.author_link { .author_link {
display: block; display: block;
......
...@@ -45,7 +45,7 @@ ...@@ -45,7 +45,7 @@
.login-heading h3 { .login-heading h3 {
font-weight: 300; font-weight: 300;
line-height: 1.5; line-height: 1.5;
margin: 0 0 10px 0; margin: 0 0 10px;
} }
.login-footer { .login-footer {
......
...@@ -26,7 +26,7 @@ ...@@ -26,7 +26,7 @@
display: none; display: none;
} }
.new_note, .edit_note { .new_note, .note-edit-form {
.note-form-actions { .note-form-actions {
margin-top: $gl-padding; margin-top: $gl-padding;
} }
......
...@@ -100,6 +100,18 @@ ul.notes { ...@@ -100,6 +100,18 @@ ul.notes {
display: block; display: block;
position: relative; position: relative;
&.is-editting {
.note-header,
.note-text,
.edited-text {
display: none;
}
.note-edit-form {
display: block;
}
}
.note-body { .note-body {
overflow: auto; overflow: auto;
......
...@@ -54,7 +54,7 @@ ...@@ -54,7 +54,7 @@
} }
.account-well { .account-well {
padding: 10px 10px; padding: 10px;
background-color: $help-well-bg; background-color: $help-well-bg;
border: 1px solid $help-well-border; border: 1px solid $help-well-border;
border-radius: $border-radius-base; border-radius: $border-radius-base;
......
...@@ -37,7 +37,7 @@ ...@@ -37,7 +37,7 @@
.dropdown-menu { .dropdown-menu {
left: auto; left: auto;
width: auto; width: auto;
right: 0px; right: 0;
max-width: 240px; max-width: 240px;
} }
} }
...@@ -311,7 +311,7 @@ pre.light-well { ...@@ -311,7 +311,7 @@ pre.light-well {
} }
.git-empty { .git-empty {
margin: 0 7px 0 7px; margin: 0 7px;
h5 { h5 {
color: #5c5d5e; color: #5c5d5e;
......
...@@ -16,7 +16,7 @@ ...@@ -16,7 +16,7 @@
#contributors { #contributors {
.contributors-list { .contributors-list {
margin: 0 0 10px 0; margin: 0 0 10px;
list-style: none; list-style: none;
padding: 0; padding: 0;
} }
......
...@@ -5,12 +5,12 @@ class Admin::GroupsController < Admin::ApplicationController ...@@ -5,12 +5,12 @@ class Admin::GroupsController < Admin::ApplicationController
@groups = Group.all @groups = Group.all
@groups = @groups.sort(@sort = params[:sort]) @groups = @groups.sort(@sort = params[:sort])
@groups = @groups.search(params[:name]) if params[:name].present? @groups = @groups.search(params[:name]) if params[:name].present?
@groups = @groups.page(params[:page]).per(PER_PAGE) @groups = @groups.page(params[:page])
end end
def show def show
@members = @group.members.order("access_level DESC").page(params[:members_page]).per(PER_PAGE) @members = @group.members.order("access_level DESC").page(params[:members_page])
@projects = @group.projects.page(params[:projects_page]).per(PER_PAGE) @projects = @group.projects.page(params[:projects_page])
end end
def new def new
......
...@@ -2,7 +2,7 @@ class Admin::LabelsController < Admin::ApplicationController ...@@ -2,7 +2,7 @@ class Admin::LabelsController < Admin::ApplicationController
before_action :set_label, only: [:show, :edit, :update, :destroy] before_action :set_label, only: [:show, :edit, :update, :destroy]
def index def index
@labels = Label.templates.page(params[:page]).per(PER_PAGE) @labels = Label.templates.page(params[:page])
end end
def show def show
......
...@@ -11,15 +11,15 @@ class Admin::ProjectsController < Admin::ApplicationController ...@@ -11,15 +11,15 @@ class Admin::ProjectsController < Admin::ApplicationController
@projects = @projects.non_archived unless params[:with_archived].present? @projects = @projects.non_archived unless params[:with_archived].present?
@projects = @projects.search(params[:name]) if params[:name].present? @projects = @projects.search(params[:name]) if params[:name].present?
@projects = @projects.sort(@sort = params[:sort]) @projects = @projects.sort(@sort = params[:sort])
@projects = @projects.includes(:namespace).order("namespaces.path, projects.name ASC").page(params[:page]).per(PER_PAGE) @projects = @projects.includes(:namespace).order("namespaces.path, projects.name ASC").page(params[:page])
end end
def show def show
if @group if @group
@group_members = @group.members.order("access_level DESC").page(params[:group_members_page]).per(PER_PAGE) @group_members = @group.members.order("access_level DESC").page(params[:group_members_page])
end end
@project_members = @project.project_members.page(params[:project_members_page]).per(PER_PAGE) @project_members = @project.project_members.page(params[:project_members_page])
end end
def transfer def transfer
......
...@@ -6,8 +6,6 @@ class ApplicationController < ActionController::Base ...@@ -6,8 +6,6 @@ class ApplicationController < ActionController::Base
include GitlabRoutingHelper include GitlabRoutingHelper
include PageLayoutHelper include PageLayoutHelper
PER_PAGE = 20
before_action :authenticate_user_from_token! before_action :authenticate_user_from_token!
before_action :authenticate_user! before_action :authenticate_user!
before_action :validate_user_service_ticket! before_action :validate_user_service_ticket!
......
...@@ -7,7 +7,7 @@ class AutocompleteController < ApplicationController ...@@ -7,7 +7,7 @@ class AutocompleteController < ApplicationController
@users = @users.search(params[:search]) if params[:search].present? @users = @users.search(params[:search]) if params[:search].present?
@users = @users.active @users = @users.active
@users = @users.reorder(:name) @users = @users.reorder(:name)
@users = @users.page(params[:page]).per(PER_PAGE) @users = @users.page(params[:page])
if params[:search].blank? if params[:search].blank?
# Include current user if available to filter by "Me" # Include current user if available to filter by "Me"
......
...@@ -6,7 +6,7 @@ module GlobalMilestones ...@@ -6,7 +6,7 @@ module GlobalMilestones
@milestones = MilestonesFinder.new.execute(@projects, params) @milestones = MilestonesFinder.new.execute(@projects, params)
@milestones = GlobalMilestone.build_collection(@milestones) @milestones = GlobalMilestone.build_collection(@milestones)
@milestones = @milestones.sort_by { |x| x.due_date.nil? ? epoch : x.due_date } @milestones = @milestones.sort_by { |x| x.due_date.nil? ? epoch : x.due_date }
@milestones = Kaminari.paginate_array(@milestones).page(params[:page]).per(ApplicationController::PER_PAGE) @milestones = Kaminari.paginate_array(@milestones).page(params[:page])
end end
def milestone def milestone
......
module IssuableActions
extend ActiveSupport::Concern
included do
before_action :authorize_destroy_issuable!, only: :destroy
end
def destroy
issuable.destroy
name = issuable.class.name.titleize.downcase
flash[:notice] = "The #{name} was successfully deleted."
redirect_to polymorphic_path([@project.namespace.becomes(Namespace), @project, issuable.class])
end
private
def authorize_destroy_issuable!
unless current_user.can?(:"destroy_#{issuable.to_ability_name}", issuable)
return access_denied!
end
end
end
...@@ -3,7 +3,7 @@ module IssuesAction ...@@ -3,7 +3,7 @@ module IssuesAction
def issues def issues
@issues = get_issues_collection.non_archived @issues = get_issues_collection.non_archived
@issues = @issues.page(params[:page]).per(ApplicationController::PER_PAGE) @issues = @issues.page(params[:page])
@issues = @issues.preload(:author, :project) @issues = @issues.preload(:author, :project)
@label = @issuable_finder.labels.first @label = @issuable_finder.labels.first
......
...@@ -3,7 +3,7 @@ module MergeRequestsAction ...@@ -3,7 +3,7 @@ module MergeRequestsAction
def merge_requests def merge_requests
@merge_requests = get_merge_requests_collection.non_archived @merge_requests = get_merge_requests_collection.non_archived
@merge_requests = @merge_requests.page(params[:page]).per(ApplicationController::PER_PAGE) @merge_requests = @merge_requests.page(params[:page])
@merge_requests = @merge_requests.preload(:author, :target_project) @merge_requests = @merge_requests.preload(:author, :target_project)
@label = @issuable_finder.labels.first @label = @issuable_finder.labels.first
......
class Dashboard::GroupsController < Dashboard::ApplicationController class Dashboard::GroupsController < Dashboard::ApplicationController
def index def index
@group_members = current_user.group_members.page(params[:page]).per(PER_PAGE) @group_members = current_user.group_members.page(params[:page])
end end
end end
...@@ -8,7 +8,7 @@ class Dashboard::ProjectsController < Dashboard::ApplicationController ...@@ -8,7 +8,7 @@ class Dashboard::ProjectsController < Dashboard::ApplicationController
@projects = filter_projects(@projects) @projects = filter_projects(@projects)
@projects = @projects.includes(:namespace) @projects = @projects.includes(:namespace)
@projects = @projects.sort(@sort = params[:sort]) @projects = @projects.sort(@sort = params[:sort])
@projects = @projects.page(params[:page]).per(PER_PAGE) @projects = @projects.page(params[:page])
@last_push = current_user.recent_push @last_push = current_user.recent_push
...@@ -32,7 +32,7 @@ class Dashboard::ProjectsController < Dashboard::ApplicationController ...@@ -32,7 +32,7 @@ class Dashboard::ProjectsController < Dashboard::ApplicationController
@projects = filter_projects(@projects) @projects = filter_projects(@projects)
@projects = @projects.includes(:namespace, :forked_from_project, :tags) @projects = @projects.includes(:namespace, :forked_from_project, :tags)
@projects = @projects.sort(@sort = params[:sort]) @projects = @projects.sort(@sort = params[:sort])
@projects = @projects.page(params[:page]).per(PER_PAGE) @projects = @projects.page(params[:page])
@last_push = current_user.recent_push @last_push = current_user.recent_push
@groups = [] @groups = []
......
...@@ -6,6 +6,6 @@ class Dashboard::SnippetsController < Dashboard::ApplicationController ...@@ -6,6 +6,6 @@ class Dashboard::SnippetsController < Dashboard::ApplicationController
user: current_user, user: current_user,
scope: params[:scope] scope: params[:scope]
) )
@snippets = @snippets.page(params[:page]).per(PER_PAGE) @snippets = @snippets.page(params[:page])
end end
end end
...@@ -2,7 +2,7 @@ class Dashboard::TodosController < Dashboard::ApplicationController ...@@ -2,7 +2,7 @@ class Dashboard::TodosController < Dashboard::ApplicationController
before_action :find_todos, only: [:index, :destroy, :destroy_all] before_action :find_todos, only: [:index, :destroy, :destroy_all]
def index def index
@todos = @todos.page(params[:page]).per(PER_PAGE) @todos = @todos.page(params[:page])
end end
def destroy def destroy
......
...@@ -3,6 +3,6 @@ class Explore::GroupsController < Explore::ApplicationController ...@@ -3,6 +3,6 @@ class Explore::GroupsController < Explore::ApplicationController
@groups = GroupsFinder.new.execute(current_user) @groups = GroupsFinder.new.execute(current_user)
@groups = @groups.search(params[:search]) if params[:search].present? @groups = @groups.search(params[:search]) if params[:search].present?
@groups = @groups.sort(@sort = params[:sort]) @groups = @groups.sort(@sort = params[:sort])
@groups = @groups.page(params[:page]).per(PER_PAGE) @groups = @groups.page(params[:page])
end end
end end
...@@ -8,7 +8,7 @@ class Explore::ProjectsController < Explore::ApplicationController ...@@ -8,7 +8,7 @@ class Explore::ProjectsController < Explore::ApplicationController
@projects = @projects.where(visibility_level: params[:visibility_level]) if params[:visibility_level].present? @projects = @projects.where(visibility_level: params[:visibility_level]) if params[:visibility_level].present?
@projects = filter_projects(@projects) @projects = filter_projects(@projects)
@projects = @projects.sort(@sort = params[:sort]) @projects = @projects.sort(@sort = params[:sort])
@projects = @projects.includes(:namespace).page(params[:page]).per(PER_PAGE) @projects = @projects.includes(:namespace).page(params[:page])
respond_to do |format| respond_to do |format|
format.html format.html
...@@ -23,7 +23,7 @@ class Explore::ProjectsController < Explore::ApplicationController ...@@ -23,7 +23,7 @@ class Explore::ProjectsController < Explore::ApplicationController
def trending def trending
@projects = TrendingProjectsFinder.new.execute(current_user) @projects = TrendingProjectsFinder.new.execute(current_user)
@projects = filter_projects(@projects) @projects = filter_projects(@projects)
@projects = @projects.page(params[:page]).per(PER_PAGE) @projects = @projects.page(params[:page])
respond_to do |format| respond_to do |format|
format.html format.html
...@@ -39,7 +39,7 @@ class Explore::ProjectsController < Explore::ApplicationController ...@@ -39,7 +39,7 @@ class Explore::ProjectsController < Explore::ApplicationController
@projects = ProjectsFinder.new.execute(current_user) @projects = ProjectsFinder.new.execute(current_user)
@projects = filter_projects(@projects) @projects = filter_projects(@projects)
@projects = @projects.reorder('star_count DESC') @projects = @projects.reorder('star_count DESC')
@projects = @projects.page(params[:page]).per(PER_PAGE) @projects = @projects.page(params[:page])
respond_to do |format| respond_to do |format|
format.html format.html
......
class Explore::SnippetsController < Explore::ApplicationController class Explore::SnippetsController < Explore::ApplicationController
def index def index
@snippets = SnippetsFinder.new.execute(current_user, filter: :all) @snippets = SnippetsFinder.new.execute(current_user, filter: :all)
@snippets = @snippets.page(params[:page]).per(PER_PAGE) @snippets = @snippets.page(params[:page])
end end
end end
...@@ -42,7 +42,7 @@ class GroupsController < Groups::ApplicationController ...@@ -42,7 +42,7 @@ class GroupsController < Groups::ApplicationController
@projects = @projects.includes(:namespace) @projects = @projects.includes(:namespace)
@projects = filter_projects(@projects) @projects = filter_projects(@projects)
@projects = @projects.sort(@sort = params[:sort]) @projects = @projects.sort(@sort = params[:sort])
@projects = @projects.page(params[:page]).per(PER_PAGE) if params[:filter_projects].blank? @projects = @projects.page(params[:page]) if params[:filter_projects].blank?
@shared_projects = GroupProjectsFinder.new(group, only_shared: true).execute(current_user) @shared_projects = GroupProjectsFinder.new(group, only_shared: true).execute(current_user)
......
...@@ -34,8 +34,7 @@ class ProfilesController < Profiles::ApplicationController ...@@ -34,8 +34,7 @@ class ProfilesController < Profiles::ApplicationController
def audit_log def audit_log
@events = AuditEvent.where(entity_type: "User", entity_id: current_user.id). @events = AuditEvent.where(entity_type: "User", entity_id: current_user.id).
order("created_at DESC"). order("created_at DESC").
page(params[:page]). page(params[:page])
per(PER_PAGE)
end end
def update_username def update_username
......
...@@ -8,7 +8,7 @@ class Projects::BranchesController < Projects::ApplicationController ...@@ -8,7 +8,7 @@ class Projects::BranchesController < Projects::ApplicationController
def index def index
@sort = params[:sort] || 'name' @sort = params[:sort] || 'name'
@branches = @repository.branches_sorted_by(@sort) @branches = @repository.branches_sorted_by(@sort)
@branches = Kaminari.paginate_array(@branches).page(params[:page]).per(PER_PAGE) @branches = Kaminari.paginate_array(@branches).page(params[:page])
@max_commits = @branches.reduce(0) do |memo, branch| @max_commits = @branches.reduce(0) do |memo, branch|
diverging_commit_counts = repository.diverging_commit_counts(branch) diverging_commit_counts = repository.diverging_commit_counts(branch)
......
...@@ -15,7 +15,7 @@ class Projects::ForksController < Projects::ApplicationController ...@@ -15,7 +15,7 @@ class Projects::ForksController < Projects::ApplicationController
@sort = params[:sort] || 'id_desc' @sort = params[:sort] || 'id_desc'
@forks = @forks.search(params[:filter_projects]) if params[:filter_projects].present? @forks = @forks.search(params[:filter_projects]) if params[:filter_projects].present?
@forks = @forks.order_by(@sort).page(params[:page]).per(PER_PAGE) @forks = @forks.order_by(@sort).page(params[:page])
respond_to do |format| respond_to do |format|
format.html format.html
......
class Projects::IssuesController < Projects::ApplicationController class Projects::IssuesController < Projects::ApplicationController
include ToggleSubscriptionAction include ToggleSubscriptionAction
include IssuableActions
before_action :module_enabled before_action :module_enabled
before_action :issue, only: [:edit, :update, :show] before_action :issue, only: [:edit, :update, :show]
...@@ -33,7 +34,7 @@ class Projects::IssuesController < Projects::ApplicationController ...@@ -33,7 +34,7 @@ class Projects::IssuesController < Projects::ApplicationController
end end
end end
@issues = @issues.page(params[:page]).per(PER_PAGE) @issues = @issues.page(params[:page])
@label = @project.labels.find_by(title: params[:label_name]) @label = @project.labels.find_by(title: params[:label_name])
respond_to do |format| respond_to do |format|
...@@ -90,6 +91,12 @@ class Projects::IssuesController < Projects::ApplicationController ...@@ -90,6 +91,12 @@ class Projects::IssuesController < Projects::ApplicationController
def update def update
@issue = Issues::UpdateService.new(project, current_user, issue_params).execute(issue) @issue = Issues::UpdateService.new(project, current_user, issue_params).execute(issue)
if params[:move_to_project_id].to_i > 0
new_project = Project.find(params[:move_to_project_id])
move_service = Issues::MoveService.new(project, current_user)
@issue = move_service.execute(@issue, new_project)
end
respond_to do |format| respond_to do |format|
format.js format.js
format.html do format.html do
...@@ -127,6 +134,7 @@ class Projects::IssuesController < Projects::ApplicationController ...@@ -127,6 +134,7 @@ class Projects::IssuesController < Projects::ApplicationController
end end
end end
alias_method :subscribable_resource, :issue alias_method :subscribable_resource, :issue
alias_method :issuable, :issue
def authorize_read_issue! def authorize_read_issue!
return render_404 unless can?(current_user, :read_issue, @issue) return render_404 unless can?(current_user, :read_issue, @issue)
......
...@@ -11,7 +11,7 @@ class Projects::LabelsController < Projects::ApplicationController ...@@ -11,7 +11,7 @@ class Projects::LabelsController < Projects::ApplicationController
respond_to :js, :html respond_to :js, :html
def index def index
@labels = @project.labels.page(params[:page]).per(PER_PAGE) @labels = @project.labels.page(params[:page])
respond_to do |format| respond_to do |format|
format.html format.html
......
class Projects::MergeRequestsController < Projects::ApplicationController class Projects::MergeRequestsController < Projects::ApplicationController
include ToggleSubscriptionAction include ToggleSubscriptionAction
include DiffHelper include DiffHelper
include IssuableActions
before_action :module_enabled before_action :module_enabled
before_action :merge_request, only: [ before_action :merge_request, only: [
...@@ -34,7 +35,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController ...@@ -34,7 +35,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
end end
end end
@merge_requests = @merge_requests.page(params[:page]).per(PER_PAGE) @merge_requests = @merge_requests.page(params[:page])
@merge_requests = @merge_requests.preload(:target_project) @merge_requests = @merge_requests.preload(:target_project)
@label = @project.labels.find_by(title: params[:label_name]) @label = @project.labels.find_by(title: params[:label_name])
...@@ -255,6 +256,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController ...@@ -255,6 +256,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
@merge_request ||= @project.merge_requests.find_by!(iid: params[:id]) @merge_request ||= @project.merge_requests.find_by!(iid: params[:id])
end end
alias_method :subscribable_resource, :merge_request alias_method :subscribable_resource, :merge_request
alias_method :issuable, :merge_request
def closes_issues def closes_issues
@closes_issues ||= @merge_request.closes_issues @closes_issues ||= @merge_request.closes_issues
......
...@@ -22,7 +22,7 @@ class Projects::MilestonesController < Projects::ApplicationController ...@@ -22,7 +22,7 @@ class Projects::MilestonesController < Projects::ApplicationController
respond_to do |format| respond_to do |format|
format.html do format.html do
@milestones = @milestones.page(params[:page]).per(PER_PAGE) @milestones = @milestones.page(params[:page])
end end
format.json do format.json do
render json: @milestones render json: @milestones
......
...@@ -21,7 +21,7 @@ class Projects::SnippetsController < Projects::ApplicationController ...@@ -21,7 +21,7 @@ class Projects::SnippetsController < Projects::ApplicationController
filter: :by_project, filter: :by_project,
project: @project project: @project
}) })
@snippets = @snippets.page(params[:page]).per(PER_PAGE) @snippets = @snippets.page(params[:page])
end end
def new def new
......
...@@ -7,7 +7,7 @@ class Projects::TagsController < Projects::ApplicationController ...@@ -7,7 +7,7 @@ class Projects::TagsController < Projects::ApplicationController
def index def index
sorted = VersionSorter.rsort(@repository.tag_names) sorted = VersionSorter.rsort(@repository.tag_names)
@tags = Kaminari.paginate_array(sorted).page(params[:page]).per(PER_PAGE) @tags = Kaminari.paginate_array(sorted).page(params[:page])
@releases = project.releases.where(tag: @tags) @releases = project.releases.where(tag: @tags)
end end
......
...@@ -7,7 +7,7 @@ class Projects::WikisController < Projects::ApplicationController ...@@ -7,7 +7,7 @@ class Projects::WikisController < Projects::ApplicationController
before_action :load_project_wiki before_action :load_project_wiki
def pages def pages
@wiki_pages = Kaminari.paginate_array(@project_wiki.pages).page(params[:page]).per(PER_PAGE) @wiki_pages = Kaminari.paginate_array(@project_wiki.pages).page(params[:page])
end end
def show def show
......
...@@ -25,7 +25,7 @@ class SnippetsController < ApplicationController ...@@ -25,7 +25,7 @@ class SnippetsController < ApplicationController
filter: :by_user, filter: :by_user,
user: @user, user: @user,
scope: params[:scope] }). scope: params[:scope] }).
page(params[:page]).per(PER_PAGE) page(params[:page])
render 'index' render 'index'
else else
......
...@@ -100,7 +100,7 @@ class UsersController < ApplicationController ...@@ -100,7 +100,7 @@ class UsersController < ApplicationController
def load_projects def load_projects
@projects = @projects =
PersonalProjectsFinder.new(@user).execute(current_user) PersonalProjectsFinder.new(@user).execute(current_user)
.page(params[:page]).per(PER_PAGE) .page(params[:page])
end end
def load_contributed_projects def load_contributed_projects
......
...@@ -27,7 +27,7 @@ module BlobHelper ...@@ -27,7 +27,7 @@ module BlobHelper
link_opts) link_opts)
if !on_top_of_branch?(project, ref) if !on_top_of_branch?(project, ref)
button_tag "Edit", class: "btn btn-default disabled has_tooltip", title: "You can only edit files when you are on a branch", data: { container: 'body' } button_tag "Edit", class: "btn btn-default disabled has-tooltip", title: "You can only edit files when you are on a branch", data: { container: 'body' }
elsif can_edit_blob?(blob, project, ref) elsif can_edit_blob?(blob, project, ref)
link_to "Edit", edit_path, class: 'btn' link_to "Edit", edit_path, class: 'btn'
elsif can?(current_user, :fork_project, project) elsif can?(current_user, :fork_project, project)
...@@ -50,9 +50,9 @@ module BlobHelper ...@@ -50,9 +50,9 @@ module BlobHelper
return unless blob return unless blob
if !on_top_of_branch?(project, ref) if !on_top_of_branch?(project, ref)
button_tag label, class: "btn btn-#{btn_class} disabled has_tooltip", title: "You can only #{action} files when you are on a branch", data: { container: 'body' } button_tag label, class: "btn btn-#{btn_class} disabled has-tooltip", title: "You can only #{action} files when you are on a branch", data: { container: 'body' }
elsif blob.lfs_pointer? elsif blob.lfs_pointer?
button_tag label, class: "btn btn-#{btn_class} disabled has_tooltip", title: "It is not possible to #{action} files that are stored in LFS using the web interface", data: { container: 'body' } button_tag label, class: "btn btn-#{btn_class} disabled has-tooltip", title: "It is not possible to #{action} files that are stored in LFS using the web interface", data: { container: 'body' }
elsif can_edit_blob?(blob, project, ref) elsif can_edit_blob?(blob, project, ref)
button_tag label, class: "btn btn-#{btn_class}", 'data-target' => "#modal-#{modal_type}-blob", 'data-toggle' => 'modal' button_tag label, class: "btn btn-#{btn_class}", 'data-target' => "#modal-#{modal_type}-blob", 'data-toggle' => 'modal'
elsif can?(current_user, :fork_project, project) elsif can?(current_user, :fork_project, project)
......
...@@ -23,36 +23,34 @@ module ButtonHelper ...@@ -23,36 +23,34 @@ module ButtonHelper
end end
def http_clone_button(project) def http_clone_button(project)
klass = 'btn js-protocol-switch' klass = 'http-selector'
klass << ' active' if default_clone_protocol == 'http' klass << ' has-tooltip' if current_user.try(:require_password?)
klass << ' has_tooltip' if current_user.try(:require_password?)
protocol = gitlab_config.protocol.upcase protocol = gitlab_config.protocol.upcase
content_tag :button, protocol, content_tag :a, protocol,
class: klass, class: klass,
href: @project.http_url_to_repo,
data: { data: {
clone: project.http_url_to_repo, html: true,
placement: 'right',
container: 'body', container: 'body',
html: 'true',
title: "Set a password on your account<br>to pull or push via #{protocol}" title: "Set a password on your account<br>to pull or push via #{protocol}"
}, }
type: :button
end end
def ssh_clone_button(project) def ssh_clone_button(project)
klass = 'btn js-protocol-switch' klass = 'ssh-selector'
klass << ' active' if default_clone_protocol == 'ssh' klass << ' has-tooltip' if current_user.try(:require_ssh_key?)
klass << ' has_tooltip' if current_user.try(:require_ssh_key?)
content_tag :button, 'SSH', content_tag :a, 'SSH',
class: klass, class: klass,
href: project.ssh_url_to_repo,
data: { data: {
clone: project.ssh_url_to_repo, html: true,
placement: 'right',
container: 'body', container: 'body',
html: 'true',
title: 'Add an SSH key to your profile<br>to pull or push via SSH.' title: 'Add an SSH key to your profile<br>to pull or push via SSH.'
}, }
type: :button
end end
end end
...@@ -182,7 +182,7 @@ module CommitsHelper ...@@ -182,7 +182,7 @@ module CommitsHelper
end end
options = { options = {
class: "commit-#{options[:source]}-link has_tooltip", class: "commit-#{options[:source]}-link has-tooltip",
data: { 'original-title'.to_sym => sanitize(source_email) } data: { 'original-title'.to_sym => sanitize(source_email) }
} }
......
...@@ -57,6 +57,19 @@ module IssuesHelper ...@@ -57,6 +57,19 @@ module IssuesHelper
options_from_collection_for_select(milestones, 'id', 'title', object.milestone_id) options_from_collection_for_select(milestones, 'id', 'title', object.milestone_id)
end end
def project_options(issuable, current_user, ability: :read_project)
projects = current_user.authorized_projects
projects = projects.select do |project|
current_user.can?(ability, project)
end
no_project = OpenStruct.new(id: 0, name_with_namespace: 'No project')
projects.unshift(no_project)
projects.delete(issuable.project)
options_from_collection_for_select(projects, :id, :name_with_namespace)
end
def status_box_class(item) def status_box_class(item)
if item.respond_to?(:expired?) && item.expired? if item.respond_to?(:expired?) && item.expired?
'status-box-expired' 'status-box-expired'
......
...@@ -56,7 +56,7 @@ module LabelsHelper ...@@ -56,7 +56,7 @@ module LabelsHelper
# Intentionally not using content_tag here so that this method can be called # Intentionally not using content_tag here so that this method can be called
# by LabelReferenceFilter # by LabelReferenceFilter
span = %(<span class="label color-label #{"has_tooltip" if tooltip}" ) + span = %(<span class="label color-label #{"has-tooltip" if tooltip}" ) +
%(style="background-color: #{label_color}; color: #{text_color}" ) + %(style="background-color: #{label_color}; color: #{text_color}" ) +
%(title="#{escape_once(label.description)}" data-container="body">) + %(title="#{escape_once(label.description)}" data-container="body">) +
%(#{escape_once(label.name)}#{label_suffix}</span>) %(#{escape_once(label.name)}#{label_suffix}</span>)
......
...@@ -52,7 +52,7 @@ module ProjectsHelper ...@@ -52,7 +52,7 @@ module ProjectsHelper
link_to(author_html, user_path(author), class: "author_link #{"#{opts[:mobile_classes]}" if opts[:mobile_classes]}").html_safe link_to(author_html, user_path(author), class: "author_link #{"#{opts[:mobile_classes]}" if opts[:mobile_classes]}").html_safe
else else
title = opts[:title].sub(":name", sanitize(author.name)) title = opts[:title].sub(":name", sanitize(author.name))
link_to(author_html, user_path(author), class: "author_link has_tooltip", data: { 'original-title'.to_sym => title, container: 'body' } ).html_safe link_to(author_html, user_path(author), class: "author_link has-tooltip", data: { 'original-title'.to_sym => title, container: 'body' } ).html_safe
end end
end end
...@@ -209,7 +209,7 @@ module ProjectsHelper ...@@ -209,7 +209,7 @@ module ProjectsHelper
def default_clone_protocol def default_clone_protocol
if !current_user || current_user.require_ssh_key? if !current_user || current_user.require_ssh_key?
"http" gitlab_config.protocol
else else
"ssh" "ssh"
end end
......
...@@ -36,6 +36,14 @@ module Emails ...@@ -36,6 +36,14 @@ module Emails
mail_answer_thread(@issue, issue_thread_options(updated_by_user_id, recipient_id)) mail_answer_thread(@issue, issue_thread_options(updated_by_user_id, recipient_id))
end end
def issue_moved_email(recipient, issue, new_issue, updated_by_user)
setup_issue_mail(issue.id, recipient.id)
@new_issue = new_issue
@new_project = new_issue.project
mail_answer_thread(issue, issue_thread_options(updated_by_user.id, recipient.id))
end
private private
def setup_issue_mail(issue_id, recipient_id) def setup_issue_mail(issue_id, recipient_id)
......
...@@ -234,7 +234,9 @@ class Ability ...@@ -234,7 +234,9 @@ class Ability
:rename_project, :rename_project,
:remove_project, :remove_project,
:archive_project, :archive_project,
:remove_fork_project :remove_fork_project,
:destroy_merge_request,
:destroy_issue
] ]
end end
......
...@@ -7,7 +7,10 @@ module InternalId ...@@ -7,7 +7,10 @@ module InternalId
end end
def set_iid def set_iid
max_iid = project.send(self.class.name.tableize).maximum(:iid) records = project.send(self.class.name.tableize)
records = records.with_deleted if self.paranoid?
max_iid = records.maximum(:iid)
self.iid = max_iid.to_i + 1 self.iid = max_iid.to_i + 1
end end
......
...@@ -58,6 +58,8 @@ module Issuable ...@@ -58,6 +58,8 @@ module Issuable
attr_mentionable :description, cache: true attr_mentionable :description, cache: true
participant :author, :assignee, :notes_with_associations participant :author, :assignee, :notes_with_associations
strip_attributes :title strip_attributes :title
acts_as_paranoid
end end
module ClassMethods module ClassMethods
...@@ -209,4 +211,13 @@ module Issuable ...@@ -209,4 +211,13 @@ module Issuable
Taskable.get_updated_tasks(old_content: previous_changes['description'].first, Taskable.get_updated_tasks(old_content: previous_changes['description'].first,
new_content: description) new_content: description)
end end
##
# Method that checks if issuable can be moved to another project.
#
# Should be overridden if issuable can be moved.
#
def can_move?(*)
false
end
end end
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
# state :string(255) # state :string(255)
# iid :integer # iid :integer
# updated_by_id :integer # updated_by_id :integer
# moved_to_id :integer
# #
require 'carrierwave/orm/activerecord' require 'carrierwave/orm/activerecord'
...@@ -31,6 +32,8 @@ class Issue < ActiveRecord::Base ...@@ -31,6 +32,8 @@ class Issue < ActiveRecord::Base
ActsAsTaggableOn.strict_case_match = true ActsAsTaggableOn.strict_case_match = true
belongs_to :project belongs_to :project
belongs_to :moved_to, class_name: 'Issue'
validates :project, presence: true validates :project, presence: true
scope :cared, ->(user) { where(assignee_id: user) } scope :cared, ->(user) { where(assignee_id: user) }
...@@ -102,9 +105,9 @@ class Issue < ActiveRecord::Base ...@@ -102,9 +105,9 @@ class Issue < ActiveRecord::Base
end end
def related_branches def related_branches
return [] if self.project.empty_repo? project.repository.branch_names.select do |branch|
branch.end_with?("-#{iid}")
self.project.repository.branch_names.select { |branch| branch.end_with?("-#{iid}") } end
end end
# Reset issue events cache # Reset issue events cache
...@@ -134,6 +137,18 @@ class Issue < ActiveRecord::Base ...@@ -134,6 +137,18 @@ class Issue < ActiveRecord::Base
end.uniq.select { |mr| mr.open? && mr.closes_issue?(self) } end.uniq.select { |mr| mr.open? && mr.closes_issue?(self) }
end end
def moved?
!moved_to.nil?
end
def can_move?(user, to_project = nil)
if to_project
return false unless user.can?(:admin_issue, to_project)
end
!moved? && user.can?(:admin_issue, self.project)
end
def to_branch_name def to_branch_name
"#{title.parameterize}-#{iid}" "#{title.parameterize}-#{iid}"
end end
......
...@@ -431,6 +431,7 @@ class Project < ActiveRecord::Base ...@@ -431,6 +431,7 @@ class Project < ActiveRecord::Base
def safe_import_url def safe_import_url
result = URI.parse(self.import_url) result = URI.parse(self.import_url)
result.password = '*****' unless result.password.nil? result.password = '*****' unless result.password.nil?
result.user = '*****' unless result.user.nil? || result.user == "git" #tokens or other data may be saved as user
result.to_s result.to_s
rescue rescue
self.import_url self.import_url
...@@ -887,6 +888,7 @@ class Project < ActiveRecord::Base ...@@ -887,6 +888,7 @@ class Project < ActiveRecord::Base
# Forked import is handled asynchronously # Forked import is handled asynchronously
unless forked? unless forked?
if gitlab_shell.add_repository(path_with_namespace) if gitlab_shell.add_repository(path_with_namespace)
repository.after_create
true true
else else
errors.add(:base, 'Failed to create repository via gitlab-shell') errors.add(:base, 'Failed to create repository via gitlab-shell')
......
...@@ -123,23 +123,27 @@ class ProjectWiki ...@@ -123,23 +123,27 @@ class ProjectWiki
end end
def repository def repository
Repository.new(path_with_namespace, @project) @repository ||= Repository.new(path_with_namespace, @project)
end end
def default_branch def default_branch
wiki.class.default_ref wiki.class.default_ref
end end
private
def create_repo! def create_repo!
if init_repo(path_with_namespace) if init_repo(path_with_namespace)
Gollum::Wiki.new(path_to_repo) wiki = Gollum::Wiki.new(path_to_repo)
else else
raise CouldNotCreateWikiError raise CouldNotCreateWikiError
end end
repository.after_create
wiki
end end
private
def init_repo(path_with_namespace) def init_repo(path_with_namespace)
gitlab_shell.add_repository(path_with_namespace) gitlab_shell.add_repository(path_with_namespace)
end end
......
...@@ -42,13 +42,16 @@ class Repository ...@@ -42,13 +42,16 @@ class Repository
end end
def exists? def exists?
return false unless raw_repository return @exists unless @exists.nil?
raw_repository.rugged @exists = cache.fetch(:exists?) do
true begin
raw_repository && raw_repository.rugged ? true : false
rescue Gitlab::Git::Repository::NoRepository rescue Gitlab::Git::Repository::NoRepository
false false
end end
end
end
def empty? def empty?
return @empty unless @empty.nil? return @empty unless @empty.nil?
...@@ -320,12 +323,23 @@ class Repository ...@@ -320,12 +323,23 @@ class Repository
@avatar = nil @avatar = nil
end end
def expire_exists_cache
cache.expire(:exists?)
@exists = nil
end
# Runs code after a repository has been created.
def after_create
expire_exists_cache
end
# Runs code just before a repository is deleted. # Runs code just before a repository is deleted.
def before_delete def before_delete
expire_cache if exists? expire_cache if exists?
expire_root_ref_cache expire_root_ref_cache
expire_emptiness_caches expire_emptiness_caches
expire_exists_cache
end end
# Runs code just before the HEAD of a repository is changed. # Runs code just before the HEAD of a repository is changed.
...@@ -351,6 +365,7 @@ class Repository ...@@ -351,6 +365,7 @@ class Repository
# Runs code after a repository has been forked/imported. # Runs code after a repository has been forked/imported.
def after_import def after_import
expire_emptiness_caches expire_emptiness_caches
expire_exists_cache
end end
# Runs code after a new commit has been pushed. # Runs code after a new commit has been pushed.
......
...@@ -435,7 +435,7 @@ class User < ActiveRecord::Base ...@@ -435,7 +435,7 @@ class User < ActiveRecord::Base
Group.where("namespaces.id IN (#{union.to_sql})") Group.where("namespaces.id IN (#{union.to_sql})")
end end
# Returns the groups a user is authorized to access. # Returns projects user is authorized to access.
def authorized_projects def authorized_projects
Project.where("projects.id IN (#{projects_union.to_sql})") Project.where("projects.id IN (#{projects_union.to_sql})")
end end
......
...@@ -120,7 +120,7 @@ class GitPushService < BaseService ...@@ -120,7 +120,7 @@ class GitPushService < BaseService
closed_issues = commit.closes_issues(current_user) closed_issues = commit.closes_issues(current_user)
closed_issues.each do |issue| closed_issues.each do |issue|
if can?(current_user, :update_issue, issue) if can?(current_user, :update_issue, issue)
Issues::CloseService.new(project, authors[commit], {}).execute(issue, commit) Issues::CloseService.new(project, authors[commit], {}).execute(issue, commit: commit)
end end
end end
end end
......
module Issues module Issues
class CloseService < Issues::BaseService class CloseService < Issues::BaseService
def execute(issue, commit = nil) def execute(issue, commit: nil, notifications: true, system_note: true)
if project.jira_tracker? && project.jira_service.active if project.jira_tracker? && project.jira_service.active
project.jira_service.execute(commit, issue) project.jira_service.execute(commit, issue)
todo_service.close_issue(issue, current_user) todo_service.close_issue(issue, current_user)
...@@ -9,8 +9,8 @@ module Issues ...@@ -9,8 +9,8 @@ module Issues
if project.default_issues_tracker? && issue.close if project.default_issues_tracker? && issue.close
event_service.close_issue(issue, current_user) event_service.close_issue(issue, current_user)
create_note(issue, commit) create_note(issue, commit) if system_note
notification_service.close_issue(issue, current_user) notification_service.close_issue(issue, current_user) if notifications
todo_service.close_issue(issue, current_user) todo_service.close_issue(issue, current_user)
execute_hooks(issue, 'close') execute_hooks(issue, 'close')
end end
......
...@@ -4,7 +4,7 @@ module Issues ...@@ -4,7 +4,7 @@ module Issues
filter_params filter_params
label_params = params[:label_ids] label_params = params[:label_ids]
issue = project.issues.new(params.except(:label_ids)) issue = project.issues.new(params.except(:label_ids))
issue.author = current_user issue.author = params[:author] || current_user
if issue.save if issue.save
issue.update_attributes(label_ids: label_params) issue.update_attributes(label_ids: label_params)
......
module Issues
class MoveService < Issues::BaseService
class MoveError < StandardError; end
def execute(issue, new_project)
@old_issue = issue
@old_project = @project
@new_project = new_project
unless issue.can_move?(current_user, new_project)
raise MoveError, 'Cannot move issue due to insufficient permissions!'
end
if @project == new_project
raise MoveError, 'Cannot move issue to project it originates from!'
end
# Using transaction because of a high resources footprint
# on rewriting notes (unfolding references)
#
ActiveRecord::Base.transaction do
# New issue tasks
#
@new_issue = create_new_issue
rewrite_notes
add_note_moved_from
# Old issue tasks
#
add_note_moved_to
close_issue
mark_as_moved
end
notify_participants
@new_issue
end
private
def create_new_issue
new_params = { id: nil, iid: nil, label_ids: [], milestone: nil,
project: @new_project, author: @old_issue.author,
description: unfold_references(@old_issue.description) }
new_params = @old_issue.serializable_hash.merge(new_params)
CreateService.new(@new_project, @current_user, new_params).execute
end
def rewrite_notes
@old_issue.notes.find_each do |note|
new_note = note.dup
new_params = { project: @new_project, noteable: @new_issue,
note: unfold_references(new_note.note),
created_at: note.created_at }
new_note.update(new_params)
end
end
def close_issue
close_service = CloseService.new(@old_project, @current_user)
close_service.execute(@old_issue, notifications: false, system_note: false)
end
def add_note_moved_from
SystemNoteService.noteable_moved(@new_issue, @new_project,
@old_issue, @current_user,
direction: :from)
end
def add_note_moved_to
SystemNoteService.noteable_moved(@old_issue, @old_project,
@new_issue, @current_user,
direction: :to)
end
def unfold_references(content)
rewriter = Gitlab::Gfm::ReferenceRewriter.new(content, @old_project,
@current_user)
rewriter.rewrite(@new_project)
end
def notify_participants
notification_service.issue_moved(@old_issue, @new_issue, @current_user)
end
def mark_as_moved
@old_issue.update(moved_to: @new_issue)
end
end
end
...@@ -22,7 +22,7 @@ module MergeRequests ...@@ -22,7 +22,7 @@ module MergeRequests
closed_issues = merge_request.closes_issues(current_user) closed_issues = merge_request.closes_issues(current_user)
closed_issues.each do |issue| closed_issues.each do |issue|
if can?(current_user, :update_issue, issue) if can?(current_user, :update_issue, issue)
Issues::CloseService.new(project, current_user, {}).execute(issue, merge_request) Issues::CloseService.new(project, current_user, {}).execute(issue, commit: merge_request)
end end
end end
end end
......
...@@ -236,6 +236,16 @@ class NotificationService ...@@ -236,6 +236,16 @@ class NotificationService
end end
end end
def issue_moved(issue, new_issue, current_user)
recipients = build_recipients(issue, issue.project, current_user)
recipients.map do |recipient|
email = mailer.issue_moved_email(recipient, issue, new_issue, current_user)
email.deliver_later
email
end
end
protected protected
# Get project users with WATCH notification level # Get project users with WATCH notification level
......
...@@ -411,4 +411,26 @@ class SystemNoteService ...@@ -411,4 +411,26 @@ class SystemNoteService
body = "Marked the task **#{new_task.source}** as #{status_label}" body = "Marked the task **#{new_task.source}** as #{status_label}"
create_note(noteable: noteable, project: project, author: author, note: body) create_note(noteable: noteable, project: project, author: author, note: body)
end end
# Called when noteable has been moved to another project
#
# direction - symbol, :to or :from
# noteable - Noteable object
# noteable_ref - Referenced noteable
# author - User performing the move
#
# Example Note text:
#
# "Moved to some_namespace/project_new#11"
#
# Returns the created Note object
def self.noteable_moved(noteable, project, noteable_ref, author, direction:)
unless [:to, :from].include?(direction)
raise ArgumentError, "Invalid direction `#{direction}`"
end
cross_reference = noteable_ref.to_reference(project)
body = "Moved #{direction} #{cross_reference}"
create_note(noteable: noteable, project: project, author: author, note: body)
end
end end
%ul.nav.nav-sidebar %ul.nav.nav-sidebar
= nav_link(path: ['root#index', 'projects#trending', 'projects#starred', 'dashboard/projects#index'], html_options: {class: 'home'}) do = nav_link(path: ['root#index', 'projects#trending', 'projects#starred', 'dashboard/projects#index'], html_options: {class: 'home'}) do
= link_to dashboard_projects_path, title: 'Projects' do = link_to dashboard_projects_path, title: 'Projects' do
= icon('home fw') = icon('bookmark fw')
%span %span
Projects Projects
= nav_link(controller: :todos) do = nav_link(controller: :todos) do
......
%ul.nav.nav-sidebar %ul.nav.nav-sidebar
= nav_link(path: ['dashboard#show', 'root#show', 'projects#trending', 'projects#starred', 'projects#index'], html_options: {class: 'home'}) do = nav_link(path: ['dashboard#show', 'root#show', 'projects#trending', 'projects#starred', 'projects#index'], html_options: {class: 'home'}) do
= link_to explore_root_path, title: 'Projects' do = link_to explore_root_path, title: 'Projects' do
= icon('home fw') = icon('bookmark fw')
%span %span
Projects Projects
= nav_link(controller: :groups) do = nav_link(controller: :groups) do
......
...@@ -3,31 +3,7 @@ ...@@ -3,31 +3,7 @@
%meta{content: "text/html; charset=utf-8", "http-equiv" => "Content-Type"} %meta{content: "text/html; charset=utf-8", "http-equiv" => "Content-Type"}
%title %title
GitLab GitLab
:css = stylesheet_link_tag 'notify'
img {
max-width: 100%;
height: auto;
}
p.details {
font-style:italic;
color:#777
}
.footer p {
font-size:small;
color:#777
}
pre.commit-message {
white-space: pre-wrap;
}
.file-stats a {
text-decoration: none;
}
.file-stats .new-file {
color: #090;
}
.file-stats .deleted-file {
color: #B00;
}
%body %body
%div.content %div.content
= yield = yield
......
%p
Issue was moved to another project.
%p
New issue:
= link_to namespace_project_issue_url(@new_project.namespace, @new_project, @new_issue) do
= @new_issue.title
Issue was moved to another project.
New issue location:
<%= namespace_project_issue_url(@new_project.namespace, @new_project, @new_issue) %>
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
= key.fingerprint = key.fingerprint
.pull-right .pull-right
%span.key-created-at %span.key-created-at
created #{time_ago_with_tooltip(key.created_at)} ago created #{time_ago_with_tooltip(key.created_at)}
= link_to path_to_key(key, is_admin), data: { confirm: 'Are you sure?'}, method: :delete, class: "btn btn-transparent prepend-left-10" do = link_to path_to_key(key, is_admin), data: { confirm: 'Are you sure?'}, method: :delete, class: "btn btn-transparent prepend-left-10" do
%span.sr-only Remove %span.sr-only Remove
= icon('trash') = icon('trash')
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
- if branch.name == @repository.root_ref - if branch.name == @repository.root_ref
%span.label.label-primary default %span.label.label-primary default
- elsif @repository.merged_to_root_ref? branch.name - elsif @repository.merged_to_root_ref? branch.name
%span.label.label-info.has_tooltip(title="Merged into #{@repository.root_ref}") %span.label.label-info.has-tooltip(title="Merged into #{@repository.root_ref}")
merged merged
- if @project.protected_branch? branch.name - if @project.protected_branch? branch.name
...@@ -30,7 +30,7 @@ ...@@ -30,7 +30,7 @@
Compare Compare
- if can_remove_branch?(@project, branch.name) - if can_remove_branch?(@project, branch.name)
= link_to namespace_project_branch_path(@project.namespace, @project, branch.name), class: 'btn btn-grouped btn-xs btn-remove remove-row has_tooltip', title: "Delete branch", method: :delete, data: { confirm: "Deleting the '#{branch.name}' branch cannot be undone. Are you sure?", container: 'body' }, remote: true do = link_to namespace_project_branch_path(@project.namespace, @project, branch.name), class: 'btn btn-grouped btn-xs btn-remove remove-row has-tooltip', title: "Delete branch", method: :delete, data: { confirm: "Deleting the '#{branch.name}' branch cannot be undone. Are you sure?", container: 'body' }, remote: true do
= icon("trash-o") = icon("trash-o")
- if branch.name != @repository.root_ref - if branch.name != @repository.root_ref
......
- unless @project.empty_repo? - unless @project.empty_repo?
- if can? current_user, :download_code, @project - if can? current_user, :download_code, @project
= link_to archive_namespace_project_repository_path(@project.namespace, @project, ref: @ref, format: 'zip'), class: 'btn has_tooltip', data: {container: "body"}, rel: 'nofollow', title: "Download ZIP" do = link_to archive_namespace_project_repository_path(@project.namespace, @project, ref: @ref, format: 'zip'), class: 'btn has-tooltip', data: {container: "body"}, rel: 'nofollow', title: "Download ZIP" do
= icon('download') = icon('download')
- unless @project.empty_repo? - unless @project.empty_repo?
- if current_user && can?(current_user, :fork_project, @project) - if current_user && can?(current_user, :fork_project, @project)
- if current_user.already_forked?(@project) && current_user.manageable_namespaces.size < 2 - if current_user.already_forked?(@project) && current_user.manageable_namespaces.size < 2
= link_to namespace_project_path(current_user, current_user.fork_of(@project)), title: 'Go to your fork', class: 'btn has_tooltip' do = link_to namespace_project_path(current_user, current_user.fork_of(@project)), title: 'Go to your fork', class: 'btn has-tooltip' do
= icon('code-fork fw') = icon('code-fork fw')
Fork Fork
%div.count-with-arrow %div.count-with-arrow
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
%span.count %span.count
= @project.forks_count = @project.forks_count
- else - else
= link_to new_namespace_project_fork_path(@project.namespace, @project), title: "Fork project", class: 'btn has_tooltip' do = link_to new_namespace_project_fork_path(@project.namespace, @project), title: "Fork project", class: 'btn has-tooltip' do
= icon('code-fork fw') = icon('code-fork fw')
Fork Fork
%div.count-with-arrow %div.count-with-arrow
......
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
= notification_list_item(level, @membership) = notification_list_item(level, @membership)
- when GroupMember - when GroupMember
.btn.disabled.notifications-btn.has_tooltip{title: "To change the notification level, you need to be a member of the project itself, not only its group."} .btn.disabled.notifications-btn.has-tooltip{title: "To change the notification level, you need to be a member of the project itself, not only its group."}
= icon('bell') = icon('bell')
= notification_label(@membership) = notification_label(@membership)
= icon('angle-down') = icon('angle-down')
- if current_user - if current_user
= link_to toggle_star_namespace_project_path(@project.namespace, @project), class: 'btn star-btn toggle-star has_tooltip', method: :post, remote: true, title: "Star project" do = link_to toggle_star_namespace_project_path(@project.namespace, @project), class: 'btn star-btn toggle-star has-tooltip', method: :post, remote: true, title: "Star project" do
- if current_user.starred?(@project) - if current_user.starred?(@project)
= icon('star fw') = icon('star fw')
%span.starred Unstar %span.starred Unstar
...@@ -12,7 +12,7 @@ ...@@ -12,7 +12,7 @@
= @project.star_count = @project.star_count
- else - else
= link_to new_user_session_path, class: 'btn has_tooltip star-btn', title: 'You must sign in to star a project' do = link_to new_user_session_path, class: 'btn has-tooltip star-btn', title: 'You must sign in to star a project' do
= icon('star fw') = icon('star fw')
Star Star
%div.count-with-arrow %div.count-with-arrow
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment