Commit 44727525 authored by Alfredo Sumaran's avatar Alfredo Sumaran

Merge branch 'master' into issue_14206

parents 38a4f5ce 7998725e
......@@ -691,7 +691,7 @@ Style/ZeroLengthPredicate:
# branches, and conditions.
Metrics/AbcSize:
Enabled: true
Max: 70
Max: 60
# Avoid excessive block nesting.
Metrics/BlockNesting:
......
Please view this file on the master branch, on stable branches it's out of date.
v 8.7.0 (unreleased)
- Enable gzip for assets, makes the page size significantly smaller. !3544 / !3632 (Connor Shea)
- Load award emoji images separately unless opening the full picker. Saves several hundred KBs of data for most pages. (Connor Shea)
- All images in discussions and wikis now link to their source files !3464 (Connor Shea).
- Return status code 303 after a branch DELETE operation to avoid project deletion (Stan Hu)
- Improved Markdown rendering performance !3389 (Yorick Peterse)
- Don't attempt to look up an avatar in repo if repo directory does not exist (Stan Hu)
- Expose project badges in project settings
- Preserve time notes/comments have been updated at when moving issue
- Make HTTP(s) label consistent on clone bar (Stan Hu)
- Expose label description in API (Mariusz Jachimowicz)
- Allow back dating on issues when created through the API
- Fix Error 500 after renaming a project path (Stan Hu)
- Fix avatar stretching by providing a cropping feature
- API: Expose `subscribed` for issues and merge requests (Robert Schilling)
- Allow SAML to handle external users based on user's information !3530
- Add endpoints to archive or unarchive a project !3372
- Add links to CI setup documentation from project settings and builds pages
- Handle nil descriptions in Slack issue messages (Stan Hu)
- Add default scope to projects to exclude projects pending deletion
- Ensure empty recipients are rejected in BuildsEmailService
- API: Ability to filter milestones by state `active` and `closed` (Robert Schilling)
- Implement 'Groups View' as an option for dashboard preferences !3379 (Elias W.)
- Better errors handling when creating milestones inside groups
- Implement 'TODOs View' as an option for dashboard preferences !3379 (Elias W.)
- Gracefully handle notes on deleted commits in merge requests (Stan Hu)
- Fix creation of merge requests for orphaned branches (Stan Hu)
- Fall back to `In-Reply-To` and `References` headers when sub-addressing is not available (David Padilla)
- Remove "Congratulations!" tweet button on newly-created project. (Connor Shea)
- Improved UX of the navigation sidebar
- Fix admin/projects when using visibility levels on search (PotHix)
- Build status notifications
- API: Expose user location (Robert Schilling)
v 8.6.5 (unreleased)
- Check permissions when user attempts to import members from another project
- ClosingIssueExtractor regex now also works with colons. e.g. "Fixes: #1234" !3591
- Update number of Todos in the sidebar when it's marked as "Done". !3600
v 8.6.5
- Fix importing from GitHub Enterprise. !3529
- Perform the language detection after updating merge requests in `GitPushService`, leading to faster visual feedback for the end-user. !3533
- Check permissions when user attempts to import members from another project. !3535
- Only update repository language if it is not set to improve performance. !3556
- Return status code 303 after a branch DELETE operation to avoid project deletion (Stan Hu). !3583
- Unblock user when active_directory is disabled and it can be found !3550
- Fix a 2FA authentication spoofing vulnerability.
v 8.6.4
- Don't attempt to fetch any tags from a forked repo (Stan Hu)
- Redesign the Labels page
v 8.6.3
- Mentions on confidential issues doesn't create todos for non-members. !3374
......@@ -146,6 +163,9 @@ v 8.6.0
- Trigger a todo for mentions on commits page
- Let project owners and admins soft delete issues and merge requests
v 8.5.10
- Fix a 2FA authentication spoofing vulnerability.
v 8.5.9
- Don't attempt to fetch any tags from a forked repo (Stan Hu).
......@@ -290,6 +310,9 @@ v 8.5.0
- Show label row when filtering issues or merge requests by label (Nuttanart Pornprasitsakul)
- Add Todos
v 8.4.8
- Fix a 2FA authentication spoofing vulnerability.
v 8.4.7
- Don't attempt to fetch any tags from a forked repo (Stan Hu).
......@@ -409,6 +432,9 @@ v 8.4.0
- Add IP check against DNSBLs at account sign-up
- Added cache:key to .gitlab-ci.yml allowing to fine tune the caching
v 8.3.7
- Fix a 2FA authentication spoofing vulnerability.
v 8.3.6
- Don't attempt to fetch any tags from a forked repo (Stan Hu).
......
......@@ -448,7 +448,7 @@ merge request:
- multi-line method chaining style **Option B**: dot `.` on previous line
- string literal quoting style **Option A**: single quoted by default
1. [Rails](https://github.com/bbatsov/rails-style-guide)
1. [Testing](https://github.com/thoughtbot/guides/tree/master/style/testing)
1. [Testing](doc/development/testing.md)
1. [CoffeeScript](https://github.com/thoughtbot/guides/tree/master/style/coffeescript)
1. [SCSS styleguide][scss-styleguide]
1. [Shell commands](doc/development/shell_commands.md) created by GitLab
......
source "https://rubygems.org"
gem 'rails', '4.2.5.2'
gem 'rails', '4.2.6'
gem 'rails-deprecated_sanitizer', '~> 1.0.3'
# Responders respond_to and respond_with
......@@ -8,7 +8,7 @@ gem 'responders', '~> 2.0'
# Specify a sprockets version due to increased performance
# See https://gitlab.com/gitlab-org/gitlab-ce/issues/6069
gem 'sprockets', '~> 3.3.5'
gem 'sprockets', '~> 3.6.0'
# Default values for AR models
gem "default_value_for", "~> 3.0.0"
......@@ -149,6 +149,10 @@ gem 'version_sorter', '~> 2.0.0'
# Cache
gem "redis-rails", '~> 4.0.0'
# Redis
gem 'redis', '~> 3.2'
gem 'connection_pool', '~> 2.0'
# Campfire integration
gem 'tinder', '~> 1.10.0'
......@@ -229,14 +233,13 @@ group :metrics do
gem 'allocations', '~> 1.0', require: false, platform: :mri
gem 'method_source', '~> 0.8', require: false
gem 'influxdb', '~> 0.2', require: false
gem 'connection_pool', '~> 2.0', require: false
end
group :development do
gem "foreman"
gem 'brakeman', '~> 3.2.0', require: false
gem "annotate", "~> 2.6.0"
gem "annotate", "~> 2.7.0"
gem "letter_opener", '~> 1.1.2'
gem 'quiet_assets', '~> 1.0.2'
gem 'rerun', '~> 0.11.0'
......@@ -290,7 +293,7 @@ group :development, :test do
gem 'rubocop', '~> 0.38.0', require: false
gem 'scss_lint', '~> 0.47.0', require: false
gem 'coveralls', '~> 0.8.2', require: false
gem 'simplecov', '~> 0.10.0', require: false
gem 'simplecov', '~> 0.11.0', require: false
gem 'flog', require: false
gem 'flay', require: false
gem 'bundler-audit', require: false
......
......@@ -4,41 +4,41 @@ GEM
CFPropertyList (2.3.2)
RedCloth (4.2.9)
ace-rails-ap (2.0.1)
actionmailer (4.2.5.2)
actionpack (= 4.2.5.2)
actionview (= 4.2.5.2)
activejob (= 4.2.5.2)
actionmailer (4.2.6)
actionpack (= 4.2.6)
actionview (= 4.2.6)
activejob (= 4.2.6)
mail (~> 2.5, >= 2.5.4)
rails-dom-testing (~> 1.0, >= 1.0.5)
actionpack (4.2.5.2)
actionview (= 4.2.5.2)
activesupport (= 4.2.5.2)
actionpack (4.2.6)
actionview (= 4.2.6)
activesupport (= 4.2.6)
rack (~> 1.6)
rack-test (~> 0.6.2)
rails-dom-testing (~> 1.0, >= 1.0.5)
rails-html-sanitizer (~> 1.0, >= 1.0.2)
actionview (4.2.5.2)
activesupport (= 4.2.5.2)
actionview (4.2.6)
activesupport (= 4.2.6)
builder (~> 3.1)
erubis (~> 2.7.0)
rails-dom-testing (~> 1.0, >= 1.0.5)
rails-html-sanitizer (~> 1.0, >= 1.0.2)
activejob (4.2.5.2)
activesupport (= 4.2.5.2)
activejob (4.2.6)
activesupport (= 4.2.6)
globalid (>= 0.3.0)
activemodel (4.2.5.2)
activesupport (= 4.2.5.2)
activemodel (4.2.6)
activesupport (= 4.2.6)
builder (~> 3.1)
activerecord (4.2.5.2)
activemodel (= 4.2.5.2)
activesupport (= 4.2.5.2)
activerecord (4.2.6)
activemodel (= 4.2.6)
activesupport (= 4.2.6)
arel (~> 6.0)
activerecord-deprecated_finders (1.0.4)
activerecord-session_store (0.1.2)
actionpack (>= 4.0.0, < 5)
activerecord (>= 4.0.0, < 5)
railties (>= 4.0.0, < 5)
activesupport (4.2.5.2)
activesupport (4.2.6)
i18n (~> 0.7)
json (~> 1.7, >= 1.7.7)
minitest (~> 5.1)
......@@ -51,8 +51,8 @@ GEM
activerecord (>= 3.0)
akismet (2.0.0)
allocations (1.0.4)
annotate (2.6.10)
activerecord (>= 3.2, <= 4.3)
annotate (2.7.0)
activerecord (>= 3.2, < 6.0)
rake (~> 10.4)
arel (6.0.3)
asana (0.4.0)
......@@ -136,17 +136,16 @@ GEM
colorize (0.7.7)
concurrent-ruby (1.0.0)
connection_pool (2.2.0)
coveralls (0.8.9)
coveralls (0.8.13)
json (~> 1.8)
rest-client (>= 1.6.8, < 2)
simplecov (~> 0.10.0)
simplecov (~> 0.11.0)
term-ansicolor (~> 1.3)
thor (~> 0.19.1)
tins (~> 1.6.0)
crack (0.4.3)
safe_yaml (~> 1.0.0)
creole (0.5.0)
css_parser (1.3.7)
css_parser (1.4.1)
addressable
d3_rails (3.5.11)
railties (>= 3.1.0)
......@@ -176,8 +175,6 @@ GEM
diff-lcs (1.2.5)
diffy (3.0.7)
docile (1.1.5)
domain_name (0.5.25)
unf (>= 0.0.5, < 1.0.0)
doorkeeper (2.2.2)
railties (>= 3.2)
dropzonejs-rails (0.7.2)
......@@ -421,8 +418,6 @@ GEM
nokogiri (~> 1.6.0)
ruby_parser (~> 3.5)
htmlentities (4.3.4)
http-cookie (1.0.2)
domain_name (~> 0.5)
http_parser.rb (0.5.3)
httparty (0.13.7)
json (~> 1.8)
......@@ -464,8 +459,8 @@ GEM
nokogiri (>= 1.5.9)
macaddr (1.7.1)
systemu (~> 2.6.2)
mail (2.6.3)
mime-types (>= 1.16, < 3)
mail (2.6.4)
mime-types (>= 1.16, < 4)
mail_room (0.6.1)
method_source (0.8.2)
mime-types (1.25.1)
......@@ -480,7 +475,6 @@ GEM
nested_form (0.3.2)
net-ldap (0.12.1)
net-ssh (3.0.1)
netrc (0.11.0)
newrelic_rpm (3.14.1.311)
nokogiri (1.6.7.2)
mini_portile2 (~> 2.0.0.rc2)
......@@ -565,8 +559,8 @@ GEM
premailer (1.8.6)
css_parser (>= 1.3.6)
htmlentities (>= 4.0.0)
premailer-rails (1.9.0)
actionmailer (>= 3, < 5)
premailer-rails (1.9.2)
actionmailer (>= 3, < 6)
premailer (~> 1.7, >= 1.7.9)
pry (0.10.3)
coderay (~> 1.1.0)
......@@ -595,16 +589,16 @@ GEM
rack
rack-test (0.6.3)
rack (>= 1.0)
rails (4.2.5.2)
actionmailer (= 4.2.5.2)
actionpack (= 4.2.5.2)
actionview (= 4.2.5.2)
activejob (= 4.2.5.2)
activemodel (= 4.2.5.2)
activerecord (= 4.2.5.2)
activesupport (= 4.2.5.2)
rails (4.2.6)
actionmailer (= 4.2.6)
actionpack (= 4.2.6)
actionview (= 4.2.6)
activejob (= 4.2.6)
activemodel (= 4.2.6)
activerecord (= 4.2.6)
activesupport (= 4.2.6)
bundler (>= 1.3.0, < 2.0)
railties (= 4.2.5.2)
railties (= 4.2.6)
sprockets-rails
rails-deprecated_sanitizer (1.0.3)
activesupport (>= 4.2.0.alpha)
......@@ -614,9 +608,9 @@ GEM
rails-deprecated_sanitizer (>= 1.0.1)
rails-html-sanitizer (1.0.3)
loofah (~> 2.0)
railties (4.2.5.2)
actionpack (= 4.2.5.2)
activesupport (= 4.2.5.2)
railties (4.2.6)
actionpack (= 4.2.6)
activesupport (= 4.2.6)
rake (>= 0.8.7)
thor (>= 0.18.1, < 2.0)
rainbow (2.1.0)
......@@ -657,10 +651,6 @@ GEM
listen (~> 3.0)
responders (2.1.1)
railties (>= 4.2.0, < 5.1)
rest-client (1.8.0)
http-cookie (>= 1.0.2, < 2.0)
mime-types (>= 1.16, < 3.0)
netrc (~> 0.7)
rinku (1.7.3)
rotp (2.1.1)
rouge (1.10.1)
......@@ -754,7 +744,7 @@ GEM
rufus-scheduler (>= 2.0.24)
sidekiq (>= 4.0.0)
simple_oauth (0.1.9)
simplecov (0.10.0)
simplecov (0.11.2)
docile (~> 1.1.0)
json (~> 1.8)
simplecov-html (~> 0.10.0)
......@@ -786,12 +776,13 @@ GEM
spring (>= 0.9.1)
spring-commands-teaspoon (0.0.2)
spring (>= 0.9.1)
sprockets (3.3.5)
sprockets (3.6.0)
concurrent-ruby (~> 1.0)
rack (> 1, < 3)
sprockets-rails (2.3.3)
actionpack (>= 3.0)
activesupport (>= 3.0)
sprockets (>= 2.8, < 4.0)
sprockets-rails (3.0.4)
actionpack (>= 4.0)
activesupport (>= 4.0)
sprockets (>= 3.0.0)
state_machines (0.4.0)
state_machines-activemodel (0.3.0)
activemodel (~> 4.1)
......@@ -845,7 +836,7 @@ GEM
underscore-rails (1.8.3)
unf (0.1.4)
unf_ext
unf_ext (0.0.7.1)
unf_ext (0.0.7.2)
unicode-display_width (1.0.2)
unicorn (4.9.0)
kgio (~> 2.6)
......@@ -897,7 +888,7 @@ DEPENDENCIES
after_commit_queue
akismet (~> 2.0)
allocations (~> 1.0)
annotate (~> 2.6.0)
annotate (~> 2.7.0)
asana (~> 0.4.0)
asciidoctor (~> 1.5.2)
attr_encrypted (~> 1.3.4)
......@@ -1002,13 +993,14 @@ DEPENDENCIES
rack-attack (~> 4.3.1)
rack-cors (~> 0.4.0)
rack-oauth2 (~> 1.2.1)
rails (= 4.2.5.2)
rails (= 4.2.6)
rails-deprecated_sanitizer (~> 1.0.3)
raphael-rails (~> 2.1.2)
rblineprof
rdoc (~> 3.6)
recaptcha
redcarpet (~> 3.3.3)
redis (~> 3.2)
redis-namespace
redis-rails (~> 4.0.0)
request_store (~> 1.3.0)
......@@ -1032,7 +1024,7 @@ DEPENDENCIES
shoulda-matchers (~> 2.8.0)
sidekiq (~> 4.0)
sidekiq-cron (~> 0.4.0)
simplecov (~> 0.10.0)
simplecov (~> 0.11.0)
sinatra (~> 1.4.4)
six (~> 0.2.0)
slack-notifier (~> 1.2.0)
......@@ -1042,7 +1034,7 @@ DEPENDENCIES
spring-commands-rspec (~> 1.0.4)
spring-commands-spinach (~> 1.0.0)
spring-commands-teaspoon (~> 0.0.2)
sprockets (~> 3.3.5)
sprockets (~> 3.6.0)
state_machines-activerecord (~> 0.3.0)
task_list (~> 1.0.2)
teaspoon (~> 1.1.0)
......
......@@ -22,8 +22,19 @@ class @AwardsHandler
emoji = $(this)
.find(".icon")
.data "emoji"
if emoji is "thumbsup" and awards_handler.didUserClickEmoji $(this), "thumbsdown"
awards_handler.addAward "thumbsdown"
else if emoji is "thumbsdown" and awards_handler.didUserClickEmoji $(this), "thumbsup"
awards_handler.addAward "thumbsup"
awards_handler.addAward emoji
didUserClickEmoji: (that, emoji) ->
if $(that).siblings("button:has([data-emoji=#{emoji}])").attr("data-original-title")
$(that).siblings("button:has([data-emoji=#{emoji}])").attr("data-original-title").indexOf('me') > -1
showEmojiMenu: ->
if $(".emoji-menu").length
if $(".emoji-menu").is ".is-visible"
......@@ -105,7 +116,7 @@ class @AwardsHandler
if origTitle
authors = origTitle.split(', ')
authors.push("me")
award_block.attr("title", authors.join(", "))
award_block.attr("data-original-title", authors.join(", "))
@resetTooltip(award_block)
resetTooltip: (award) ->
......@@ -122,7 +133,7 @@ class @AwardsHandler
nodes = []
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' data-original-title='me'>",
"<div class='icon emoji-icon #{emojiCssClass}' data-emoji='#{emoji}'></div>",
"<span class='award-control-text js-counter'>1</span>",
"</button>"
......
class @Compare
constructor: (@opts) ->
@source_loading = $ ".js-source-loading"
@target_loading = $ ".js-target-loading"
$('.js-compare-dropdown').each (i, dropdown) =>
$dropdown = $(dropdown)
$dropdown.glDropdown(
selectable: true
fieldName: $dropdown.data 'field-name'
filterable: true
id: (obj, $el) ->
$el.data 'id'
toggleLabel: (obj, $el) ->
$el.text().trim()
clicked: (e, el) =>
if $dropdown.is '.js-target-branch'
@getTargetHtml()
else if $dropdown.is '.js-source-branch'
@getSourceHtml()
else if $dropdown.is '.js-target-project'
@getTargetProject()
)
@initialState()
initialState: ->
@getSourceHtml()
@getTargetHtml()
getTargetProject: ->
$.ajax(
url: @opts.targetProjectUrl
data:
target_project_id: $("input[name='merge_request[target_project_id]']").val()
beforeSend: ->
$('.mr_target_commit').empty()
success: (html) ->
$('.js-target-branch-dropdown .dropdown-content').html html
)
getSourceHtml: ->
@sendAjax(@opts.sourceBranchUrl, @source_loading, '.mr_source_commit',
ref: $("input[name='merge_request[source_branch]']").val()
)
getTargetHtml: ->
@sendAjax(@opts.targetBranchUrl, @target_loading, '.mr_target_commit',
target_project_id: $("input[name='merge_request[target_project_id]']").val()
ref: $("input[name='merge_request[target_branch]']").val()
)
sendAjax: (url, loading, target, data) ->
$target = $(target)
$.ajax(
url: url
data: data
beforeSend: ->
loading.show()
$target.empty()
success: (html) ->
loading.hide()
$target.html html
$('.js-timeago', $target).timeago()
)
......@@ -57,14 +57,30 @@ class GitLabDropdownFilter
filter: (search_text) ->
data = @options.data()
if data?
results = data
if search_text isnt ""
if search_text isnt ''
results = fuzzaldrinPlus.filter(data, search_text,
key: @options.keys
)
@options.callback results
else
elements = @options.elements()
if search_text
elements.each ->
$el = $(@)
matches = fuzzaldrinPlus.match($el.text().trim(), search_text)
if matches.length
$el.show()
else
$el.hide()
else
elements.show()
class GitLabDropdownRemote
constructor: (@dataEndpoint, @options) ->
......@@ -123,7 +139,7 @@ class GitLabDropdown
if _.isString(@filterInput)
@filterInput = @getElement(@filterInput)
search_fields = if @options.search then @options.search.fields else [];
searchFields = if @options.search then @options.search.fields else [];
if @options.data
# If data is an array
......@@ -147,7 +163,14 @@ class GitLabDropdown
filterInputBlur: @filterInputBlur
remote: @options.filterRemote
query: @options.data
keys: @options.search.fields
keys: searchFields
elements: =>
selector = '.dropdown-content li:not(.divider)'
if @dropdown.find('.dropdown-toggle-page').length
selector = ".dropdown-page-one #{selector}"
return $(selector)
data: =>
return @fullData
callback: (data) =>
......@@ -376,7 +399,7 @@ class GitLabDropdown
# Toggle the dropdown label
if @options.toggleLabel
$(@el).find(".dropdown-toggle-text").text @options.toggleLabel(selectedObject)
$(@el).find(".dropdown-toggle-text").text @options.toggleLabel(selectedObject, el)
if value?
if !field.length and fieldName
# Create hidden input for form
......
......@@ -73,6 +73,7 @@ class @MergeRequestTabs
@expandView()
else if action == 'diffs'
@loadDiff($target.attr('href'))
if bp? and bp.getBreakpointSize() isnt 'lg'
@shrinkView()
else if action == 'builds'
@loadBuilds($target.attr('href'))
......
......@@ -15,6 +15,8 @@ class @MergeRequestWidget
@pollCIStatus()
notifyPermissions()
setOpts: (@opts) ->
mergeInProgress: (deleteSourceBranch = false)->
$.ajax
type: 'GET'
......@@ -48,7 +50,7 @@ class @MergeRequestWidget
@getCIStatus(true)
@readyForCICheck = false
), 5000
), 10000
getCIStatus: (showNotification) ->
_this = @
......@@ -61,6 +63,10 @@ class @MergeRequestWidget
@firstCICheck = false
@opts.ci_status = data.status
if @opts.ci_status is ''
@opts.ci_status = data.status
return
if data.status isnt @opts.ci_status
@showCIStatus data.status
if data.coverage
......
......@@ -85,15 +85,21 @@ class @MilestoneSelect
# display:block overrides the hide-collapse rule
$value.removeAttr('style')
clicked: (selected) ->
page = $('body').data 'page'
isIssueIndex = page is 'projects:issues:index'
isMRIndex = page is page is 'projects:merge_requests:index'
if $dropdown.hasClass 'js-filter-bulk-update'
return
if $dropdown.hasClass('js-filter-submit')
if $dropdown.hasClass('js-filter-submit') and (isIssueIndex or isMRIndex)
if selected.name?
selectedMilestone = selected.name
else
selectedMilestone = ''
Issues.filterResults $dropdown.closest('form')
else if $dropdown.hasClass('js-filter-submit')
$dropdown.closest('form').submit()
else
selected = $selectbox
.find('input[type="hidden"]')
......
......@@ -4,6 +4,7 @@ expanded = 'page-sidebar-expanded'
toggleSidebar = ->
$('.page-with-sidebar').toggleClass("#{collapsed} #{expanded}")
$('header').toggleClass("header-collapsed header-expanded")
$('.toggle-nav-collapse i').toggleClass("fa-angle-right fa-angle-left")
$.cookie("collapsed_nav", $('.page-with-sidebar').hasClass(collapsed), { path: '/' })
setTimeout ( ->
......
......@@ -10,10 +10,10 @@ class @Subscription
btn = $(event.currentTarget)
action = btn.find('span').text()
current_status = @subscription_status.attr('data-status')
btn.prop('disabled', true)
btn.addClass('disabled')
$.post @url, =>
btn.prop('disabled', false)
btn.removeClass('disabled')
status = if current_status == 'subscribed' then 'unsubscribed' else 'subscribed'
@subscription_status.attr('data-status', status)
action = if status == 'subscribed' then 'Unsubscribe' else 'Subscribe'
......
......@@ -57,5 +57,10 @@ class @Todos
$('.todos-pending .badge, .todos-pending-count').text data.count
$('.todos-done .badge').text data.done_count
goToTodoUrl: ->
Turbolinks.visit($(this).data('url'))
goToTodoUrl: (e)->
todoLink = $(this).data('url')
if e.metaKey
e.preventDefault()
window.open(todoLink,'_blank')
else
Turbolinks.visit(todoLink)
......@@ -7,6 +7,7 @@
&:focus,
&:active {
outline: none;
background-color: $btn-active-gray;
@include box-shadow($gl-btn-active-background);
}
}
......@@ -27,7 +28,8 @@
color: $color;
}
&:active {
&:active,
&.active {
@include box-shadow ($gl-btn-active-background);
background-color: $dark;
......@@ -61,7 +63,7 @@
}
@mixin btn-white {
@include btn-color($white-light, $border-white-light, $white-normal, $border-white-normal, $white-dark, $border-white-dark, #313236);
@include btn-color($white-light, $border-color, $white-normal, $border-white-normal, $white-dark, $border-white-dark, $btn-white-active);
}
.btn {
......@@ -218,3 +220,26 @@
margin-right: 5px;
}
}
.btn-text-field {
width: 100%;
text-align: left;
padding: 6px 16px;
border-color: $border-color;
color: $btn-placeholder-gray;
background-color: $background-color;
&:hover,
&:active,
&:focus {
cursor: text;
box-shadow: none;
border-color: $border-color;
color: $btn-placeholder-gray;
background-color: $background-color;
}
}
.btn-file-option {
background: linear-gradient(180deg, $white-light 25%, $gray-light 100%);
}
......@@ -248,7 +248,7 @@
.dropdown-title {
position: relative;
padding: 0 0 15px;
padding: 0 25px 15px;
margin: 0 10px 10px;
font-weight: 600;
line-height: 1;
......@@ -275,7 +275,7 @@
}
.dropdown-menu-close {
right: 7px;
right: 5px;
width: 20px;
height: 20px;
top: -1px;
......
......@@ -15,11 +15,13 @@
.file-title {
position: relative;
background: $background-color;
background-color: $background-color;
border-bottom: 1px solid $border-color;
margin: 0;
text-align: left;
padding: 10px $gl-padding;
word-wrap: break-word;
border-radius: 3px 3px 0 0;
.file-actions {
float: right;
......@@ -48,7 +50,7 @@
}
}
a {
a:not(.btn) {
color: $gl-dark-link-color;
}
......
......@@ -33,15 +33,10 @@
background: $color;
}
.complex-sidebar .nav-primary {
border-right: 1px solid lighten($color, 3%);
}
.sidebar-wrapper {
background: $color-darker;
.sidebar-user {
border-top: 1px solid lighten($color, 3%);
background: $color-darker;
color: $color-light;
......@@ -67,6 +62,7 @@
.count {
color: $color-light;
background: $color-dark;
}
}
......
......@@ -123,11 +123,11 @@ header {
}
@mixin collapsed-header {
margin-left: 40px;
margin-left: $sidebar_collapsed_width;
}
.header-collapsed {
margin-left: 40px;
margin-left: $sidebar_collapsed_width;
@media (min-width: $screen-md-min) {
@include collapsed-header;
......
......@@ -84,6 +84,7 @@
.md-preview-holder {
min-height: 167px;
padding: 10px 0;
overflow-x: auto;
}
.markdown-area {
......
......@@ -144,7 +144,7 @@
}
a {
padding: 7px 12px;
padding: 7px 15px;
font-size: $gl-font-size;
line-height: 24px;
color: $gray;
......@@ -169,12 +169,10 @@
}
.count {
&:before {
content: '(';
}
&:after {
content: ')';
}
float: right;
background: #eee;
padding: 0 8px;
@include border-radius(6px);
}
&.back-link i {
......@@ -193,27 +191,6 @@
}
}
.expand-nav a {
color: $gl-icon-color;
width: 60px;
position: fixed;
top: 0;
left: 0;
font-size: 20px;
background: #fff;
height: 59px;
text-align: center;
line-height: 59px;
border-bottom: 1px solid #eee;
transition-duration: .3s;
outline: none;
z-index: 100;
&:hover {
text-decoration: none;
}
}
.collapse-nav a {
width: $sidebar_width;
position: fixed;
......@@ -233,12 +210,55 @@
}
.page-sidebar-collapsed {
padding-left: $sidebar_collapsed_width;
.sidebar-wrapper {
width: $sidebar_collapsed_width;
.header-logo {
width: $sidebar_collapsed_width;
a {
padding-left: ($sidebar_collapsed_width - 36) / 2;
.gitlab-text-container {
display: none;
}
}
}
.nav-sidebar {
width: $sidebar_collapsed_width;
li {
width: auto;
a {
span {
display: none;
}
}
}
}
.collapse-nav a {
width: $sidebar_collapsed_width;
}
.sidebar-user {
padding-left: ($sidebar_collapsed_width - 36) / 2;
width: $sidebar_collapsed_width;
.username {
display: none;
}
}
}
}
.page-sidebar-expanded {
padding-left: $sidebar_collapsed_width;
@media (min-width: $screen-md-min) {
padding-left: $sidebar_width;
}
......@@ -289,48 +309,3 @@
padding-right: $sidebar_collapsed_width;
}
}
.complex-sidebar {
display: inline-block;
.nav-primary {
width: 61px;
float: left;
height: 100vh;
.nav-sidebar {
width: 60px;
li a {
width: 60px;
span {
display: none;
}
}
}
}
.nav-secondary {
$nav-secondary-width: 168px;
float: left;
width: $nav-secondary-width;
.nav-sidebar {
width: $nav-secondary-width;
li {
width: $nav-secondary-width;
a {
width: $nav-secondary-width;
i {
display: none;
}
}
}
}
}
}
......@@ -14,10 +14,6 @@
background: $row-hover;
}
&:last-child {
border-bottom: none;
}
.avatar {
margin-right: 15px;
}
......
......@@ -10,10 +10,10 @@ $gutter_inner_width: 258px;
/*
* UI elements
*/
$border-color: #efeff1;
$border-color: #e5e5e5;
$focus-border-color: #3aabf0;
$table-border-color: #eef0f2;
$background-color: #faf9f9;
$background-color: #fafafa;
/*
* Text
......@@ -81,7 +81,7 @@ $provider-btn-not-active-color: #4688f1;
$white-light: #fff;
$white-normal: #ededed;
$white-dark: #ededed;
$white-dark: #ececec;
$gray-light: #faf9f9;
$gray-normal: #f5f5f5;
......@@ -108,6 +108,8 @@ $red-light: #e52c5a;
$red-normal: #d22852;
$red-dark: darken($red-normal, 5%);
$black-transparent: rgba(0, 0, 0, 0.3);
$border-white-light: #f1f2f4;
$border-white-normal: #d6dae2;
$border-white-dark: #c6cacf;
......@@ -150,15 +152,22 @@ $gl-success: $green-normal;
$gl-info: $blue-normal;
$gl-warning: $orange-normal;
$gl-danger: $red-normal;
$gl-btn-active-background: rgba(0, 0, 0, 0.12);
$gl-btn-active-gradient: inset 0 0 4px $gl-btn-active-background;
$gl-btn-active-background: rgba(0, 0, 0, 0.16);
$gl-btn-active-gradient: inset 0 2px 3px $gl-btn-active-background;
/*
* Commit Diff Colors
*/
$added: #63c363;
$deleted: #f77;
$line-added: #ecfdf0;
$line-added-dark: #c7f0d2;
$line-removed: #fbe9eb;
$line-removed-dark: #fac5cd;
$line-number-old: #f9d7dc;
$line-number-new: #ddfbe6;
$match-line: #fafafa;
$table-border-gray: #f0f0f0;
/*
* Fonts
*/
......@@ -191,6 +200,13 @@ $dropdown-toggle-hover-border-color: darken($dropdown-toggle-border-color, 15%);
$dropdown-toggle-icon-color: #c4c4c4;
$dropdown-toggle-hover-icon-color: $dropdown-toggle-hover-border-color;
/*
* Buttons
*/
$btn-active-gray: #ececec;
$btn-placeholder-gray: #c7c7c7;
$btn-white-active: #848484;
/*
* Award emoji
*/
......
......@@ -6,7 +6,7 @@
}
.diff-line-num, .diff-line-num a {
color: rgba(0, 0, 0, 0.3);
color: $black-transparent;
}
// Code itself
......@@ -30,7 +30,7 @@
}
.line_content.match {
color: rgba(0, 0, 0, 0.3);
color: $black-transparent;
background: rgba(255, 255, 255, 0.4);
}
}
......
......@@ -6,12 +6,12 @@
}
.diff-line-num, .diff-line-num a {
color: rgba(0, 0, 0, 0.3);
color: $black-transparent;
}
// Code itself
pre.code, .diff-line-num {
border-color: $border-color;
border-color: $table-border-gray;
}
&, pre.code, .line_holder .line_content {
......@@ -23,36 +23,36 @@
.line_holder {
.diff-line-num {
&.old {
background: #fdd;
border-color: #f1c0c0;
background-color: $line-number-old;
border-color: $line-removed-dark;
}
&.new {
background: #dbffdb;
border-color: #c1e9c1;
background-color: $line-number-new;
border-color: $line-added-dark;
}
}
.line_content {
&.old {
background: #ffecec;
background: $line-removed;
span.idiff {
background-color: #f8cbcb;
background-color: $line-removed-dark;
}
}
&.new {
background: #eaffea;
background-color: $line-added;
span.idiff {
background-color: #a6f3a6;
background-color: $line-added-dark;
}
}
&.match {
color: rgba(0, 0, 0, 0.3);
background: #fafafa;
color: $black-transparent;
background: $match-line;
}
}
}
......
......@@ -20,6 +20,8 @@
margin: 0;
padding: 0;
margin-top: 10px;
word-break: normal;
white-space: pre-wrap;
}
.commit-info-row {
......
......@@ -47,6 +47,7 @@ li.commit {
.commit_short_id {
min-width: 65px;
color: $gl-dark-link-color;
font-family: $monospace_font;
}
......@@ -88,6 +89,10 @@ li.commit {
padding: 0;
margin: 0;
}
a {
color: $gl-dark-link-color;
}
}
.commit-row-info {
......
......@@ -33,8 +33,12 @@
.description {
margin-top: 6px;
p:last-child {
p {
overflow-x: auto;
&:last-child {
margin-bottom: 0;
}
}
}
}
......@@ -2,6 +2,7 @@
.diff-file {
border: 1px solid $border-color;
margin-bottom: $gl-padding;
border-radius: 3px;
.diff-header {
position: relative;
......@@ -10,6 +11,7 @@
padding: 10px 16px;
color: #555;
z-index: 10;
border-radius: 3px 3px 0 0;
.diff-title {
font-family: $monospace_font;
......@@ -31,6 +33,7 @@
overflow-y: hidden;
background: #fff;
color: #333;
border-radius: 0 0 3px 3px;
.unfold {
cursor: pointer;
......@@ -59,9 +62,14 @@
border-collapse: separate;
margin: 0;
padding: 0;
.line_holder td {
line-height: $code_line_height;
font-size: $code_font_size;
span {
white-space: pre;
}
}
}
......@@ -104,6 +112,10 @@
display: table-cell;
}
}
.text-file.diff-wrap-lines table .line_holder td span {
white-space: pre-wrap;
}
}
.image {
background: #ddd;
......@@ -316,6 +328,16 @@
float: right;
}
.diffs {
.content-block {
border-bottom: none;
}
}
.files-changed {
border-bottom: none;
}
// Mobile
@media (max-width: 480px) {
.diff-title {
......
......@@ -59,6 +59,9 @@
position: relative;
overflow-y: auto;
padding: 15px;
.form-actions {
margin: -$gl-padding+1;
}
}
body.modal-open {
......
......@@ -49,6 +49,15 @@
}
.label-row {
.label-name {
display: inline-block;
width: 200px;
@media (max-width: $screen-xs-min) {
display: block;
}
}
.label {
padding: 9px;
font-size: 14px;
......@@ -69,3 +78,52 @@
background-color: $gl-danger;
color: $white-light;
}
.manage-labels-list {
.prepend-left-10 {
display: inline-block;
width: 40%;
vertical-align: middle;
@media (max-width: $screen-xs-min) {
display: block;
width: 100%;
margin-left: 0;
padding: 10px 0;
}
}
.pull-info-right {
float: right;
@media (max-width: $screen-xs-min) {
float: none;
}
.action-buttons {
border-color: transparent;
padding: 6px;
color: $gl-text-color;
&.subscribe-button {
padding-left: 0;
}
}
i {
color: $gl-text-color;
}
.append-right-20 {
a {
color: $gl-text-color;
}
@media (max-width: $screen-xs-min) {
display: block;
margin-bottom: 10px;
}
}
}
}
......@@ -123,6 +123,8 @@
.mr_source_commit,
.mr_target_commit {
margin-bottom: 0;
.commit {
margin: 0;
padding: 2px 0;
......@@ -174,10 +176,6 @@
display: none;
}
.merge-request-form .select2-container {
width: 250px !important;
}
#modal_merge_info .modal-dialog {
width: 600px;
......@@ -200,3 +198,76 @@
overflow-x: scroll;
}
}
.panel-new-merge-request {
.panel-heading {
padding: 5px 10px;
font-weight: 600;
line-height: 25px;
}
.panel-body {
padding: 10px 5px;
}
.panel-footer {
padding: 5px 10px;
}
.commit {
.commit-row-title {
margin-bottom: 4px;
}
.avatar {
width: 20px;
height: 20px;
margin-right: 5px;
}
.commit-row-info {
line-height: 20px;
}
}
.btn-clipboard {
margin-right: 5px;
padding: 0;
background: transparent;
}
.ci-status-link {
margin-right: 5px;
}
}
.merge-request-select {
padding-left: 5px;
padding-right: 5px;
margin-bottom: 10px;
&:last-child {
margin-bottom: 0;
}
@media (min-width: $screen-sm-min) {
float: left;
width: 50%;
margin-bottom: 0;
}
.dropdown-menu-toggle {
width: 100%;
}
.dropdown-menu {
left: 5px;
right: 5px;
width: auto;
}
}
.issuable-form-select-holder {
display: inline-block;
width: 250px;
}
/**
* Note Form
*/
.reply-btn {
@extend .btn-primary;
margin: 10px $gl-padding;
.comment-btn {
@extend .btn-create;
}
.diff-file .diff-content {
tr.line_holder:hover > td .line_note_link {
opacity: 1.0;
......@@ -113,13 +113,12 @@
.discussion-body,
.diff-file {
.notes .note {
border-color: #ddd;
padding: 10px 15px;
}
.discussion-reply-holder {
background: $background-color;
border-top: 1px solid $border-color;
background-color: $white-light;
padding: 10px 16px;
}
}
......
......@@ -58,6 +58,7 @@ ul.notes {
.note {
display: block;
position: relative;
border-bottom: 1px solid $table-border-gray;
&.is-editting {
.note-header,
......@@ -82,7 +83,7 @@ ul.notes {
// On diffs code should wrap nicely and not overflow
pre {
code {
white-space: pre-wrap;
white-space: pre;
}
}
......@@ -117,9 +118,6 @@ ul.notes {
padding-bottom: 3px;
}
&:last-child {
border-bottom: 1px solid $border-color;
}
}
}
......@@ -137,14 +135,14 @@ ul.notes {
font-family: $regular_font;
td {
border: 1px solid #ddd;
border: 1px solid $table-border-gray;
border-left: none;
&.notes_line {
vertical-align: middle;
text-align: center;
padding: 10px 0;
background: #fff;
background: $background-color;
color: $text-color;
}
&.notes_line2 {
......@@ -175,9 +173,6 @@ ul.notes {
}
}
.author_link {
font-weight: 600;
}
}
.note-headline-light,
......@@ -203,14 +198,26 @@ ul.notes {
line-height: 24px;
.fa {
color: $notes-action-color;
position: relative;
top: 1px;
font-size: 17px;
}
.fa-trash-o {
top: 0;
font-size: 16px;
&.js-note-delete {
i {
&:hover {
color: $gl-text-red;
}
}
}
&.js-note-edit {
i {
&:hover {
color: $gl-link-color;
}
}
}
}
......
......@@ -5,7 +5,7 @@ class Admin::ProjectsController < Admin::ApplicationController
def index
@projects = Project.all
@projects = @projects.in_namespace(params[:namespace_id]) if params[:namespace_id].present?
@projects = @projects.where("visibility_level IN (?)", params[:visibility_levels]) if params[:visibility_levels].present?
@projects = @projects.where("projects.visibility_level IN (?)", params[:visibility_levels]) if params[:visibility_levels].present?
@projects = @projects.with_push if params[:with_push].present?
@projects = @projects.abandoned if params[:abandoned].present?
@projects = @projects.non_archived unless params[:with_archived].present?
......
......@@ -47,6 +47,16 @@ class ApplicationController < ActionController::Base
email: current_user.email,
username: current_user.username,
)
Raven.tags_context(program: sentry_program_context)
end
end
def sentry_program_context
if Sidekiq.server?
'sidekiq'
else
'rails'
end
end
......
......@@ -18,14 +18,14 @@ class Groups::MilestonesController < Groups::ApplicationController
end
def create
project_ids = params[:milestone][:project_ids]
project_ids = params[:milestone][:project_ids].reject(&:blank?)
title = milestone_params[:title]
@projects.where(id: project_ids).each do |project|
Milestones::CreateService.new(project, current_user, milestone_params).execute
end
if create_milestones(project_ids)
redirect_to milestone_path(title)
else
render_new_with_error(project_ids.empty?)
end
end
def show
......@@ -41,6 +41,27 @@ class Groups::MilestonesController < Groups::ApplicationController
private
def create_milestones(project_ids)
return false unless project_ids.present?
ActiveRecord::Base.transaction do
@projects.where(id: project_ids).each do |project|
Milestones::CreateService.new(project, current_user, milestone_params).execute
end
end
true
rescue ActiveRecord::ActiveRecordError => e
flash.now[:alert] = "An error occurred while creating the milestone: #{e.message}"
false
end
def render_new_with_error(empty_project_ids)
@milestone = Milestone.new(milestone_params)
@milestone.errors.add(:project_id, "Please select at least one project.") if empty_project_ids
render :new
end
def authorize_admin_milestones!
return render_404 unless can?(current_user, :admin_milestones, group)
end
......
......@@ -55,7 +55,7 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController
end
else
saml_user = Gitlab::Saml::User.new(oauth)
saml_user.save
saml_user.save if saml_user.changed?
@user = saml_user.gl_user
continue_login_process
......
......@@ -68,7 +68,9 @@ class Projects::ApplicationController < ApplicationController
end
def require_non_empty_project
redirect_to namespace_project_path(@project.namespace, @project) if @project.empty_repo?
# Be sure to return status code 303 to avoid a double DELETE:
# http://api.rubyonrails.org/classes/ActionController/Redirecting.html
redirect_to namespace_project_path(@project.namespace, @project), status: 303 if @project.empty_repo?
end
def require_branch_head
......
class Projects::BadgesController < Projects::ApplicationController
before_action :no_cache_headers
layout 'project_settings'
before_action :authorize_admin_project!, only: [:index]
before_action :no_cache_headers, except: [:index]
def index
@ref = params[:ref] || @project.default_branch || 'master'
@build_badge = Gitlab::Badge::Build.new(@project, @ref)
end
def build
badge = Gitlab::Badge::Build.new(project, params[:ref])
......
......@@ -48,7 +48,7 @@ class Projects::BranchesController < Projects::ApplicationController
respond_to do |format|
format.html do
redirect_to namespace_project_branches_path(@project.namespace,
@project)
@project), status: 303
end
format.js { render status: status[:return_code] }
end
......
......@@ -207,20 +207,20 @@ class Projects::MergeRequestsController < Projects::ApplicationController
#This is always source
@source_project = @merge_request.nil? ? @project : @merge_request.source_project
@commit = @repository.commit(params[:ref]) if params[:ref].present?
render layout: false
end
def branch_to
@target_project = selected_target_project
@commit = @target_project.commit(params[:ref]) if params[:ref].present?
render layout: false
end
def update_branches
@target_project = selected_target_project
@target_branches = @target_project.repository.branch_names
respond_to do |format|
format.js
end
render layout: false
end
def ci_status
......
......@@ -24,6 +24,8 @@ class Projects::RefsController < Projects::ApplicationController
namespace_project_find_file_path(@project.namespace, @project, @id)
when "graphs_commits"
commits_namespace_project_graph_path(@project.namespace, @project, @id)
when "badges"
namespace_project_badges_path(@project.namespace, @project, ref: @id)
else
namespace_project_commits_path(@project.namespace, @project, @id)
end
......
......@@ -88,6 +88,20 @@ class Projects::WikisController < Projects::ApplicationController
)
end
def markdown_preview
text = params[:text]
ext = Gitlab::ReferenceExtractor.new(@project, current_user, current_user)
ext.analyze(text)
render json: {
body: view_context.markdown(text, pipeline: :wiki, project_wiki: @project_wiki),
references: {
users: ext.users.map(&:username)
}
}
end
def git_access
end
......
......@@ -5,7 +5,8 @@ class SessionsController < Devise::SessionsController
skip_before_action :check_2fa_requirement, only: [:destroy]
prepend_before_action :check_initial_setup, only: [:new]
prepend_before_action :authenticate_with_two_factor, only: [:create]
prepend_before_action :authenticate_with_two_factor,
if: :two_factor_enabled?, only: [:create]
prepend_before_action :store_redirect_path, only: [:new]
before_action :auto_sign_in_with_provider, only: [:new]
......@@ -56,10 +57,10 @@ class SessionsController < Devise::SessionsController
end
def find_user
if user_params[:login]
User.by_login(user_params[:login])
elsif user_params[:otp_attempt] && session[:otp_user_id]
if session[:otp_user_id]
User.find(session[:otp_user_id])
elsif user_params[:login]
User.by_login(user_params[:login])
end
end
......@@ -83,11 +84,13 @@ class SessionsController < Devise::SessionsController
end
end
def two_factor_enabled?
find_user.try(:two_factor_enabled?)
end
def authenticate_with_two_factor
user = self.resource = find_user
return unless user && user.two_factor_enabled?
if user_params[:otp_attempt].present? && session[:otp_user_id]
if valid_otp_attempt?(user)
# Remove any lingering user data from login
......
......@@ -27,9 +27,9 @@ module BlobHelper
link_opts)
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 disabled has-tooltip btn-file-option", title: "You can only edit files when you are on a branch", data: { container: 'body' }
elsif can_edit_blob?(blob, project, ref)
link_to "Edit", edit_path, class: 'btn'
link_to "Edit", edit_path, class: 'btn btn-file-option'
elsif can?(current_user, :fork_project, project)
continue_params = {
to: edit_path,
......@@ -38,7 +38,7 @@ module BlobHelper
}
fork_path = namespace_project_forks_path(project.namespace, project, namespace_key: current_user.namespace.id, continue: continue_params)
link_to "Edit", fork_path, class: 'btn', method: :post
link_to "Edit", fork_path, class: 'btn btn-file-option', method: :post
end
end
......
......@@ -28,7 +28,7 @@ module CommitsHelper
def commit_to_html(commit, project, inline = true)
template = inline ? "inline_commit" : "commit"
escape_javascript(render "projects/commits/#{template}", commit: commit, project: project) unless commit.nil?
render "projects/commits/#{template}", commit: commit, project: project unless commit.nil?
end
# Breadcrumb links for a Project and, if applicable, a tree path
......@@ -117,7 +117,7 @@ module CommitsHelper
end
end
link_to(
"Browse Files »",
"Browse Files",
namespace_project_tree_path(project.namespace, project, commit),
class: "pull-right"
)
......@@ -197,7 +197,7 @@ module CommitsHelper
link_to(
namespace_project_blob_path(project.namespace, project,
tree_join(commit_sha, diff.new_path)),
class: 'btn view-file js-view-file'
class: 'btn view-file js-view-file btn-file-option'
) do
raw('View file @') + content_tag(:span, commit_sha[0..6],
class: 'commit-short-id')
......
module FormHelper
def form_errors(model)
return unless model.errors.any?
pluralized = 'error'.pluralize(model.errors.count)
headline = "The form contains the following #{pluralized}:"
content_tag(:div, class: 'alert alert-danger', id: 'error_explanation') do
content_tag(:h4, headline) <<
content_tag(:ul) do
model.errors.full_messages.
map { |msg| content_tag(:li, msg) }.
join.
html_safe
end
end
end
end
......@@ -116,29 +116,6 @@ module GitlabMarkdownHelper
end
end
MARKDOWN_TIPS = [
"End a line with two or more spaces for a line-break, or soft-return",
"Inline code can be denoted by `surrounding it with backticks`",
"Blocks of code can be denoted by three backticks ``` or four leading spaces",
"Emoji can be added by :emoji_name:, for example :thumbsup:",
"Notify other participants using @user_name",
"Notify a specific group using @group_name",
"Notify the entire team using @all",
"Reference an issue using a hash, for example issue #123",
"Reference a merge request using an exclamation point, for example MR !123",
"Italicize words or phrases using *asterisks* or _underscores_",
"Bold words or phrases using **double asterisks** or __double underscores__",
"Strikethrough words or phrases using ~~two tildes~~",
"Make a bulleted list using + pluses, - minuses, or * asterisks",
"Denote blockquotes using > at the beginning of a line",
"Make a horizontal line using three or more hyphens ---, asterisks ***, or underscores ___"
].freeze
# Returns a random markdown tip for use as a textarea placeholder
def random_markdown_tip
MARKDOWN_TIPS.sample
end
private
# Return +text+, truncated to +max_chars+ characters, excluding any HTML
......
......@@ -115,17 +115,32 @@ module IssuesHelper
icon('eye-slash') if issue.confidential?
end
def emoji_icon(name, unicode = nil, aliases = [])
def emoji_icon(name, unicode = nil, aliases = [], sprite: true)
unicode ||= Emoji.emoji_filename(name) rescue ""
content_tag :div, "",
class: "icon emoji-icon emoji-#{unicode}",
title: name,
data: {
aliases: aliases.join(' '),
data = {
aliases: aliases.join(" "),
emoji: name,
unicode_name: unicode
}
if sprite
# Emoji icons for the emoji menu, these use a spritesheet.
content_tag :div, "",
class: "icon emoji-icon emoji-#{unicode}",
title: name,
data: data
else
# Emoji icons displayed separately, used for the awards already given
# to an issue or merge request.
content_tag :img, "",
class: "icon emoji",
title: name,
height: "20px",
width: "20px",
src: url_to_image("#{unicode}.png"),
data: data
end
end
def emoji_author_list(notes, current_user)
......
......@@ -69,10 +69,7 @@ module NotesHelper
line_type: line_type
}
button_tag class: 'btn btn-nr reply-btn js-discussion-reply-button',
data: data, title: 'Add a reply' do
link_text = icon('comment')
link_text << ' Reply'
end
button_tag 'Reply...', class: 'btn btn-text-field js-discussion-reply-button',
data: data, title: 'Add a reply'
end
end
......@@ -331,6 +331,8 @@ class Repository
# Runs code after a repository has been created.
def after_create
expire_exists_cache
expire_root_ref_cache
expire_emptiness_caches
end
# Runs code just before a repository is deleted.
......
......@@ -55,15 +55,15 @@ class GitPushService < BaseService
end
def update_main_language
# Performance can be bad so for now only check main_language once
# See https://gitlab.com/gitlab-org/gitlab-ce/issues/14937
return if @project.main_language.present?
return unless is_default_branch?
return unless push_to_new_branch? || push_to_existing_branch?
current_language = @project.repository.main_language
unless current_language == @project.main_language
return @project.update_attributes(main_language: current_language)
end
@project.update_attributes(main_language: current_language)
true
end
......
......@@ -3,7 +3,7 @@ module Milestones
def execute
milestone = project.milestones.new(params)
if milestone.save
if milestone.save!
event_service.open_milestone(milestone, current_user)
end
......
......@@ -3,11 +3,9 @@
%p Please use this form to report users who create spam issues, comments or behave inappropriately.
%hr
= form_for @abuse_report, html: { class: 'form-horizontal js-quick-submit js-requires-input'} do |f|
= form_errors(@abuse_report)
= f.hidden_field :user_id
- if @abuse_report.errors.any?
.alert.alert-danger
- @abuse_report.errors.full_messages.each do |msg|
%p= msg
.form-group
= f.label :user_id, class: 'control-label'
.col-sm-10
......
= form_for @appearance, url: admin_appearances_path, html: { class: 'form-horizontal'} do |f|
- if @appearance.errors.any?
.alert.alert-danger
- @appearance.errors.full_messages.each do |msg|
%p= msg
= form_errors(@appearance)
%fieldset.sign-in
%legend
......
= form_for @application_setting, url: admin_application_settings_path, html: { class: 'form-horizontal fieldset-form' } do |f|
- if @application_setting.errors.any?
#error_explanation
.alert.alert-danger
- @application_setting.errors.full_messages.each do |msg|
%p= msg
= form_errors(@application_setting)
%fieldset
%legend Visibility and Access Controls
......
= form_for [:admin, @application], url: @url, html: {class: 'form-horizontal', role: 'form'} do |f|
- if application.errors.any?
.alert.alert-danger
%button{ type: "button", class: "close", "data-dismiss" => "alert"} &times;
- application.errors.full_messages.each do |msg|
%p= msg
= form_errors(application)
= content_tag :div, class: 'form-group' do
= f.label :name, class: 'col-sm-2 control-label'
.col-sm-10
......
......@@ -4,10 +4,8 @@
= render_broadcast_message(@broadcast_message.message.presence || "Your message here")
= form_for [:admin, @broadcast_message], html: { class: 'broadcast-message-form form-horizontal js-quick-submit js-requires-input'} do |f|
-if @broadcast_message.errors.any?
.alert.alert-danger
- @broadcast_message.errors.full_messages.each do |msg|
%p= msg
= form_errors(@broadcast_message)
.form-group
= f.label :message, class: 'control-label'
.col-sm-10
......
......@@ -4,11 +4,7 @@
%div
= form_for [:admin, @deploy_key], html: { class: 'deploy-key-form form-horizontal' } do |f|
-if @deploy_key.errors.any?
.alert.alert-danger
%ul
- @deploy_key.errors.full_messages.each do |msg|
%li= msg
= form_errors(@deploy_key)
.form-group
= f.label :title, class: "control-label"
......
= form_for [:admin, @group], html: { class: "form-horizontal" } do |f|
- if @group.errors.any?
.alert.alert-danger
%span= @group.errors.full_messages.first
= form_errors(@group)
= render 'shared/group_form', f: f
.form-group.group-description-holder
......
......@@ -10,10 +10,8 @@
= form_for @hook, as: :hook, url: admin_hooks_path, html: { class: 'form-horizontal' } do |f|
-if @hook.errors.any?
.alert.alert-danger
- @hook.errors.full_messages.each do |msg|
%p= msg
= form_errors(@hook)
.form-group
= f.label :url, "URL:", class: 'control-label'
.col-sm-10
......
= form_for [:admin, @user, @identity], html: { class: 'form-horizontal fieldset-form' } do |f|
- if @identity.errors.any?
#error_explanation
.alert.alert-danger
- @identity.errors.full_messages.each do |msg|
%p= msg
= form_errors(@identity)
.form-group
= f.label :provider, class: 'control-label'
......
= form_for [:admin, @label], html: { class: 'form-horizontal label-form js-requires-input' } do |f|
-if @label.errors.any?
.row
.col-sm-offset-2.col-sm-10
.alert.alert-danger
- @label.errors.full_messages.each do |msg|
%span= msg
%br
= form_errors(@label)
.form-group
= f.label :title, class: 'control-label'
......
.user_new
= form_for [:admin, @user], html: { class: 'form-horizontal fieldset-form' } do |f|
-if @user.errors.any?
#error_explanation
.alert.alert-danger
- @user.errors.full_messages.each do |msg|
%p= msg
= form_errors(@user)
%fieldset
%legend Account
......
= form_for application, url: doorkeeper_submit_path(application), html: {role: 'form'} do |f|
- if application.errors.any?
.alert.alert-danger
%ul
- application.errors.full_messages.each do |msg|
%li= msg
= form_errors(application)
.form-group
= f.label :name, class: 'label-light'
......
......@@ -5,9 +5,7 @@
Group settings
.panel-body
= form_for @group, html: { multipart: true, class: "form-horizontal" }, authenticity_token: true do |f|
- if @group.errors.any?
.alert.alert-danger
%span= @group.errors.full_messages.first
= form_errors(@group)
= render 'shared/group_form', f: f
.form-group
......
......@@ -10,6 +10,14 @@
= form_for @milestone, url: group_milestones_path(@group), html: { class: 'form-horizontal milestone-form gfm-form js-quick-submit js-requires-input' } do |f|
.row
- if @milestone.errors.any?
#error_explanation
.alert.alert-danger
%ul
- @milestone.errors.full_messages.each do |msg|
%li
= msg
.col-md-6
.form-group
= f.label :title, "Title", class: "control-label"
......
......@@ -6,10 +6,7 @@
%hr
= form_for @group, html: { class: 'group-form form-horizontal' } do |f|
- if @group.errors.any?
.alert.alert-danger
%span= @group.errors.full_messages.first
= form_errors(@group)
= render 'shared/group_form', f: f, autofocus: true
.form-group.group-description-holder
......
- if nav_menu_collapsed?
= link_to icon('angle-right'), '#', class: 'toggle-nav-collapse', title: "Open/Close"
- else
= link_to icon('angle-left'), '#', class: 'toggle-nav-collapse', title: "Open/Close"
.page-with-sidebar{ class: "#{page_sidebar_class} #{page_gutter_class}" }
= render "layouts/broadcast"
.expand-nav
= link_to icon('bars'), '#', class: 'toggle-nav-collapse', title: "Open sidebar"
.sidebar-wrapper.nicescroll{ class: nav_sidebar_class }
.header-logo
%a#logo
......@@ -10,19 +8,15 @@
.gitlab-text-container
%h3 GitLab
- primary_sidebar = current_user ? 'dashboard' : 'explore'
- if defined?(sidebar) && sidebar && sidebar != primary_sidebar
.complex-sidebar
.nav-primary
= render "layouts/nav/#{primary_sidebar}"
.nav-secondary
- if defined?(sidebar) && sidebar
= render "layouts/nav/#{sidebar}"
- elsif current_user
= render 'layouts/nav/dashboard'
- else
= render "layouts/nav/#{primary_sidebar}"
= render 'layouts/nav/explore'
.collapse-nav
= link_to icon('angle-left'), '#', class: 'toggle-nav-collapse', title: "Hide sidebar"
= render partial: 'layouts/collapse_button'
- if current_user
= link_to current_user, class: 'sidebar-user', title: "Profile" do
= image_tag avatar_icon(current_user, 60), alt: 'Profile', class: 'avatar avatar s36'
......
- if controller.controller_path =~ /^groups/
- if controller.controller_path =~ /^groups/ && @group.persisted?
- label = 'This group'
- if controller.controller_path =~ /^projects/
- if controller.controller_path =~ /^projects/ && @project.persisted?
- label = 'This project'
.search.search-form{class: "#{'has-location-badge' if label.present?}"}
......
......@@ -95,7 +95,7 @@
Spam Logs
%span.count= number_with_delimiter(SpamLog.count(:all))
= nav_link(controller: :application_settings) do
= nav_link(controller: :application_settings, html_options: { class: 'separate-item'}) do
= link_to admin_application_settings_path, title: 'Settings' do
= icon('cogs fw')
%span
......
......@@ -9,18 +9,18 @@
= icon('bell fw')
%span
Todos
%span.count= number_with_delimiter(todos_pending_count)
%span.count.todos-pending-count= number_with_delimiter(todos_pending_count)
= nav_link(path: 'dashboard#activity') do
= link_to activity_dashboard_path, class: 'shortcuts-activity', title: 'Activity' do
= icon('dashboard fw')
%span
Activity
= nav_link(path: ['dashboard/groups#index', 'explore/groups#index']) do
= nav_link(controller: :groups) do
= link_to dashboard_groups_path, title: 'Groups' do
= icon('group fw')
%span
Groups
= nav_link(path: 'dashboard#milestones') do
= nav_link(controller: :milestones) do
= link_to dashboard_milestones_path, title: 'Milestones' do
= icon('clock-o fw')
%span
......@@ -48,6 +48,7 @@
%span
Help
%li.separate-item
= nav_link(controller: :profile) do
= link_to profile_path, title: 'Profile Settings', data: {placement: 'bottom'} do
= icon('user fw')
......
%ul.nav.nav-sidebar
= nav_link do
= link_to root_path, title: 'Go to dashboard', class: 'back-link' do
= icon('caret-square-o-left fw')
%span
Go to dashboard
%li.separate-item
= nav_link(path: 'groups#show', html_options: {class: 'home'}) do
= link_to group_path(@group), title: 'Home' do
= icon('group fw')
......@@ -34,7 +42,7 @@
%span
Members
- if can?(current_user, :admin_group, @group)
= nav_link do
= nav_link(html_options: { class: "separate-item" }) do
= link_to edit_group_path(@group), title: 'Settings' do
= icon ('cogs fw')
%span
......
%ul.nav.nav-sidebar
= nav_link do
= link_to root_path, title: 'Go to dashboard', class: 'back-link' do
= icon('caret-square-o-left fw')
%span
Go to dashboard
%li.separate-item
= nav_link(path: 'profiles#show', html_options: {class: 'home'}) do
= link_to profile_path, title: 'Profile Settings' do
= icon('user fw')
......
%ul.nav.nav-sidebar
- if @project.group
= nav_link do
= link_to group_path(@project.group), title: 'Go to group', class: 'back-link' do
= icon('caret-square-o-left fw')
%span
Go to group
- else
= nav_link do
= link_to root_path, title: 'Go to dashboard', class: 'back-link' do
= icon('caret-square-o-left fw')
%span
Go to dashboard
%li.separate-item
= nav_link(path: 'projects#show', html_options: {class: 'home'}) do
= link_to project_path(@project), title: 'Project', class: 'shortcuts-project' do
= icon('bookmark fw')
......@@ -98,7 +113,7 @@
Snippets
- if project_nav_tab? :settings
= nav_link(html_options: {class: "#{project_tab_class}"}) do
= nav_link(html_options: {class: "#{project_tab_class} separate-item"}) do
= link_to edit_project_path(@project), title: 'Settings' do
= icon('cogs fw')
%span
......
......@@ -51,8 +51,13 @@
= icon('code fw')
%span
Variables
= nav_link path: 'triggers#index' do
= nav_link(controller: :triggers) do
= link_to namespace_project_triggers_path(@project.namespace, @project), title: 'Triggers' do
= icon('retweet fw')
%span
Triggers
= nav_link(controller: :badges) do
= link_to namespace_project_badges_path(@project.namespace, @project), title: 'Badges' do
= icon('star-half-empty fw')
%span
Badges
......@@ -5,10 +5,14 @@
- content_for :scripts_body_top do
- project = @target_project || @project
- if @project_wiki
- markdown_preview_path = namespace_project_wikis_markdown_preview_path(project.namespace, project)
- else
- markdown_preview_path = markdown_preview_namespace_project_path(project.namespace, project)
- if current_user
:javascript
window.project_uploads_path = "#{namespace_project_uploads_path project.namespace,project}";
window.markdown_preview_path = "#{markdown_preview_namespace_project_path(project.namespace, project)}";
window.markdown_preview_path = "#{markdown_preview_path}";
- content_for :scripts_body do
= render "layouts/init_auto_complete" if current_user
......
%div
= form_for [:profile, @key], html: { class: 'js-requires-input' } do |f|
- if @key.errors.any?
.alert.alert-danger
%ul
- @key.errors.full_messages.each do |msg|
%li= msg
= form_errors(@key)
.form-group
= f.label :key, class: 'label-light'
......
......@@ -2,11 +2,7 @@
- header_title page_title, profile_notifications_path
= form_for @user, url: profile_notifications_path, method: :put, html: { class: 'update-notifications prepend-top-default' } do |f|
-if @user.errors.any?
%div.alert.alert-danger
%ul
- @user.errors.full_messages.each do |msg|
%li= msg
= form_errors(@user)
= hidden_field_tag :notification_type, 'global'
.row
......
......@@ -13,11 +13,8 @@
- unless @user.password_automatically_set?
or recover your current one
= form_for @user, url: profile_password_path, method: :put, html: {class: "update-password"} do |f|
-if @user.errors.any?
.alert.alert-danger
%ul
- @user.errors.full_messages.each do |msg|
%li= msg
= form_errors(@user)
- unless @user.password_automatically_set?
.form-group
= f.label :current_password, class: 'label-light'
......
......@@ -7,11 +7,8 @@
Please set a new password before proceeding.
%br
After a successful password update you will be redirected to login screen.
-if @user.errors.any?
.alert.alert-danger
%ul
- @user.errors.full_messages.each do |msg|
%li= msg
= form_errors(@user)
- unless @user.password_automatically_set?
.form-group
......
= form_for @user, url: profile_path, method: :put, html: { multipart: true, class: "edit-user prepend-top-default" }, authenticity_token: true do |f|
-if @user.errors.any?
%div.alert.alert-danger
%ul
- @user.errors.full_messages.each do |msg|
%li= msg
= form_errors(@user)
.row
.col-lg-3.profile-settings-sidebar
%h4.prepend-top-0
......
- if @project.errors.any?
.alert.alert-danger
%button{ type: "button", class: "close", "data-dismiss" => "alert"} &times;
= @project.errors.full_messages.first
= form_errors(@project)
......@@ -2,13 +2,13 @@
.md-header
%ul.nav-links
%li.active
%a.js-md-write-button{ href: "#md-write-holder" }
%a.js-md-write-button{ href: "#md-write-holder", tabindex: -1 }
Write
%li
%a.js-md-preview-button{ href: "#md-preview-holder" }
%a.js-md-preview-button{ href: "#md-preview-holder", tabindex: -1 }
Preview
%li.pull-right
%button.zen-cotrol.zen-control-full.js-zen-enter{ type: 'button' }
%button.zen-cotrol.zen-control-full.js-zen-enter{ type: 'button', tabindex: -1 }
Go full screen
.md-write-holder
......
- page_title 'Badges'
- badges_path = namespace_project_badges_path(@project.namespace, @project)
- header_title project_title(@project, 'Badges', badges_path)
.prepend-top-10
.panel.panel-default
.panel-heading
%b Builds badge &middot;
= @build_badge.to_html
.pull-right
= render 'shared/ref_switcher', destination: 'badges'
.panel-body
.row
.col-md-2.text-center
Markdown
.col-md-10.code.js-syntax-highlight
= highlight('.md', @build_badge.to_markdown)
.row
%hr
.row
.col-md-2.text-center
HTML
.col-md-10.code.js-syntax-highlight
= highlight('.html', @build_badge.to_html)
......@@ -19,24 +19,17 @@
.pull-right
- if ci_commit
= render_ci_status(ci_commit)
&nbsp;
= clipboard_button(clipboard_text: commit.id)
= link_to commit.short_id, namespace_project_commit_path(project.namespace, project, commit), class: "commit_short_id"
.notes_count
- if note_count > 0
%span.light
%i.fa.fa-comments
= note_count
- if commit.description?
.commit-row-description.js-toggle-content
%pre
= preserve(markdown(escape_once(commit.description), pipeline: :single_line))
.commit-row-info
by
= commit_author_link(commit, avatar: true, size: 24)
authored
.committed_ago
#{time_ago_with_tooltip(commit.committed_date, skip_js: true)} &nbsp;
= link_to_browse_code(project, commit)
%div
= form_for [@project.namespace.becomes(Namespace), @project, @key], url: namespace_project_deploy_keys_path, html: { class: 'deploy-key-form form-horizontal js-requires-input' } do |f|
-if @key.errors.any?
.alert.alert-danger
%ul
- @key.errors.full_messages.each do |msg|
%li= msg
= form_errors(@key)
.form-group
= f.label :title, class: "control-label"
......
......@@ -3,7 +3,7 @@
- diff_files = safe_diff_files(diffs, diff_refs)
.content-block.oneline-block
.content-block.oneline-block.files-changed
.inline-parallel-buttons
.btn-group
= inline_diff_btn
......
......@@ -3,7 +3,7 @@
- if diff_file.diff.submodule?
%span
= icon('archive fw')
%strong
%span
= submodule_link(blob, @commit.id, project.repository)
- else
= blob_icon blob.mode, blob.name
......@@ -11,13 +11,13 @@
= link_to "#diff-#{i}" do
- if diff_file.renamed_file
- old_path, new_path = mark_inline_diffs(diff_file.old_path, diff_file.new_path)
%strong.filename.old
.filename.old
= old_path
&rarr;
%strong.filename.new
.filename.new
= new_path
- else
%strong
%span
= diff_file.new_path
- if diff_file.deleted_file
deleted
......@@ -28,8 +28,8 @@
.file-actions.hidden-xs
- if blob_text_viewable?(blob)
= link_to '#', class: 'js-toggle-diff-comments btn active has-tooltip', title: "Toggle comments for this file" do
= icon('comments')
= link_to '#', class: 'js-toggle-diff-comments btn active has-tooltip btn-file-option', title: "Toggle comments for this file" do
= icon('comment')
\
- if editable_diff?(diff_file)
......
......@@ -9,10 +9,8 @@
%hr.clearfix
= form_for [@project.namespace.becomes(Namespace), @project, @hook], as: :hook, url: namespace_project_hooks_path(@project.namespace, @project), html: { class: 'form-horizontal' } do |f|
-if @hook.errors.any?
.alert.alert-danger
- @hook.errors.full_messages.each do |msg|
%p= msg
= form_errors(@hook)
.form-group
= f.label :url, "URL", class: 'control-label'
.col-sm-10
......
= form_for [@project.namespace.becomes(Namespace), @project, @label], html: { class: 'form-horizontal label-form js-quick-submit js-requires-input' } do |f|
-if @label.errors.any?
.row
.col-sm-offset-2.col-sm-10
.alert.alert-danger
- @label.errors.full_messages.each do |msg|
%span= msg
%br
= form_errors(@label)
.form-group
= f.label :title, class: 'control-label'
......
%li{id: dom_id(label)}
= render "shared/label_row", label: label
.pull-right
%strong.append-right-20
.pull-info-right
%span.append-right-20
= link_to_label(label, type: :merge_request) do
= pluralize label.open_merge_requests_count, 'open merge request'
= pluralize label.open_merge_requests_count, 'merge request'
%strong.append-right-20
%span.append-right-20
= link_to_label(label) do
= pluralize label.open_issues_count(current_user), 'open issue'
- if current_user
.label-subscription{data: {url: toggle_subscription_namespace_project_label_path(@project.namespace, @project, label)}}
.subscription-status{data: {status: label_subscription_status(label)}}
%button.btn.btn-sm.btn-info.subscribe-button
%a.subscribe-button.btn.action-buttons{data: {toggle: "tooltip"}}
%span= label_subscription_toggle_button_text(label)
- if can? current_user, :admin_label, @project
= link_to 'Edit', edit_namespace_project_label_path(@project.namespace, @project, label), class: 'btn btn-sm'
= link_to 'Delete', namespace_project_label_path(@project.namespace, @project, label), class: 'btn btn-sm btn-remove remove-row', method: :delete, remote: true, data: {confirm: "Remove this label? Are you sure?"}
= link_to edit_namespace_project_label_path(@project.namespace, @project, label), title: "Edit", class: 'btn action-buttons', data: {toggle: "tooltip"} do
%i.fa.fa-pencil-square-o
= link_to namespace_project_label_path(@project.namespace, @project, label), title: "Delete", class: 'btn action-buttons remove-row', method: :delete, remote: true, data: {confirm: "Remove this label? Are you sure?", toggle: "tooltip"} do
%i.fa.fa-trash-o
- if current_user
:javascript
......
......@@ -5,33 +5,74 @@
.hide.alert.alert-danger.mr-compare-errors
.merge-request-branches.row
.col-md-6
.panel.panel-default
.panel.panel-default.panel-new-merge-request
.panel-heading
%strong Source branch
.panel-body
= f.select(:source_project_id, [[@merge_request.source_project_path,@merge_request.source_project.id]] , {}, { class: 'source_project select2 span3', disabled: @merge_request.persisted?, required: true })
&nbsp;
= f.select(:source_branch, @merge_request.source_branches, { include_blank: true }, { class: 'source_branch select2 span2', required: true, data: { placeholder: "Select source branch" } })
Source branch
.panel-body.clearfix
.merge-request-select.dropdown
= f.hidden_field :source_project_id
= dropdown_toggle @merge_request.source_project_path, { toggle: "dropdown", field_name: "#{f.object_name}[source_project_id]", disabled: @merge_request.persisted? }, { toggle_class: "js-compare-dropdown js-source-project" }
.dropdown-menu.dropdown-menu-selectable.dropdown-source-project
= dropdown_title("Select source project")
= dropdown_filter("Search projects")
= dropdown_content do
- is_active = f.object.source_project_id == @merge_request.source_project.id
%ul
%li
%a{ href: "#", class: "#{("is-active" if is_active)}", data: { id: @merge_request.source_project.id } }
= @merge_request.source_project_path
.merge-request-select.dropdown
= f.hidden_field :source_branch
= dropdown_toggle "Select source branch", { toggle: "dropdown", field_name: "#{f.object_name}[source_branch]" }, { toggle_class: "js-compare-dropdown js-source-branch" }
.dropdown-menu.dropdown-menu-selectable.dropdown-source-branch
= dropdown_title("Select source branch")
= dropdown_filter("Search branches")
= dropdown_content do
%ul
- @merge_request.source_branches.each do |branch|
%li
%a{ href: "#", class: "#{("is-active" if f.object.source_branch == branch)}", data: { id: branch } }
= branch
.panel-footer
.mr_source_commit
= icon('spinner spin', class: 'js-source-loading')
%ul.list-unstyled.mr_source_commit
.col-md-6
.panel.panel-default
.panel.panel-default.panel-new-merge-request
.panel-heading
%strong Target branch
.panel-body
Target branch
.panel-body.clearfix
- projects = @project.forked_from_project.nil? ? [@project] : [@project, @project.forked_from_project]
= f.select(:target_project_id, options_from_collection_for_select(projects, 'id', 'path_with_namespace', f.object.target_project_id), {}, { class: 'target_project select2 span3', disabled: @merge_request.persisted?, required: true })
&nbsp;
= f.select(:target_branch, @merge_request.target_branches, { include_blank: true }, { class: 'target_branch select2 span2', required: true, data: { placeholder: "Select target branch" } })
.merge-request-select.dropdown
= f.hidden_field :target_project_id
= dropdown_toggle f.object.target_project.path_with_namespace, { toggle: "dropdown", field_name: "#{f.object_name}[target_project_id]", disabled: @merge_request.persisted? }, { toggle_class: "js-compare-dropdown js-target-project" }
.dropdown-menu.dropdown-menu-selectable.dropdown-target-project
= dropdown_title("Select target project")
= dropdown_filter("Search projects")
= dropdown_content do
%ul
- projects.each do |project|
%li
%a{ href: "#", class: "#{("is-active" if f.object.target_project_id == project.id)}", data: { id: project.id } }
= project.path_with_namespace
.merge-request-select.dropdown
= f.hidden_field :target_branch
= dropdown_toggle f.object.target_branch, { toggle: "dropdown", field_name: "#{f.object_name}[target_branch]" }, { toggle_class: "js-compare-dropdown js-target-branch" }
.dropdown-menu.dropdown-menu-selectable.dropdown-target-branch.js-target-branch-dropdown
= dropdown_title("Select target branch")
= dropdown_filter("Search branches")
= dropdown_content do
%ul
- @merge_request.target_branches.each do |branch|
%li
%a{ href: "#", class: "#{("is-active" if f.object.target_branch == branch)}", data: { id: branch } }
= branch
.panel-footer
.mr_target_commit
= icon('spinner spin', class: "js-target-loading")
%ul.list-unstyled.mr_target_commit
- if @merge_request.errors.any?
.alert.alert-danger
- @merge_request.errors.full_messages.each do |msg|
%div= msg
= form_errors(@merge_request)
- elsif @merge_request.source_branch.present? && @merge_request.target_branch.present?
.light-well.append-bottom-default
.center
......@@ -45,40 +86,11 @@
and
%span.label-branch #{@merge_request.target_branch}
are the same.
.form-actions
= f.submit 'Compare branches and continue', class: "btn btn-new mr-compare-btn"
:javascript
var source_branch = $("#merge_request_source_branch")
, target_branch = $("#merge_request_target_branch")
, target_project = $("#merge_request_target_project_id");
$.get("#{branch_from_namespace_project_merge_requests_path(@source_project.namespace, @source_project)}", {ref: source_branch.val() });
$.get("#{branch_to_namespace_project_merge_requests_path(@source_project.namespace, @source_project)}", {target_project_id: target_project.val(),ref: target_branch.val() });
target_project.on("change", function() {
$.get("#{update_branches_namespace_project_merge_requests_path(@source_project.namespace, @source_project)}", {target_project_id: $(this).val() });
});
source_branch.on("change", function() {
$.get("#{branch_from_namespace_project_merge_requests_path(@source_project.namespace, @source_project)}", {ref: $(this).val() });
$(".mr-compare-errors").fadeOut();
$(".mr-compare-btn").enable();
});
target_branch.on("change", function() {
$.get("#{branch_to_namespace_project_merge_requests_path(@source_project.namespace, @source_project)}", {target_project_id: target_project.val(),ref: $(this).val() });
$(".mr-compare-errors").fadeOut();
$(".mr-compare-btn").enable();
});
:javascript
$(".merge-request-form").on('submit', function () {
if ($("#merge_request_source_branch").val() === "" || $('#merge_request_target_branch').val() === "") {
$(".mr-compare-errors").html("You must select source and target branch to proceed");
$(".mr-compare-errors").fadeIn();
event.preventDefault();
return;
}
new Compare({
targetProjectUrl: "#{update_branches_namespace_project_merge_requests_path(@source_project.namespace, @source_project)}",
sourceBranchUrl: "#{branch_from_namespace_project_merge_requests_path(@source_project.namespace, @source_project)}",
targetBranchUrl: "#{branch_to_namespace_project_merge_requests_path(@source_project.namespace, @source_project)}"
});
= commit_to_html(@commit, @source_project, false)
:plain
$(".mr_source_commit").html("#{commit_to_html(@commit, @source_project, false)}");
$('.js-timeago').timeago()
= commit_to_html(@commit, @target_project, false)
:plain
$(".mr_target_commit").html("#{commit_to_html(@commit, @target_project, false)}");
$('.js-timeago').timeago()
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.
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